libhoney/
lib.rs

1/*! Rust library for sending data to Honeycomb.
2
3I'd be forever greatful if you can try it out and provide feedback. There are a few
4reasons why I think this may not yet be ready for production use:
5
6- Honeycomb uses the singleton pattern for the libraries but I decided not to use it here (mostly due to: harder to get right, it feels to me like a rust anti-pattern). If you think I should have, please let me know.
7
8- I'm not convinced of the threading code. Although "it works" it probably isn't great - any feedback would be greatly appreciated.
9
10For these reasons, you're probably better waiting for a 1.0.0 release (I'll follow
11[semantic versioning][semantic versioning]). Having said that, if you still want to use
12this, thank you for being brave, and make sure to open bugs against it!
13
14# libhoney
15
16Rust library for sending events to Honeycomb, a service for debugging your software in
17production.
18
19- [Usage and Examples](#usage-and-examples)
20- [API Reference][API reference]
21
22# Usage and Examples
23
24## Initialization
25
26Initialize the library by passing in your Team API key and the default dataset name to
27which it should send events. When you call the library’s initialization routine, it spins
28up background threads to handle sending all the events. Calling .close() on the client
29will terminate all background threads.
30
31```rust
32let client = libhoney::init(libhoney::Config{
33  options: libhoney::client::Options {
34    api_key: "YOUR_API_KEY".to_string(),
35    dataset: "honeycomb-rust-example".to_string(),
36    ..libhoney::client::Options::default()
37  },
38  transmission_options: libhoney::transmission::Options::default(),
39});
40
41client.close();
42```
43
44Further configuration options can be found in the [API reference][API reference].
45
46## Building and Sending Events
47
48Once initialized, the libhoney client is ready to send events. Events go through three
49phases:
50
51- Creation `event := builder.new_event()`
52- Adding fields `event.add_field("key", Value::String("val".to_string()))`, `event.add(data)`
53- Transmission `event.send(&mut client)`
54
55Upon calling .send(), the event is dispatched to be sent to Honeycomb. All libraries set
56defaults that will allow your application to function as smoothly as possible during error
57conditions.
58
59In its simplest form, you can add a single attribute to an event with the `.add_field(k,
60v)` method. If you add the same key multiple times, only the last value added will be
61kept.
62
63More complex structures (maps and structs—things that can be serialized into a JSON
64object) can be added to an event with the .add(data) method.
65
66Events can have metadata associated with them that is not sent to Honeycomb. This metadata
67is used to identify the event when processing the response. More detail about metadata is
68below in the Response section.
69
70## Handling responses
71
72Sending an event is an asynchronous action and will avoid blocking by default. .send()
73will enqueue the event to be sent as soon as possible (thus, the return value doesn’t
74indicate that the event was successfully sent). Use the Vec returned by .responses() to
75check whether events were successfully received by Honeycomb’s servers.
76
77Before sending an event, you have the option to attach metadata to that event. This
78metadata is not sent to Honeycomb; instead, it’s used to help you match up individual
79responses with sent events. When sending an event, libhoney will take the metadata from
80the event and attach it to the response object for you to consume. Add metadata by
81populating the .metadata attribute directly on an event.
82
83Responses have a number of fields describing the result of an attempted event send:
84
85- `metadata`: the metadata you attached to the event to which this response corresponds
86
87- `status_code`: the HTTP status code returned by Honeycomb when trying to send the event. 2xx indicates success.
88
89- `duration`: the time.Duration it took to send the event.
90
91- `body`: the body of the HTTP response from Honeycomb. On failures, this body contains some more information about the failure.
92
93- `error`: when the event doesn’t even get to create a HTTP attempt, the reason will be in this field. (e.g. when sampled or dropped because of a queue overflow).
94
95You don’t have to process responses if you’re not interested in them—simply ignoring them
96is perfectly safe. Unread responses will be dropped.
97
98## Examples
99
100Honeycomb can calculate all sorts of statistics, so send the data you care about and let
101us crunch the averages, percentiles, lower/upper bounds, cardinality—whatever you want—for
102you.
103
104### Simple: send an event
105```rust
106# use std::collections::HashMap;
107# use serde_json::{json, Value};
108# use libhoney::{init, Config};
109# let api_host = &mockito::server_url();
110# let _m = mockito::mock(
111#    "POST",
112#     mockito::Matcher::Regex(r"/1/batch/(.*)$".to_string()),
113# )
114# .with_status(200)
115# .with_header("content-type", "application/json")
116# .with_body("finished batch to honeycomb")
117# .create();
118
119# let options = libhoney::client::Options{api_host: api_host.to_string(), api_key: "some key".to_string(), ..libhoney::client::Options::default()};
120use libhoney::FieldHolder; // Add trait to allow for adding fields
121// Call init to get a client
122let mut client = init(libhoney::Config {
123  options: options,
124  transmission_options: libhoney::transmission::Options::default(),
125});
126
127let mut data: HashMap<String, Value> = HashMap::new();
128data.insert("duration_ms".to_string(), json!(153.12));
129data.insert("method".to_string(), Value::String("get".to_string()));
130data.insert("hostname".to_string(), Value::String("appserver15".to_string()));
131data.insert("payload_length".to_string(), json!(27));
132
133let mut ev = client.new_event();
134ev.add(data);
135 // In production code, please check return of `.send()`
136ev.send(&mut client).err();
137```
138
139[API reference]: https://docs.rs/libhoney-rust
140[semantic versioning]: https://semver.org
141
142 */
143#![deny(missing_docs)]
144
145mod builder;
146pub mod client;
147mod errors;
148mod event;
149mod eventdata;
150mod events;
151mod fields;
152pub mod mock;
153mod response;
154mod sender;
155pub mod transmission;
156
157pub use builder::{Builder, DynamicFieldFunc};
158pub use client::Client;
159pub use errors::{Error, ErrorKind, Result};
160pub use event::{Event, Metadata};
161pub use fields::FieldHolder;
162pub use sender::Sender;
163pub use serde_json::{json, Value};
164use transmission::Transmission;
165
166/// Config allows the user to customise the initialisation of the library (effectively the
167/// Client)
168#[derive(Debug, Clone)]
169#[must_use = "must be set up for client to be properly initialised"]
170pub struct Config {
171    /// options is a subset of the global libhoney config that focuses on the
172    /// configuration of the client itself. The other config options are specific to a
173    /// given transmission Sender and should be specified there if the defaults need to be
174    /// overridden.
175    pub options: client::Options,
176
177    /// Configuration for the underlying sender. It is safe (and recommended) to leave
178    /// these values at their defaults. You cannot change these values after calling
179    /// init()
180    pub transmission_options: transmission::Options,
181}
182
183/// init is called on app initialisation and passed a `Config`. A `Config` has two sets of
184/// options (`client::Options` and `transmission::Options`).
185#[inline]
186pub fn init(config: Config) -> Client<Transmission> {
187    let transmission =
188        Transmission::new(config.transmission_options).expect("failed to instantiate transmission");
189    Client::new(config.options, transmission)
190}
191
192/// Auxiliary test module
193pub mod test {
194    use crate::mock;
195    /// `init` is purely used for testing purposes
196    pub fn init(config: super::Config) -> super::Client<mock::TransmissionMock> {
197        let transmission = mock::TransmissionMock::new(config.transmission_options)
198            .expect("failed to instantiate transmission");
199        super::Client::new(config.options, transmission)
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206
207    #[test]
208    fn test_init() {
209        let client = init(Config {
210            options: client::Options::default(),
211            transmission_options: transmission::Options::default(),
212        });
213        assert_eq!(client.options.dataset, "librust-dataset");
214        client.close().unwrap();
215    }
216}