wire_framework/vlog/
mod.rs1use std::time::Duration;
5
6use ::sentry::ClientInitGuard;
7use eyre::Context as _;
8use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
9
10pub mod logs;
11pub mod opentelemetry;
12pub mod prometheus;
13pub mod sentry;
14
15pub use logs::Logs;
16pub use opentelemetry::OpenTelemetry;
17pub use sentry::Sentry;
18
19#[derive(Debug, Default)]
22pub struct ObservabilityBuilder {
23 logs: Option<Logs>,
24 opentelemetry_layer: Option<OpenTelemetry>,
25 sentry: Option<Sentry>,
26}
27
28pub struct ObservabilityGuard {
31 otlp_tracing_provider: Option<opentelemetry_sdk::trace::SdkTracerProvider>,
33 otlp_logging_provider: Option<opentelemetry_sdk::logs::SdkLoggerProvider>,
35 sentry_guard: Option<ClientInitGuard>,
37}
38
39impl ObservabilityGuard {
40 pub fn force_flush(&self) {
43 const FLUSH_TIMEOUT: Duration = Duration::from_secs(1);
45
46 if let Some(sentry_guard) = &self.sentry_guard {
47 sentry_guard.flush(Some(FLUSH_TIMEOUT));
48 tracing::info!("Sentry events are flushed");
49 }
50
51 if let Some(provider) = &self.otlp_tracing_provider {
52 if let Err(err) = provider.force_flush() {
53 tracing::warn!("Flushing the spans failed: {err:?}");
54 }
55 tracing::info!("Spans are flushed");
56 }
57
58 if let Some(provider) = &self.otlp_logging_provider {
59 if let Err(err) = provider.force_flush() {
60 tracing::warn!("Flushing the logs failed: {err:?}");
61 }
62 tracing::info!("Logs are flushed");
63 }
64 }
65
66 pub fn shutdown(&mut self) {
69 const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1);
71
72 if let Some(sentry_guard) = self.sentry_guard.take() {
74 sentry_guard.close(Some(SHUTDOWN_TIMEOUT));
75 tracing::info!("Sentry client is shut down");
76 }
77 if let Some(provider) = self.otlp_tracing_provider.take() {
78 if let Err(err) = provider.shutdown() {
79 tracing::warn!("Shutting down the OTLP tracing provider failed: {err:?}");
80 } else {
81 tracing::info!("OTLP tracing provider is shut down");
82 }
83 }
84 if let Some(provider) = self.otlp_logging_provider.take() {
85 if let Err(err) = provider.shutdown() {
86 tracing::warn!("Shutting down the OTLP logs provider failed: {err:?}");
87 } else {
88 tracing::info!("OTLP logs provider is shut down");
89 }
90 }
91 }
92}
93
94impl Drop for ObservabilityGuard {
95 fn drop(&mut self) {
96 self.force_flush();
97 self.shutdown();
98 }
99}
100
101impl std::fmt::Debug for ObservabilityGuard {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("ObservabilityGuard").finish()
104 }
105}
106
107impl ObservabilityBuilder {
108 pub fn new() -> Self {
110 Self::default()
111 }
112
113 pub fn with_logs(mut self, logs: Option<Logs>) -> Self {
114 self.logs = logs;
115 self
116 }
117
118 pub fn with_opentelemetry(mut self, opentelemetry: Option<OpenTelemetry>) -> Self {
119 self.opentelemetry_layer = opentelemetry;
120 self
121 }
122
123 pub fn with_sentry(mut self, sentry: Option<Sentry>) -> Self {
124 self.sentry = sentry;
125 self
126 }
127
128 pub fn try_build(self) -> eyre::Result<ObservabilityGuard> {
131 let logs = self.logs.unwrap_or_default();
132 logs.install_panic_hook();
133
134 let global_filter = logs.build_filter();
137
138 let logs_layer = logs.into_layer();
139 let (otlp_tracing_provider, otlp_tracing_layer) = self
140 .opentelemetry_layer
141 .as_ref()
142 .and_then(|layer| layer.tracing_layer())
143 .unzip();
144 let (otlp_logging_provider, otlp_logging_layer) = self
145 .opentelemetry_layer
146 .and_then(|layer| layer.logs_layer())
147 .unzip();
148
149 tracing_subscriber::registry()
150 .with(global_filter)
151 .with(logs_layer)
152 .with(otlp_tracing_layer)
153 .with(otlp_logging_layer)
154 .try_init()
155 .context("failed installing global tracer / logger")?;
156
157 let sentry_guard = self.sentry.map(|sentry| sentry.install());
158
159 Ok(ObservabilityGuard {
160 otlp_tracing_provider,
161 otlp_logging_provider,
162 sentry_guard,
163 })
164 }
165
166 pub fn build(self) -> ObservabilityGuard {
168 self.try_build().unwrap()
169 }
170}