tracing_honeycomb/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#![deny(
    warnings,
    missing_debug_implementations,
    missing_copy_implementations,
    missing_docs
)]

//! This crate provides:
//! - A tracing layer, `TelemetryLayer`, that can be used to publish trace data to honeycomb.io
//! - Utilities for implementing distributed tracing against the honeycomb.io backend
//!
//! As a tracing layer, `TelemetryLayer` can be composed with other layers to provide stdout logging, filtering, etc.

mod honeycomb;
mod reporter;
mod span_id;
mod trace_id;
mod visitor;

pub use honeycomb::HoneycombTelemetry;
pub use reporter::{LibhoneyReporter, Reporter, StdoutReporter};
pub use span_id::SpanId;
pub use trace_id::TraceId;
#[doc(no_inline)]
pub use tracing_distributed::{TelemetryLayer, TraceCtxError};
pub use visitor::HoneycombVisitor;

pub(crate) mod deterministic_sampler;

#[cfg(feature = "use_parking_lot")]
use parking_lot::Mutex;
#[cfg(not(feature = "use_parking_lot"))]
use std::sync::Mutex;

/// Register the current span as the local root of a distributed trace.
///
/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
pub fn register_dist_tracing_root(
    trace_id: TraceId,
    remote_parent_span: Option<SpanId>,
) -> Result<(), TraceCtxError> {
    tracing_distributed::register_dist_tracing_root(trace_id, remote_parent_span)
}

/// Retrieve the distributed trace context associated with the current span.
///
/// Returns the `TraceId`, if any, that the current span is associated with along with
/// the `SpanId` belonging to the current span.
///
/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
pub fn current_dist_trace_ctx() -> Result<(TraceId, SpanId), TraceCtxError> {
    tracing_distributed::current_dist_trace_ctx()
}

/// Construct a TelemetryLayer that does not publish telemetry to any backend.
///
/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
pub fn new_blackhole_telemetry_layer(
) -> TelemetryLayer<tracing_distributed::BlackholeTelemetry<SpanId, TraceId>, SpanId, TraceId> {
    TelemetryLayer::new(
        "honeycomb_blackhole_tracing_layer",
        tracing_distributed::BlackholeTelemetry::default(),
        move |tracing_id| SpanId { tracing_id },
    )
}

/// Construct a TelemetryLayer that publishes telemetry to honeycomb.io using the provided honeycomb config.
///
/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
pub fn new_honeycomb_telemetry_layer(
    service_name: &'static str,
    honeycomb_config: libhoney::Config,
) -> TelemetryLayer<HoneycombTelemetry<LibhoneyReporter>, SpanId, TraceId> {
    let reporter = libhoney::init(honeycomb_config);
    // publishing requires &mut so just mutex-wrap it
    // FIXME: may not be performant, investigate options (eg mpsc)
    let reporter = Mutex::new(reporter);

    TelemetryLayer::new(
        service_name,
        HoneycombTelemetry::new(reporter, None),
        move |tracing_id| SpanId { tracing_id },
    )
}

/// Construct a TelemetryLayer that publishes telemetry to honeycomb.io using the
/// provided honeycomb config, and sample rate.
///
/// This function differs from `new_honeycomb_telemetry_layer` and the `sample_rate`
/// on the `libhoney::Config` there in an important way. `libhoney` samples `Event`
/// data, which is individual spans on each trace. This means that using the
/// sampling logic in libhoney may result in missing event data or incomplete
/// traces. Calling this function provides trace-level sampling, meaning sampling
/// decisions are based on a modulo of the traceID, and events in a single trace
/// will not be sampled differently. If the trace is sampled, then all spans
/// under it will be sent to honeycomb. If a trace is not sampled, no spans or
/// events under it will be sent. When using this trace-level sampling, the
/// `sample_rate` parameter on the `libhoney::Config` should be set to 1, which
/// is the default.
///
/// Specialized to the honeycomb.io-specific SpanId and TraceId provided by this crate.
pub fn new_honeycomb_telemetry_layer_with_trace_sampling(
    service_name: &'static str,
    honeycomb_config: libhoney::Config,
    sample_rate: u32,
) -> TelemetryLayer<HoneycombTelemetry<LibhoneyReporter>, SpanId, TraceId> {
    let reporter = libhoney::init(honeycomb_config);
    // publishing requires &mut so just mutex-wrap it
    // FIXME: may not be performant, investigate options (eg mpsc)
    let reporter = Mutex::new(reporter);

    TelemetryLayer::new(
        service_name,
        HoneycombTelemetry::new(reporter, Some(sample_rate)),
        move |tracing_id| SpanId { tracing_id },
    )
}

/// Builds Honeycomb Telemetry with custom configuration values.
///
/// Methods can be chained in order to set the configuration values. The
/// TelemetryLayer is constructed by calling [`build`].
///
/// New instances of `Builder` are obtained via [`Builder::new_libhoney`]
/// or [`Builder::new_stdout`].
///
/// [`Builder::new_stdout`] is useful when instrumenting e.g. AWS Lambda functions.
/// See more at [AWS Lambda Instrumentation]. For almost all other use cases you are probably
/// looking for [`Builder::new_libhoney`].
///
/// [`build`]: method@Self::build
/// [`Builder::new_stdout`]: method@Builder::<StdoutReporter>::new_stdout
/// [`Builder::new_libhoney`]: method@Builder::<LibhoneyReporter>::new_libhoney
/// [AWS Lambda Instrumentation]: https://docs.honeycomb.io/getting-data-in/integrations/aws/aws-lambda/
#[derive(Debug)]
pub struct Builder<R> {
    reporter: R,
    sample_rate: Option<u32>,
    service_name: &'static str,
}

impl Builder<StdoutReporter> {
    /// Returns a new `Builder` that reports data to stdout
    pub fn new_stdout(service_name: &'static str) -> Self {
        Self {
            reporter: StdoutReporter,
            sample_rate: None,
            service_name,
        }
    }
}

impl Builder<LibhoneyReporter> {
    /// Returns a new `Builder` that reports data to a [`libhoney::Client`]
    pub fn new_libhoney(service_name: &'static str, config: libhoney::Config) -> Self {
        let reporter = libhoney::init(config);

        // Handle the libhoney response channel by consuming and ignoring messages. This prevents a
        // deadlock because the responses() channel is bounded and gains an item for every event
        // emitted.
        let responses = reporter.responses();
        std::thread::spawn(move || {
            loop {
                if responses.recv().is_err() {
                    // If we receive an error, the channel is empty & disconnected. No need to keep
                    // this thread around.
                    break;
                }
            }
        });

        // publishing requires &mut so just mutex-wrap it
        // FIXME: may not be performant, investigate options (eg mpsc)
        let reporter = Mutex::new(reporter);

        Self {
            reporter,
            sample_rate: None,
            service_name,
        }
    }
}

impl<R: Reporter> Builder<R> {
    /// Enables sampling for the telemetry layer.
    ///
    /// The `sample_rate` on the `libhoney::Config` is different from this in an important way.
    /// `libhoney` samples `Event` data, which is individual spans on each trace.
    /// This means that using the sampling logic in libhoney may result in missing
    /// event data or incomplete traces.
    /// Calling this function provides trace-level sampling, meaning sampling
    /// decisions are based on a modulo of the traceID, and events in a single trace
    /// will not be sampled differently. If the trace is sampled, then all spans
    /// under it will be sent to honeycomb. If a trace is not sampled, no spans or
    /// events under it will be sent. When using this trace-level sampling,
    /// when using a [`LibhoneyReporter`] the `sample_rate` parameter on the
    /// [`libhoney::Config`] should be set to 1, which is the default.
    pub fn with_trace_sampling(mut self, sample_rate: u32) -> Self {
        self.sample_rate.replace(sample_rate);
        self
    }

    /// Constructs the configured `TelemetryLayer`
    pub fn build(self) -> TelemetryLayer<HoneycombTelemetry<R>, SpanId, TraceId> {
        TelemetryLayer::new(
            self.service_name,
            HoneycombTelemetry::new(self.reporter, self.sample_rate),
            move |tracing_id| SpanId { tracing_id },
        )
    }
}