1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-17 21:03:01 -05:00

refactor(unstable): otel configuration (#27333)

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: https://github.com/denoland/deno/issues/27273
This commit is contained in:
snek 2024-12-12 09:17:26 +01:00 committed by GitHub
parent c6fa62896d
commit 5f8be055db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 124 additions and 65 deletions

View file

@ -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;
@ -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"),
}
}

View file

@ -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;
@ -1130,7 +1131,7 @@ impl CliOptions {
}
}
pub fn otel_config(&self) -> Option<OtelConfig> {
pub fn otel_config(&self) -> OtelConfig {
self.flags.otel_config()
}
@ -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;

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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(

View file

@ -1,24 +1,34 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use super::draw_thread::DrawThread;
use deno_telemetry::OtelConfig;
use deno_telemetry::OtelConsoleConfig;
use std::io::Write;
use super::draw_thread::DrawThread;
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) {
@ -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()
@ -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() {

View file

@ -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,
}
@ -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 {

View file

@ -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 {
@ -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
}
}
@ -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: {}",

View file

@ -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) {

View file

@ -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 {
@ -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(),
}
}
}
@ -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()

View file

@ -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}`,