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:
parent
c6fa62896d
commit
5f8be055db
11 changed files with 124 additions and 65 deletions
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
10
cli/main.rs
10
cli/main.rs
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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: {}",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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}`,
|
||||
|
|
Loading…
Reference in a new issue