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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LogFormat {
Pretty,
Compact,
Json,
}
impl std::fmt::Display for LogFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Pretty => "pretty",
Self::Compact => "compact",
Self::Json => "json",
})
}
}
impl std::str::FromStr for LogFormat {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_str() {
"pretty" => Self::Pretty,
"compact" => Self::Compact,
"json" => Self::Json,
unknown => anyhow::bail!("unknown LogFormat: '{unknown}"),
})
}
}
// ---
const fn default_telemetry_attributes() -> &'static str {
concat!(
"service.namespace=redap,service.version=",
env!("CARGO_PKG_VERSION")
)
}
const fn default_log_filter() -> &'static str {
if cfg!(debug_assertions) {
"debug"
} else {
"info"
}
}
/// Complete configuration for all things telemetry.
///
/// Many of these are part of the official `OpenTelemetry` spec and can be configured directly via
/// the environment. Refer to this command's help as well as [the spec].
///
/// [the spec]: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
#[derive(Clone, Debug, clap::Parser)]
#[clap(author, version, about)]
pub struct TelemetryArgs {
/// Enable telemetry?
///
/// If disabled, this will completely skip the initialization of the different telemetry subscribers,
/// both native and `OpenTelemetry`.
/// i.e. all events will be dropped immediately, with very minimal cost.
/// Disabling is particularly useful in conjunction with `TRACY_ENABLED`, to prevent noise in the trace
/// data.
///
/// To remove all traces of telemetry _at compile time_, compile with the appropriate `tracing`
/// feature flags instead: <https://docs.rs/tracing/0.1.41/tracing/level_filters/index.html>.
#[cfg_attr(
feature = "enabled",
clap(
long = "telemetry-enabled",
env = "TELEMETRY_ENABLED",
default_value_t = true
)
)]
#[cfg_attr(
not(feature = "enabled"),
clap(
long = "telemetry-enabled",
env = "TELEMETRY_ENABLED",
default_value_t = false
)
)]
pub enabled: bool,
/// If set, all the traces and logs will be forwarded to [Tracy], without any filtering.
///
/// It is recommended to set `TELEMETRY_ENABLED=false` when using this, to prevent the noise
/// from the rest of the `tracing` stack of interfering with your measurements.
///
/// This requires the `tracy` feature flag.
///
/// [Tracy]: https://github.com/wolfpld/tracy
#[cfg_attr(
feature = "tracy_enabled",
clap(long, env = "TRACY_ENABLED", default_value_t = true)
)]
#[cfg_attr(
not(feature = "tracy_enabled"),
clap(long, env = "TRACY_ENABLED", default_value_t = false)
)]
pub tracy_enabled: bool,
/// The service name used for all things telemetry.
///
/// This is mandatory, but we leave it as optional to give users a chance to set it at initialization
/// time (as opposed to e.g. via env configuration) if needed.
///
/// Part of the `OpenTelemetry` spec.
#[clap(long, env = "OTEL_SERVICE_NAME")]
pub service_name: Option<String>,
/// The service attributes used for all things telemetry.
///
/// Expects a comma-separated string of key=value pairs, e.g. `a=b,c=d`.
///
/// Part of the `OpenTelemetry` spec.
#[clap(
long,
env = "OTEL_RESOURCE_ATTRIBUTES",
default_value = default_telemetry_attributes(),
)]
pub attributes: String,
/// This is the same as `RUST_LOG`.
///
/// This only affects logs, not traces nor metrics.
#[clap(long, env = "RUST_LOG", default_value_t = default_log_filter().to_owned())]
pub log_filter: String,
/// Capture test output as part of the logs.
#[clap(long, env = "RUST_LOG_CAPTURE_TEST_OUTPUT", default_value_t = false)]
pub log_test_output: bool,
/// Use `json` in production. Pick between `pretty` and `compact` during development according
/// to your preferences.
#[clap(long, env = "RUST_LOG_FORMAT", default_value_t = LogFormat::Pretty)]
pub log_format: LogFormat,
/// If true, log extra information about all retired spans, including their timings.
#[clap(long, env = "RUST_LOG_CLOSED_SPANS", default_value_t = false)]
pub log_closed_spans: bool,
/// Should an OTLP exporter for logs be setup too (in addition to trace events)?
///
/// *Not* part of the `OpenTelemetry` spec.
///
/// See also [`Self::log_endpoint`].
#[clap(long, env = "OTEL_EXPORTER_OTLP_LOGS_ENABLED", default_value_t = false)]
pub log_otlp_enabled: bool,
/// The gRPC OTLP endpoint to send the logs to.
///
/// When unset (or empty), no log exporter is created. As a fallback, the umbrella
/// `OTEL_EXPORTER_OTLP_ENDPOINT` env var is consulted at telemetry init.
///
/// Part of the `OpenTelemetry` spec.
#[clap(long, env = "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", default_value = "")]
pub log_endpoint: String,
/// Same as `RUST_LOG`, but for traces.
///
/// This only affects traces, not logs nor metrics.
#[clap(long, env = "RUST_TRACE", default_value = "info")]
pub trace_filter: String,
/// The gRPC OTLP endpoint to send the traces to.
///
/// When unset (or empty), no trace exporter is created — spans still flow through
/// the in-process tracing pipeline (so propagators / `current_trace_id()` keep
/// working) but nothing is shipped to a collector. As a fallback, the umbrella
/// `OTEL_EXPORTER_OTLP_ENDPOINT` env var is consulted at telemetry init.
///
/// Part of the `OpenTelemetry` spec.
#[clap(long, env = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", default_value = "")]
pub trace_endpoint: String,
/// How are spans sampled?
///
/// This is applied _after_ `RUST_TRACE`.
///
/// Remember: sampling only applies at the `OpenTelemetry` level, i.e. we are sampling the
/// traces we export, *not* the traces we generate. Internally, all traces are always
/// generated, there is no such thing as sampling at the `tracing` level.
///
/// Part of the `OpenTelemetry` spec.
#[clap(
long,
env = "OTEL_TRACES_SAMPLER",
default_value = "parentbased_traceidratio"
)]
pub trace_sampler: String,
/// The specified value will only be used if `OTEL_TRACES_SAMPLER` is set.
///
/// Each Sampler type defines its own expected input, if any. Invalid or unrecognized input
/// MUST be logged and MUST be otherwise ignored, i.e. the implementation MUST behave as if
/// `OTEL_TRACES_SAMPLER_ARG` is not set.
///
/// Part of the `OpenTelemetry` spec.
#[clap(long, env = "OTEL_TRACES_SAMPLER_ARG", default_value = "1.0")]
pub trace_sampler_args: String,
/// The HTTP OTLP endpoint to send the metrics to.
///
/// Part of the `OpenTelemetry` spec.
#[clap(long, env = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", default_value = "")]
pub metric_endpoint: String,
/// The interval in milliseconds at which metrics are pushed to the collector.
///
/// Part of the `OpenTelemetry` spec.
#[clap(long, env = "OTEL_METRIC_EXPORT_INTERVAL", default_value = "10000")]
pub metric_interval: String,
/// Listening address for dedicated HTTP /metrics endpoint for scraping.
///
/// Setting this has no immediate effect. The actual listener has to be
/// started by calling `Telemetry::start_metrics_listener()`.
///
/// Metrics are the same as those being pushed to the OTLP endpoint.
///
/// Format: ":9091", "0.0.0.0:9091", or "127.0.0.1:9091"
/// Empty value means the listener is disabled.
///
/// This has no effect if `TELEMETRY_ENABLED` is false.
#[clap(long, env = "METRICS_LISTEN_ADDRESS", default_value = "")]
pub metrics_listen_address: String,
}