#![warn(missing_docs)]
use tracing::{Event, Subscriber};
use tracing_subscriber::{
filter::EnvFilter,
fmt::{format::FmtSpan, time::ChronoUtc, FmtContext},
registry::LookupSpan,
FmtSubscriber,
};
use std::{str::FromStr, sync::Once};
use flames::{toml_path, FlameTimed};
use fmt::*;
mod flames;
mod fmt;
pub mod metrics;
mod open;
#[cfg(all(feature = "opentelemetry-on", feature = "channels"))]
pub use open::channel;
#[cfg(feature = "opentelemetry-on")]
pub use open::should_run;
pub use open::{Config, Context, MsgWrap, OpenSpanExt};
pub use tracing;
#[derive(Debug, Clone)]
pub enum Output {
Compact,
Json,
JsonTimed,
Log,
LogTimed,
FlameTimed,
IceTimed,
OpenTel,
None,
}
pub type ParseError = String;
static INIT: Once = Once::new();
impl FromStr for Output {
type Err = ParseError;
fn from_str(day: &str) -> Result<Self, Self::Err> {
match day {
"Json" => Ok(Output::Json),
"JsonTimed" => Ok(Output::JsonTimed),
"IceTimed" => Ok(Output::IceTimed),
"Log" => Ok(Output::Log),
"LogTimed" => Ok(Output::LogTimed),
"FlameTimed" => Ok(Output::FlameTimed),
"Compact" => Ok(Output::Compact),
"OpenTel" => Ok(Output::OpenTel),
"None" => Ok(Output::None),
_ => Err("Could not parse log output type".into()),
}
}
}
pub fn test_run() -> Result<(), errors::TracingError> {
if std::env::var_os("RUST_LOG").is_none() {
return Ok(());
}
init_fmt(Output::Log)
}
pub fn test_run_open() -> Result<(), errors::TracingError> {
if std::env::var_os("RUST_LOG").is_none() {
return Ok(());
}
init_fmt(Output::OpenTel)
}
pub fn test_run_timed() -> Result<(), errors::TracingError> {
if std::env::var_os("RUST_LOG").is_none() {
return Ok(());
}
init_fmt(Output::LogTimed)
}
pub fn test_run_timed_json() -> Result<(), errors::TracingError> {
if std::env::var_os("RUST_LOG").is_none() {
return Ok(());
}
init_fmt(Output::JsonTimed)
}
pub fn test_run_timed_flame(path: Option<&str>) -> Result<Option<impl Drop>, errors::TracingError> {
if std::env::var_os("RUST_LOG").is_none() {
return Ok(None);
}
init_fmt(Output::FlameTimed)?;
Ok(path.and_then(|p| {
toml_path().map(|mut t| {
t.push(p);
FlameTimed::new(t)
})
}))
}
pub fn test_run_timed_ice(path: Option<&str>) -> Result<Option<impl Drop>, errors::TracingError> {
if std::env::var_os("RUST_LOG").is_none() {
return Ok(None);
}
init_fmt(Output::IceTimed)?;
Ok(path.and_then(|p| {
toml_path().map(|mut t| {
t.push(p);
FlameTimed::new(t)
})
}))
}
pub fn init_fmt(output: Output) -> Result<(), errors::TracingError> {
let mut filter = match std::env::var("RUST_LOG") {
Ok(_) => EnvFilter::from_default_env(),
Err(_) => EnvFilter::from_default_env().add_directive("[wasm_debug]=debug".parse()?),
};
if std::env::var("CUSTOM_FILTER").is_ok() {
EnvFilter::try_from_env("CUSTOM_FILTER")
.map_err(|e| eprintln!("Failed to parse CUSTOM_FILTER {:?}", e))
.map(|f| {
filter = f;
})
.ok();
}
let fm: fn(
ctx: &FmtContext<'_, _, _>,
&mut dyn std::fmt::Write,
&Event<'_>,
) -> std::fmt::Result = format_event;
let fm_flame: fn(
ctx: &FmtContext<'_, _, _>,
&mut dyn std::fmt::Write,
&Event<'_>,
) -> std::fmt::Result = format_event_flame;
let fm_ice: fn(
ctx: &FmtContext<'_, _, _>,
&mut dyn std::fmt::Write,
&Event<'_>,
) -> std::fmt::Result = format_event_ice;
let subscriber = FmtSubscriber::builder()
.with_writer(std::io::stderr)
.with_target(true);
match output {
Output::Json => {
let subscriber = subscriber
.with_env_filter(filter)
.with_timer(ChronoUtc::rfc3339())
.json()
.event_format(fm);
finish(subscriber.finish())
}
Output::JsonTimed => {
let subscriber = subscriber
.with_span_events(FmtSpan::CLOSE)
.with_env_filter(filter)
.with_timer(ChronoUtc::rfc3339())
.json()
.event_format(fm);
finish(subscriber.finish())
}
Output::Log => finish(subscriber.with_env_filter(filter).finish()),
Output::LogTimed => {
let subscriber = subscriber.with_span_events(FmtSpan::CLOSE);
finish(subscriber.with_env_filter(filter).finish())
}
Output::FlameTimed => {
let subscriber = subscriber
.with_span_events(FmtSpan::CLOSE)
.with_env_filter(filter)
.with_timer(ChronoUtc::rfc3339())
.event_format(fm_flame);
finish(subscriber.finish())
}
Output::IceTimed => {
let subscriber = subscriber
.with_span_events(FmtSpan::CLOSE)
.with_env_filter(filter)
.with_timer(ChronoUtc::rfc3339())
.event_format(fm_ice);
finish(subscriber.finish())
}
Output::Compact => {
let subscriber = subscriber.compact();
finish(subscriber.with_env_filter(filter).finish())
}
Output::OpenTel => {
#[cfg(feature = "opentelemetry-on")]
{
use open::OPEN_ON;
use opentelemetry::api::Provider;
OPEN_ON.store(true, std::sync::atomic::Ordering::SeqCst);
use tracing_subscriber::prelude::*;
open::init();
let tracer = opentelemetry::sdk::Provider::default().get_tracer("component_name");
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
finish(
subscriber
.with_env_filter(filter)
.finish()
.with(telemetry)
.with(open::OpenLayer),
)
}
#[cfg(not(feature = "opentelemetry-on"))]
{
Ok(())
}
}
Output::None => Ok(()),
}
}
fn finish<S>(subscriber: S) -> Result<(), errors::TracingError>
where
S: Subscriber + Send + Sync + for<'span> LookupSpan<'span>,
{
let mut result = Ok(());
INIT.call_once(|| {
result = tracing::subscriber::set_global_default(subscriber).map_err(Into::into);
});
result
}
pub mod errors {
use thiserror::Error;
#[allow(missing_docs)] #[derive(Error, Debug)]
pub enum TracingError {
#[error(transparent)]
SetGlobal(#[from] tracing::subscriber::SetGlobalDefaultError),
#[error("Failed to setup tracing flame")]
TracingFlame,
#[error(transparent)]
BadDirective(#[from] tracing_subscriber::filter::ParseError),
}
}