113 lines
3.8 KiB
Rust
113 lines
3.8 KiB
Rust
//! The Abscissa component for Zebra's `tracing` implementation.
|
|
|
|
use abscissa_core::{Component, FrameworkError, FrameworkErrorKind, Shutdown};
|
|
use tracing_error::ErrorLayer;
|
|
use tracing_subscriber::{
|
|
fmt::Formatter, layer::SubscriberExt, reload::Handle, util::SubscriberInitExt, EnvFilter,
|
|
FmtSubscriber,
|
|
};
|
|
|
|
use crate::{application::app_version, config::TracingSection};
|
|
|
|
use super::flame;
|
|
|
|
/// Abscissa component for initializing the `tracing` subsystem
|
|
pub struct Tracing {
|
|
filter_handle: Handle<EnvFilter, Formatter>,
|
|
flamegrapher: Option<flame::Grapher>,
|
|
}
|
|
|
|
impl Tracing {
|
|
/// Try to create a new [`Tracing`] component with the given `filter`.
|
|
pub fn new(config: TracingSection) -> Result<Self, FrameworkError> {
|
|
let filter = config.filter.unwrap_or_else(|| "".to_string());
|
|
let flame_root = &config.flamegraph;
|
|
|
|
// Only use color if tracing output is being sent to a terminal or if it was explicitly
|
|
// forced to.
|
|
let use_color =
|
|
config.force_use_color || (config.use_color && atty::is(atty::Stream::Stdout));
|
|
|
|
// Construct a tracing subscriber with the supplied filter and enable reloading.
|
|
let builder = FmtSubscriber::builder()
|
|
.with_ansi(use_color)
|
|
.with_env_filter(filter)
|
|
.with_filter_reloading();
|
|
let filter_handle = builder.reload_handle();
|
|
|
|
let (flamelayer, flamegrapher) = if let Some(path) = flame_root {
|
|
let (flamelayer, flamegrapher) = flame::layer(path);
|
|
(Some(flamelayer), Some(flamegrapher))
|
|
} else {
|
|
(None, None)
|
|
};
|
|
|
|
let journaldlayer = if config.use_journald {
|
|
let layer = tracing_journald::layer()
|
|
.map_err(|e| FrameworkErrorKind::ComponentError.context(e))?;
|
|
Some(layer)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let subscriber = builder.finish().with(ErrorLayer::default());
|
|
|
|
#[cfg(feature = "enable-sentry")]
|
|
let subscriber = subscriber.with(sentry_tracing::layer());
|
|
|
|
match (flamelayer, journaldlayer) {
|
|
(None, None) => subscriber.init(),
|
|
(Some(layer1), None) => subscriber.with(layer1).init(),
|
|
(None, Some(layer2)) => subscriber.with(layer2).init(),
|
|
(Some(layer1), Some(layer2)) => subscriber.with(layer1).with(layer2).init(),
|
|
};
|
|
|
|
Ok(Self {
|
|
filter_handle,
|
|
flamegrapher,
|
|
})
|
|
}
|
|
|
|
/// Return the currently-active tracing filter.
|
|
pub fn filter(&self) -> String {
|
|
self.filter_handle
|
|
.with_current(|filter| filter.to_string())
|
|
.expect("the subscriber is not dropped before the component is")
|
|
}
|
|
|
|
/// Reload the currently-active filter with the supplied value.
|
|
///
|
|
/// This can be used to provide a dynamic tracing filter endpoint.
|
|
pub fn reload_filter(&self, filter: impl Into<EnvFilter>) {
|
|
self.filter_handle
|
|
.reload(filter)
|
|
.expect("the subscriber is not dropped before the component is");
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for Tracing {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("Tracing").finish()
|
|
}
|
|
}
|
|
|
|
impl<A: abscissa_core::Application> Component<A> for Tracing {
|
|
fn id(&self) -> abscissa_core::component::Id {
|
|
abscissa_core::component::Id::new("zebrad::components::tracing::component::Tracing")
|
|
}
|
|
|
|
fn version(&self) -> abscissa_core::Version {
|
|
app_version()
|
|
}
|
|
|
|
fn before_shutdown(&self, _kind: Shutdown) -> Result<(), FrameworkError> {
|
|
if let Some(ref grapher) = self.flamegrapher {
|
|
info!("writing flamegraph");
|
|
grapher
|
|
.write_flamegraph()
|
|
.map_err(|e| FrameworkErrorKind::ComponentError.context(e))?
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|