#![warn(missing_docs)]
use derive_more::Display;
use flames::FlameTimed;
use fmt::*;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use tracing::Subscriber;
use tracing_subscriber::{
filter::EnvFilter,
fmt::{
format::{DefaultFields, FmtSpan, Format},
time::UtcTime,
},
layer::SubscriberExt,
registry::LookupSpan,
util::SubscriberInitExt,
Layer, Registry,
};
mod flames;
mod fmt;
pub mod metrics;
mod writer;
mod open;
use crate::writer::InMemoryWriter;
pub use open::{Config, Context, MsgWrap, OpenSpanExt};
pub use tracing;
use tracing_subscriber::fmt::MakeWriter;
#[derive(Debug, Clone, Display)]
pub enum Output {
Compact,
Json,
JsonTimed,
Log,
LogTimed,
FlameTimed,
IceTimed,
None,
}
pub type ParseError = String;
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),
"None" => Ok(Output::None),
_ => Err("Could not parse log output type".into()),
}
}
}
pub fn test_run() {
if std::env::var_os("RUST_LOG").is_none() {
return;
}
static INIT_ONCE: std::sync::Once = std::sync::Once::new();
INIT_ONCE.call_once(|| {
init_fmt(Output::Log).unwrap();
});
}
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() -> Result<Option<Box<impl Drop>>, errors::TracingError> {
if std::env::var_os("RUST_LOG").is_none() {
return Ok(None);
}
let buffer = Arc::new(Mutex::new(Vec::new()));
let writer_handle = InMemoryWriter::new(buffer.clone());
init_fmt_with_opts(Output::FlameTimed, move || {
InMemoryWriter::new(buffer.clone())
})?;
Ok(Some(Box::new(FlameTimed::new(writer_handle))))
}
pub fn test_run_timed_ice() -> Result<Option<Box<impl Drop>>, errors::TracingError> {
if std::env::var_os("RUST_LOG").is_none() {
return Ok(None);
}
let buffer = Arc::new(Mutex::new(Vec::new()));
let writer_handle = InMemoryWriter::new(buffer.clone());
init_fmt_with_opts(Output::IceTimed, move || {
InMemoryWriter::new(buffer.clone())
})?;
Ok(Some(Box::new(FlameTimed::new(writer_handle))))
}
pub fn standard_filter() -> Result<EnvFilter, errors::TracingError> {
let mut filter = 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();
}
Ok(filter)
}
pub fn standard_layer_unfiltered<W, S>(
writer: W,
) -> Result<tracing_subscriber::fmt::Layer<S, DefaultFields, Format, W>, errors::TracingError>
where
W: for<'w> MakeWriter<'w> + Send + Sync + 'static,
S: Subscriber + Send + Sync + for<'span> LookupSpan<'span>,
{
Ok(tracing_subscriber::fmt::Layer::default()
.with_test_writer()
.with_writer(writer)
.with_file(true)
.with_line_number(true)
.with_target(true))
}
pub fn standard_layer<W, S>(writer: W) -> Result<impl Layer<S>, errors::TracingError>
where
W: for<'w> MakeWriter<'w> + Send + Sync + 'static,
S: Subscriber + Send + Sync + for<'span> LookupSpan<'span>,
{
let filter = standard_filter()?;
Ok(standard_layer_unfiltered(writer)?.with_filter(filter))
}
pub fn init_fmt(output: Output) -> Result<(), errors::TracingError> {
init_fmt_with_opts(output, std::io::stderr)
}
fn init_fmt_with_opts<W>(output: Output, writer: W) -> Result<(), errors::TracingError>
where
W: for<'writer> MakeWriter<'writer> + Send + Sync + 'static,
{
let filter = standard_filter()?;
println!("Initialising log output formatting with option {output:?}");
match output {
Output::Json => Registry::default()
.with(
standard_layer_unfiltered(writer)?
.with_timer(UtcTime::rfc_3339())
.json()
.event_format(FormatEvent)
.with_filter(filter),
)
.init(),
Output::JsonTimed => Registry::default()
.with(
standard_layer_unfiltered(writer)?
.with_span_events(FmtSpan::CLOSE)
.with_timer(UtcTime::rfc_3339())
.json()
.event_format(FormatEvent)
.with_filter(filter),
)
.init(),
Output::Log => Registry::default().with(standard_layer(writer)?).init(),
Output::LogTimed => Registry::default()
.with(
standard_layer_unfiltered(writer)?
.with_span_events(FmtSpan::FULL)
.with_filter(filter),
)
.init(),
Output::FlameTimed => Registry::default()
.with(
standard_layer_unfiltered(writer)?
.with_span_events(FmtSpan::CLOSE)
.with_timer(UtcTime::rfc_3339())
.event_format(FormatEventFlame)
.with_filter(filter),
)
.init(),
Output::IceTimed => Registry::default()
.with(
standard_layer_unfiltered(writer)?
.with_span_events(FmtSpan::CLOSE)
.with_timer(UtcTime::rfc_3339())
.event_format(FormatEventIce)
.with_filter(filter),
)
.init(),
Output::Compact => Registry::default()
.with(
standard_layer_unfiltered(writer)?
.compact()
.with_filter(filter),
)
.init(),
Output::None => (),
};
Ok(())
}
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),
}
}