use std::fmt;
use std::panic::Location;
use std::panic::PanicInfo;
use backtrace::Backtrace;
use clap::ValueEnum;
use tracing::Level;
use tracing_log::LogTracer;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Registry;
#[cfg(feature = "browser")]
pub use self::browser::init_logging;
#[cfg(feature = "node")]
pub use self::node::init_logging;
#[derive(ValueEnum, Debug, Clone)]
pub enum LogLevel {
Debug,
Info,
Warn,
Error,
Trace,
}
impl From<LogLevel> for Level {
fn from(val: LogLevel) -> Self {
match val {
LogLevel::Trace => Level::TRACE,
LogLevel::Debug => Level::DEBUG,
LogLevel::Info => Level::INFO,
LogLevel::Warn => Level::WARN,
LogLevel::Error => Level::ERROR,
}
}
}
#[derive(Debug, Clone)]
pub struct PanicLocation {
file: String,
line: String,
column: String,
}
impl<'a, T> From<T> for PanicLocation
where T: Into<Location<'a>>
{
fn from(lo: T) -> Self {
let lo: Location = lo.into();
Self {
file: lo.file().to_string(),
line: lo.line().to_string(),
column: lo.file().to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct PanicData<'a> {
message: &'a PanicInfo<'a>,
backtrace: String,
location: Option<PanicLocation>,
}
impl<'a, T> From<T> for PanicData<'a>
where T: Into<&'a PanicInfo<'a>>
{
fn from(panic: T) -> PanicData<'a> {
let panic = panic.into();
let backtrace = Backtrace::new();
let backtrace = format!("{:?}", backtrace);
let location: Option<PanicLocation> = panic.location().map(|l| PanicLocation::from(*l));
PanicData {
message: panic,
backtrace,
location,
}
}
}
impl fmt::Display for PanicLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}:{}", self.file, self.line, self.column)
}
}
impl<'a> fmt::Display for PanicData<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.location {
Some(l) => write!(f, "{}, {} \n\n {}", self.message, l, self.backtrace),
None => write!(f, "{} \n\n {}", self.message, self.backtrace),
}
}
}
fn log_panic(panic: &PanicInfo) {
let data: PanicData = panic.into();
tracing::error!("{}", data)
}
pub fn set_panic_hook() {
std::panic::set_hook(Box::new(|panic| {
log_panic(panic);
}));
}
#[cfg(feature = "node")]
pub mod node {
use opentelemetry::global;
use opentelemetry::sdk::propagation::TraceContextPropagator;
use tracing_subscriber::filter;
use tracing_subscriber::fmt;
use tracing_subscriber::Layer;
use super::*;
pub fn init_logging(level: Level) {
set_panic_hook();
let subscriber = Registry::default();
let level_filter = filter::LevelFilter::from_level(level);
let mdns_log_filter = filter::FilterFn::new(|metadata| {
!metadata.target().starts_with("webrtc_mdns::conn")
|| [276, 322]
.iter()
.all(|&line| !metadata.line().unwrap_or_default() == line)
});
let subscriber = subscriber.with(
fmt::layer()
.with_writer(std::io::stderr)
.with_filter(level_filter)
.with_filter(mdns_log_filter.clone()),
);
let subscriber = {
if let Ok(endpoint) = std::env::var("RINGS_JAEGER_AGENT_ENDPOINT") {
global::set_text_map_propagator(TraceContextPropagator::new());
let jaeger = opentelemetry_jaeger::new_agent_pipeline()
.with_service_name("rings")
.with_endpoint(endpoint)
.with_auto_split_batch(true)
.install_batch(opentelemetry::runtime::Tokio)
.expect("opentelemetry_jaeger install");
subscriber.with(Some(
tracing_opentelemetry::layer()
.with_tracer(jaeger)
.with_filter(level_filter)
.with_filter(mdns_log_filter),
))
} else {
subscriber.with(None)
}
};
let _ = LogTracer::init();
let _ = tracing::subscriber::set_global_default(subscriber);
}
}
#[cfg(feature = "browser")]
pub mod browser {
use tracing_wasm::ConsoleConfig;
use tracing_wasm::WASMLayer;
use tracing_wasm::WASMLayerConfigBuilder;
use super::*;
pub fn init_logging(level: Level) {
set_panic_hook();
let subscriber = Registry::default();
let subscriber = subscriber.with(WASMLayer::new(
WASMLayerConfigBuilder::new()
.set_max_level(level)
.set_console_config(ConsoleConfig::ReportWithoutConsoleColor)
.build(),
));
let _ = LogTracer::init();
let _ = tracing::subscriber::set_global_default(subscriber);
}
}