Skip to content

Commit

Permalink
refactor(unstable): otel configuration (#27333)
Browse files Browse the repository at this point in the history
split up otel config into user configurable and runtime configurable
parts. user configurable part is now set via env vars parsed according
to the otel spec. otel is now enabled via `OTEL_DENO=true`, and
`--unstable-otel` only acts as a guard.

Fixes: #27273
  • Loading branch information
devsnek authored Dec 12, 2024
1 parent c6fa628 commit 5f8be05
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 65 deletions.
47 changes: 34 additions & 13 deletions cli/args/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use deno_path_util::url_to_file_path;
use deno_runtime::deno_permissions::PermissionsOptions;
use deno_runtime::deno_permissions::SysDescriptor;
use deno_telemetry::OtelConfig;
use deno_telemetry::OtelConsoleConfig;
use log::debug;
use log::Level;
use serde::Deserialize;
Expand Down Expand Up @@ -986,21 +987,41 @@ impl Flags {
args
}

pub fn otel_config(&self) -> Option<OtelConfig> {
if self
pub fn otel_config(&self) -> OtelConfig {
let has_unstable_flag = self
.unstable_config
.features
.contains(&String::from("otel"))
{
Some(OtelConfig {
runtime_name: Cow::Borrowed("deno"),
runtime_version: Cow::Borrowed(crate::version::DENO_VERSION_INFO.deno),
deterministic: std::env::var("DENO_UNSTABLE_OTEL_DETERMINISTIC")
.is_ok(),
..Default::default()
})
} else {
None
.contains(&String::from("otel"));

let otel_var = |name| match std::env::var(name) {
Ok(s) if s.to_lowercase() == "true" => Some(true),
Ok(s) if s.to_lowercase() == "false" => Some(false),
_ => None,
};

let disabled =
!has_unstable_flag || otel_var("OTEL_SDK_DISABLED").unwrap_or(false);
let default = !disabled && otel_var("OTEL_DENO").unwrap_or(false);

OtelConfig {
tracing_enabled: !disabled
&& otel_var("OTEL_DENO_TRACING").unwrap_or(default),
console: match std::env::var("OTEL_DENO_CONSOLE").as_deref() {
Ok(_) if disabled => OtelConsoleConfig::Ignore,
Ok("ignore") => OtelConsoleConfig::Ignore,
Ok("capture") => OtelConsoleConfig::Capture,
Ok("replace") => OtelConsoleConfig::Replace,
_ => {
if default {
OtelConsoleConfig::Capture
} else {
OtelConsoleConfig::Ignore
}
}
},
deterministic: std::env::var("DENO_UNSTABLE_OTEL_DETERMINISTIC")
.as_deref()
== Ok("1"),
}
}

Expand Down
10 changes: 9 additions & 1 deletion cli/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use deno_npm_cache::NpmCacheSetting;
use deno_path_util::normalize_path;
use deno_semver::npm::NpmPackageReqReference;
use deno_telemetry::OtelConfig;
use deno_telemetry::OtelRuntimeConfig;
use import_map::resolve_import_map_value_from_specifier;

pub use deno_config::deno_json::BenchConfig;
Expand Down Expand Up @@ -1130,7 +1131,7 @@ impl CliOptions {
}
}

pub fn otel_config(&self) -> Option<OtelConfig> {
pub fn otel_config(&self) -> OtelConfig {
self.flags.otel_config()
}

Expand Down Expand Up @@ -2000,6 +2001,13 @@ pub enum NpmCachingStrategy {
Manual,
}

pub(crate) fn otel_runtime_config() -> OtelRuntimeConfig {
OtelRuntimeConfig {
runtime_name: Cow::Borrowed("deno"),
runtime_version: Cow::Borrowed(crate::version::DENO_VERSION_INFO.deno),
}
}

#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
Expand Down
10 changes: 4 additions & 6 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,20 +437,18 @@ 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() {
deno_telemetry::init(otel_config)?;
}
util::logger::init(flags.log_level);
deno_telemetry::init(crate::args::otel_runtime_config())?;
util::logger::init(flags.log_level, Some(flags.otel_config()));

// TODO(bartlomieju): remove in Deno v2.5 and hard error then.
if flags.unstable_config.legacy_flag_enabled {
Expand Down
11 changes: 6 additions & 5 deletions cli/mainrt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,18 @@ fn main() {
let future = async move {
match standalone {
Ok(Some(data)) => {
if let Some(otel_config) = data.metadata.otel_config.clone() {
deno_telemetry::init(otel_config)?;
}
util::logger::init(data.metadata.log_level);
deno_telemetry::init(crate::args::otel_runtime_config())?;
util::logger::init(
data.metadata.log_level,
Some(data.metadata.otel_config.clone()),
);
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)
}
}
Expand Down
2 changes: 1 addition & 1 deletion cli/standalone/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ pub struct Metadata {
pub entrypoint_key: String,
pub node_modules: Option<NodeModules>,
pub unstable_config: UnstableConfig,
pub otel_config: Option<OtelConfig>, // None means disabled.
pub otel_config: OtelConfig,
}

fn write_binary_bytes(
Expand Down
51 changes: 39 additions & 12 deletions cli/util/logger.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use std::io::Write;

use super::draw_thread::DrawThread;
use deno_telemetry::OtelConfig;
use deno_telemetry::OtelConsoleConfig;
use std::io::Write;

struct CliLogger(env_logger::Logger);
struct CliLogger {
otel_console_config: OtelConsoleConfig,
logger: env_logger::Logger,
}

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) {
Expand All @@ -28,18 +38,30 @@ impl log::Log for CliLogger {
// could potentially block other threads that access the draw
// thread's state
DrawThread::hide();
self.0.log(record);
deno_telemetry::handle_log(record);

match self.otel_console_config {
OtelConsoleConfig::Ignore => {
self.logger.log(record);
}
OtelConsoleConfig::Capture => {
self.logger.log(record);
deno_telemetry::handle_log(record);
}
OtelConsoleConfig::Replace => {
deno_telemetry::handle_log(record);
}
}

DrawThread::show();
}
}

fn flush(&self) {
self.0.flush();
self.logger.flush();
}
}

pub fn init(maybe_level: Option<log::Level>) {
pub fn init(maybe_level: Option<log::Level>, 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()
Expand Down Expand Up @@ -93,7 +115,12 @@ pub fn init(maybe_level: Option<log::Level>) {
})
.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() {
Expand Down
4 changes: 2 additions & 2 deletions cli/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ struct SharedWorkerState {
storage_key_resolver: StorageKeyResolver,
options: CliMainWorkerOptions,
subcommand: DenoSubcommand,
otel_config: Option<OtelConfig>, // `None` means OpenTelemetry is disabled.
otel_config: OtelConfig,
default_npm_caching_strategy: NpmCachingStrategy,
}

Expand Down Expand Up @@ -426,7 +426,7 @@ impl CliMainWorkerFactory {
storage_key_resolver: StorageKeyResolver,
subcommand: DenoSubcommand,
options: CliMainWorkerOptions,
otel_config: Option<OtelConfig>,
otel_config: OtelConfig,
default_npm_caching_strategy: NpmCachingStrategy,
) -> Self {
Self {
Expand Down
32 changes: 20 additions & 12 deletions ext/telemetry/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,28 @@ deno_core::extension!(
);

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OtelConfig {
pub struct OtelRuntimeConfig {
pub runtime_name: Cow<'static, str>,
pub runtime_version: Cow<'static, str>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct OtelConfig {
pub tracing_enabled: bool,
pub console: OtelConsoleConfig,
pub deterministic: bool,
}

impl OtelConfig {
pub fn as_v8(&self) -> Box<[u8]> {
Box::new([
self.tracing_enabled as u8,
self.console as u8,
self.deterministic as u8,
])
}
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[repr(u8)]
pub enum OtelConsoleConfig {
Expand All @@ -112,14 +127,9 @@ pub enum OtelConsoleConfig {
Replace = 2,
}

impl Default for OtelConfig {
impl Default for OtelConsoleConfig {
fn default() -> Self {
Self {
runtime_name: Cow::Borrowed(env!("CARGO_PKG_NAME")),
runtime_version: Cow::Borrowed(env!("CARGO_PKG_VERSION")),
console: OtelConsoleConfig::Capture,
deterministic: false,
}
Self::Ignore
}
}

Expand Down Expand Up @@ -411,16 +421,14 @@ static BUILT_IN_INSTRUMENTATION_SCOPE: OnceCell<
opentelemetry::InstrumentationScope,
> = OnceCell::new();

pub fn init(config: OtelConfig) -> anyhow::Result<()> {
pub fn init(config: OtelRuntimeConfig) -> anyhow::Result<()> {
// Parse the `OTEL_EXPORTER_OTLP_PROTOCOL` variable. The opentelemetry_*
// crates don't do this automatically.
// TODO(piscisaureus): enable GRPC support.
let protocol = match env::var("OTEL_EXPORTER_OTLP_PROTOCOL").as_deref() {
Ok("http/protobuf") => Protocol::HttpBinary,
Ok("http/json") => Protocol::HttpJson,
Ok("") | Err(env::VarError::NotPresent) => {
return Ok(());
}
Ok("") | Err(env::VarError::NotPresent) => Protocol::HttpBinary,
Ok(protocol) => {
return Err(anyhow!(
"Env var OTEL_EXPORTER_OTLP_PROTOCOL specifies an unsupported protocol: {}",
Expand Down
10 changes: 5 additions & 5 deletions ext/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -950,15 +950,15 @@ const otelConsoleConfig = {
};

export function bootstrap(
config: [] | [
config: [
0 | 1,
typeof otelConsoleConfig[keyof typeof otelConsoleConfig],
number,
0 | 1,
],
): void {
if (config.length === 0) return;
const { 0: consoleConfig, 1: deterministic } = config;
const { 0: tracingEnabled, 1: consoleConfig, 2: deterministic } = config;

TRACING_ENABLED = true;
TRACING_ENABLED = tracingEnabled === 1;
DETERMINISTIC = deterministic === 1;

switch (consoleConfig) {
Expand Down
11 changes: 3 additions & 8 deletions runtime/worker_bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ pub struct BootstrapOptions {
// Used by `deno serve`
pub serve_port: Option<u16>,
pub serve_host: Option<String>,
// OpenTelemetry output options. If `None`, OpenTelemetry is disabled.
pub otel_config: Option<OtelConfig>,
pub otel_config: OtelConfig,
}

impl Default for BootstrapOptions {
Expand Down Expand Up @@ -155,7 +154,7 @@ impl Default for BootstrapOptions {
mode: WorkerExecutionMode::None,
serve_port: Default::default(),
serve_host: Default::default(),
otel_config: None,
otel_config: Default::default(),
}
}
}
Expand Down Expand Up @@ -225,11 +224,7 @@ impl BootstrapOptions {
self.serve_host.as_deref(),
serve_is_main,
serve_worker_count,
if let Some(otel_config) = self.otel_config.as_ref() {
Box::new([otel_config.console as u8, otel_config.deterministic as u8])
} else {
Box::new([])
},
self.otel_config.as_v8(),
);

bootstrap.serialize(ser).unwrap()
Expand Down
1 change: 1 addition & 0 deletions tests/specs/cli/otel_basic/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const server = Deno.serve(
const command = new Deno.Command(Deno.execPath(), {
args: ["run", "-A", "-q", "--unstable-otel", Deno.args[0]],
env: {
OTEL_DENO: "true",
DENO_UNSTABLE_OTEL_DETERMINISTIC: "1",
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
OTEL_EXPORTER_OTLP_ENDPOINT: `http://localhost:${port}`,
Expand Down

0 comments on commit 5f8be05

Please sign in to comment.