tracing_honeycomb/
lib.rs

1#![deny(
2    warnings,
3    missing_debug_implementations,
4    missing_copy_implementations,
5    missing_docs
6)]
7
8//! This crate provides:
9//! - A tracing layer, `TelemetryLayer`, that can be used to publish trace data to honeycomb.io
10//! - Utilities for implementing distributed tracing against the honeycomb.io backend
11//!
12//! As a tracing layer, `TelemetryLayer` can be composed with other layers to provide stdout logging, filtering, etc.
13
14mod honeycomb;
15mod reporter;
16mod span_id;
17mod trace_id;
18mod visitor;
19
20pub use honeycomb::HoneycombTelemetry;
21pub use reporter::{LibhoneyReporter, Reporter, StdoutReporter};
22pub use span_id::SpanId;
23pub use trace_id::TraceId;
24#[doc(no_inline)]
25pub use tracing_distributed::{TelemetryLayer, TraceCtxError};
26pub use visitor::HoneycombVisitor;
27
28pub(crate) mod deterministic_sampler;
29
30#[cfg(feature = "use_parking_lot")]
31use parking_lot::Mutex;
32#[cfg(not(feature = "use_parking_lot"))]
33use std::sync::Mutex;
34
35/// Register the current span as the local root of a distributed trace.
36///
37/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
38pub fn register_dist_tracing_root(
39    trace_id: TraceId,
40    remote_parent_span: Option<SpanId>,
41) -> Result<(), TraceCtxError> {
42    tracing_distributed::register_dist_tracing_root(trace_id, remote_parent_span)
43}
44
45/// Retrieve the distributed trace context associated with the current span.
46///
47/// Returns the `TraceId`, if any, that the current span is associated with along with
48/// the `SpanId` belonging to the current span.
49///
50/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
51pub fn current_dist_trace_ctx() -> Result<(TraceId, SpanId), TraceCtxError> {
52    tracing_distributed::current_dist_trace_ctx()
53}
54
55/// Construct a TelemetryLayer that does not publish telemetry to any backend.
56///
57/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
58pub fn new_blackhole_telemetry_layer(
59) -> TelemetryLayer<tracing_distributed::BlackholeTelemetry<SpanId, TraceId>, SpanId, TraceId> {
60    TelemetryLayer::new(
61        "honeycomb_blackhole_tracing_layer",
62        tracing_distributed::BlackholeTelemetry::default(),
63        move |tracing_id| SpanId { tracing_id },
64    )
65}
66
67/// Construct a TelemetryLayer that publishes telemetry to honeycomb.io using the provided honeycomb config.
68///
69/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
70pub fn new_honeycomb_telemetry_layer(
71    service_name: &'static str,
72    honeycomb_config: libhoney::Config,
73) -> TelemetryLayer<HoneycombTelemetry<LibhoneyReporter>, SpanId, TraceId> {
74    let reporter = libhoney::init(honeycomb_config);
75    // publishing requires &mut so just mutex-wrap it
76    // FIXME: may not be performant, investigate options (eg mpsc)
77    let reporter = Mutex::new(reporter);
78
79    TelemetryLayer::new(
80        service_name,
81        HoneycombTelemetry::new(reporter, None),
82        move |tracing_id| SpanId { tracing_id },
83    )
84}
85
86/// Construct a TelemetryLayer that publishes telemetry to honeycomb.io using the
87/// provided honeycomb config, and sample rate.
88///
89/// This function differs from `new_honeycomb_telemetry_layer` and the `sample_rate`
90/// on the `libhoney::Config` there in an important way. `libhoney` samples `Event`
91/// data, which is individual spans on each trace. This means that using the
92/// sampling logic in libhoney may result in missing event data or incomplete
93/// traces. Calling this function provides trace-level sampling, meaning sampling
94/// decisions are based on a modulo of the traceID, and events in a single trace
95/// will not be sampled differently. If the trace is sampled, then all spans
96/// under it will be sent to honeycomb. If a trace is not sampled, no spans or
97/// events under it will be sent. When using this trace-level sampling, the
98/// `sample_rate` parameter on the `libhoney::Config` should be set to 1, which
99/// is the default.
100///
101/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
102pub fn new_honeycomb_telemetry_layer_with_trace_sampling(
103    service_name: &'static str,
104    honeycomb_config: libhoney::Config,
105    sample_rate: u32,
106) -> TelemetryLayer<HoneycombTelemetry<LibhoneyReporter>, SpanId, TraceId> {
107    let reporter = libhoney::init(honeycomb_config);
108    // publishing requires &mut so just mutex-wrap it
109    // FIXME: may not be performant, investigate options (eg mpsc)
110    let reporter = Mutex::new(reporter);
111
112    TelemetryLayer::new(
113        service_name,
114        HoneycombTelemetry::new(reporter, Some(sample_rate)),
115        move |tracing_id| SpanId { tracing_id },
116    )
117}
118
119/// Builds Honeycomb Telemetry with custom configuration values.
120///
121/// Methods can be chained in order to set the configuration values. The
122/// TelemetryLayer is constructed by calling [`build`].
123///
124/// New instances of `Builder` are obtained via [`Builder::new_libhoney`]
125/// or [`Builder::new_stdout`].
126///
127/// [`Builder::new_stdout`] is useful when instrumenting e.g. AWS Lambda functions.
128/// See more at [AWS Lambda Instrumentation]. For almost all other use cases you are probably
129/// looking for [`Builder::new_libhoney`].
130///
131/// [`build`]: method@Self::build
132/// [`Builder::new_stdout`]: method@Builder::<StdoutReporter>::new_stdout
133/// [`Builder::new_libhoney`]: method@Builder::<LibhoneyReporter>::new_libhoney
134/// [AWS Lambda Instrumentation]: https://docs.honeycomb.io/getting-data-in/integrations/aws/aws-lambda/
135#[derive(Debug)]
136pub struct Builder<R> {
137    reporter: R,
138    sample_rate: Option<u32>,
139    service_name: &'static str,
140}
141
142impl Builder<StdoutReporter> {
143    /// Returns a new `Builder` that reports data to stdout
144    pub fn new_stdout(service_name: &'static str) -> Self {
145        Self {
146            reporter: StdoutReporter,
147            sample_rate: None,
148            service_name,
149        }
150    }
151}
152
153impl Builder<LibhoneyReporter> {
154    /// Returns a new `Builder` that reports data to a [`libhoney::Client`]
155    pub fn new_libhoney(service_name: &'static str, config: libhoney::Config) -> Self {
156        let reporter = libhoney::init(config);
157
158        // Handle the libhoney response channel by consuming and ignoring messages. This prevents a
159        // deadlock because the responses() channel is bounded and gains an item for every event
160        // emitted.
161        let responses = reporter.responses();
162        std::thread::spawn(move || {
163            loop {
164                if responses.recv().is_err() {
165                    // If we receive an error, the channel is empty & disconnected. No need to keep
166                    // this thread around.
167                    break;
168                }
169            }
170        });
171
172        // publishing requires &mut so just mutex-wrap it
173        // FIXME: may not be performant, investigate options (eg mpsc)
174        let reporter = Mutex::new(reporter);
175
176        Self {
177            reporter,
178            sample_rate: None,
179            service_name,
180        }
181    }
182}
183
184impl<R: Reporter> Builder<R> {
185    /// Enables sampling for the telemetry layer.
186    ///
187    /// The `sample_rate` on the `libhoney::Config` is different from this in an important way.
188    /// `libhoney` samples `Event` data, which is individual spans on each trace.
189    /// This means that using the sampling logic in libhoney may result in missing
190    /// event data or incomplete traces.
191    /// Calling this function provides trace-level sampling, meaning sampling
192    /// decisions are based on a modulo of the traceID, and events in a single trace
193    /// will not be sampled differently. If the trace is sampled, then all spans
194    /// under it will be sent to honeycomb. If a trace is not sampled, no spans or
195    /// events under it will be sent. When using this trace-level sampling,
196    /// when using a [`LibhoneyReporter`] the `sample_rate` parameter on the
197    /// [`libhoney::Config`] should be set to 1, which is the default.
198    pub fn with_trace_sampling(mut self, sample_rate: u32) -> Self {
199        self.sample_rate.replace(sample_rate);
200        self
201    }
202
203    /// Constructs the configured `TelemetryLayer`
204    pub fn build(self) -> TelemetryLayer<HoneycombTelemetry<R>, SpanId, TraceId> {
205        TelemetryLayer::new(
206            self.service_name,
207            HoneycombTelemetry::new(self.reporter, self.sample_rate),
208            move |tracing_id| SpanId { tracing_id },
209        )
210    }
211}