diff --git a/Cargo.toml b/Cargo.toml index f384b92553..17e6c31b9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -209,7 +209,7 @@ zstd = "=0.12.4" opentelemetry = "0.27.0" opentelemetry-http = "0.27.0" -opentelemetry-otlp = { version = "0.27.0", features = ["logs", "http-proto", "http-json"] } +opentelemetry-otlp = { version = "0.27.0", features = ["logs", "http-proto", "http-json", "populate-logs-event-name"] } opentelemetry-semantic-conventions = { version = "0.27.0", features = ["semconv_experimental"] } opentelemetry_sdk = "0.27.0" diff --git a/cli/main.rs b/cli/main.rs index 20d2cb6bff..f120f0ec4d 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -431,20 +431,21 @@ fn resolve_flags_and_init( if err.kind() == clap::error::ErrorKind::DisplayVersion => { // Ignore results to avoid BrokenPipe errors. - util::logger::init(None); + util::logger::init(None, None); let _ = err.print(); deno_runtime::exit(0); } Err(err) => { - util::logger::init(None); + util::logger::init(None, None); exit_for_error(AnyError::from(err)) } }; - if let Some(otel_config) = flags.otel_config() { + let otel_config = flags.otel_config(); + util::logger::init(flags.log_level, otel_config.as_ref()); + if let Some(otel_config) = otel_config { deno_runtime::ops::otel::init(otel_config)?; } - util::logger::init(flags.log_level); // TODO(bartlomieju): remove in Deno v2.5 and hard error then. if flags.unstable_config.legacy_flag_enabled { diff --git a/cli/mainrt.rs b/cli/mainrt.rs index 2951aa711a..3510c07c69 100644 --- a/cli/mainrt.rs +++ b/cli/mainrt.rs @@ -87,17 +87,20 @@ fn main() { let future = async move { match standalone { Ok(Some(data)) => { + util::logger::init( + data.metadata.log_level, + data.metadata.otel_config.as_ref(), + ); if let Some(otel_config) = data.metadata.otel_config.clone() { deno_runtime::ops::otel::init(otel_config)?; } - util::logger::init(data.metadata.log_level); load_env_vars(&data.metadata.env_vars_from_env_file); let exit_code = standalone::run(data).await?; deno_runtime::exit(exit_code); } Ok(None) => Ok(()), Err(err) => { - util::logger::init(None); + util::logger::init(None, None); Err(err) } } diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs index bebb3f5881..8fe9ae442e 100644 --- a/cli/tools/run/mod.rs +++ b/cli/tools/run/mod.rs @@ -77,9 +77,14 @@ pub async fn run_script( let worker_factory = factory.create_cli_main_worker_factory().await?; let mut worker = worker_factory .create_main_worker(mode, main_module.clone()) - .await?; + .await + .inspect_err(|e| { + deno_runtime::ops::otel::report_event("deno_boot_failure", e) + })?; - let exit_code = worker.run().await?; + let exit_code = worker.run().await.inspect_err(|e| { + deno_runtime::ops::otel::report_event("deno_uncaught_exception", e) + })?; Ok(exit_code) } diff --git a/cli/util/logger.rs b/cli/util/logger.rs index f76663df2c..8021db46e6 100644 --- a/cli/util/logger.rs +++ b/cli/util/logger.rs @@ -3,22 +3,33 @@ use std::io::Write; use super::draw_thread::DrawThread; +use deno_runtime::ops::otel::OtelConfig; +use deno_runtime::ops::otel::OtelConsoleConfig; -struct CliLogger(env_logger::Logger); +struct CliLogger { + logger: env_logger::Logger, + otel_console_config: OtelConsoleConfig, +} impl CliLogger { - pub fn new(logger: env_logger::Logger) -> Self { - Self(logger) + pub fn new( + logger: env_logger::Logger, + otel_console_config: OtelConsoleConfig, + ) -> Self { + Self { + logger, + otel_console_config, + } } pub fn filter(&self) -> log::LevelFilter { - self.0.filter() + self.logger.filter() } } impl log::Log for CliLogger { fn enabled(&self, metadata: &log::Metadata) -> bool { - self.0.enabled(metadata) + self.logger.enabled(metadata) } fn log(&self, record: &log::Record) { @@ -28,18 +39,28 @@ impl log::Log for CliLogger { // could potentially block other threads that access the draw // thread's state DrawThread::hide(); - self.0.log(record); - deno_runtime::ops::otel::handle_log(record); + match self.otel_console_config { + OtelConsoleConfig::Ignore => { + self.logger.log(record); + } + OtelConsoleConfig::Capture => { + self.logger.log(record); + deno_runtime::ops::otel::handle_log(record); + } + OtelConsoleConfig::Replace => { + deno_runtime::ops::otel::handle_log(record); + } + } DrawThread::show(); } } fn flush(&self) { - self.0.flush(); + self.logger.flush(); } } -pub fn init(maybe_level: Option) { +pub fn init(maybe_level: Option, otel_config: Option<&OtelConfig>) { let log_level = maybe_level.unwrap_or(log::Level::Info); let logger = env_logger::Builder::from_env( env_logger::Env::new() @@ -93,7 +114,12 @@ pub fn init(maybe_level: Option) { }) .build(); - let cli_logger = CliLogger::new(logger); + let cli_logger = CliLogger::new( + logger, + otel_config + .map(|c| c.console) + .unwrap_or(OtelConsoleConfig::Ignore), + ); let max_level = cli_logger.filter(); let r = log::set_boxed_logger(Box::new(cli_logger)); if r.is_ok() { diff --git a/runtime/ops/otel.rs b/runtime/ops/otel.rs index b32764d7f8..5e11b7c75a 100644 --- a/runtime/ops/otel.rs +++ b/runtime/ops/otel.rs @@ -403,6 +403,25 @@ pub fn flush() { } } +pub fn report_event(name: &'static str, data: impl std::fmt::Display) { + let Some((_, log_processor)) = OTEL_PROCESSORS.get() else { + return; + }; + + let mut log_record = LogRecord::default(); + + log_record.set_observed_timestamp(SystemTime::now()); + log_record.set_event_name(name); + log_record.set_severity_number(Severity::Trace); + log_record.set_severity_text(Severity::Trace.name()); + log_record.set_body(format!("{data}").into()); + + log_processor.emit( + &mut log_record, + &InstrumentationScope::builder("deno").build(), + ); +} + pub fn handle_log(record: &log::Record) { use log::Level;