From ec0cd94f3476759dafe6a34870a76e8d052dfb1c Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Sat, 7 Dec 2024 15:21:23 -0800 Subject: [PATCH 1/5] OTLP Example fixes --- .../examples/basic-otlp-http/Cargo.toml | 2 +- .../examples/basic-otlp-http/README.md | 23 ++++-- .../examples/basic-otlp-http/src/main.rs | 71 +++++++++---------- .../examples/basic-otlp/Cargo.toml | 2 +- .../examples/basic-otlp/README.md | 47 ++++++++++-- .../examples/basic-otlp/src/main.rs | 68 +++++++++--------- 6 files changed, 127 insertions(+), 86 deletions(-) diff --git a/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml b/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml index 89df249f2d..8e640ee1c9 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml +++ b/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml @@ -27,4 +27,4 @@ http-body-util = { workspace = true, optional = true } tokio = { workspace = true, features = ["full"] } tracing = { workspace = true, features = ["std"]} tracing-core = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter","registry", "std"] } +tracing-subscriber = { workspace = true, features = ["env-filter","registry", "std", "fmt"] } diff --git a/opentelemetry-otlp/examples/basic-otlp-http/README.md b/opentelemetry-otlp/examples/basic-otlp-http/README.md index d70a5534a0..19b646f60c 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/README.md +++ b/opentelemetry-otlp/examples/basic-otlp-http/README.md @@ -1,10 +1,23 @@ -# Basic OTLP exporter Example +# Basic OTLP Exporter Example -This example shows how to setup OpenTelemetry OTLP exporter for logs, metrics -and traces to export them to the [OpenTelemetry +This example demonstrates how to set up an OpenTelemetry OTLP exporter for logs, +metrics, and traces to send data to the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector) via OTLP -over selected protocol such as HTTP/protobuf or HTTP/json. The Collector then sends the data to the appropriate -backend, in this case, the logging Exporter, which displays data to console. +over HTTP (using `protobuf` encoding by default but can be changed to use +`json`). The Collector then forwards the data to the configured backend, which +in this case is the logging exporter, displaying data on the console. +Additionally, the example configures a `tracing::fmt` layer to output logs +emitted via `tracing` to `stdout`. For demonstration, this layer uses a filter +to display `DEBUG` level logs from various OpenTelemetry components. In real +applications, these filters should be adjusted appropriately. + +The example employs a `BatchExporter` for logs and traces, which is the +recommended approach when using OTLP exporters. While it can be modified to use +a `SimpleExporter`, this requires enabling feature flag `reqwest-blocking-client` and +making the `main()` a normal main and *not* `tokio::main` + +// TODO: Metrics does not work with non tokio main when using `reqwest-blocking-client` today, fix that when switching +// default to use own thread. ## Usage diff --git a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs index bba642e657..bf05f0e8e0 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs +++ b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs @@ -71,51 +71,48 @@ fn init_metrics() -> Result Result<(), Box> { - let result = init_tracer_provider(); - assert!( - result.is_ok(), - "Init tracer failed with error: {:?}", - result.err() - ); - - let tracer_provider = result.unwrap(); + let tracer_provider = init_tracer_provider()?; global::set_tracer_provider(tracer_provider.clone()); - let result = init_metrics(); - assert!( - result.is_ok(), - "Init metrics failed with error: {:?}", - result.err() - ); - - let meter_provider = result.unwrap(); + let meter_provider = init_metrics()?; global::set_meter_provider(meter_provider.clone()); - // Opentelemetry will not provide a global API to manage the logger - // provider. Application users must manage the lifecycle of the logger - // provider on their own. Dropping logger providers will disable log - // emitting. - let logger_provider = init_logs().unwrap(); + let logger_provider = init_logs()?; // Create a new OpenTelemetryTracingBridge using the above LoggerProvider. - let layer = OpenTelemetryTracingBridge::new(&logger_provider); + let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider); - // Add a tracing filter to filter events from crates used by opentelemetry-otlp. - // The filter levels are set as follows: + // For the OpenTelemetry layer, add a tracing filter to filter events from + // OpenTelemetry and its dependent crates (opentelemetry-otlp uses crates + // like reqwest/tonic etc.) from being sent back to OTel itself, thus + // preventing infinite telemetry generation. The filter levels are set as + // follows: // - Allow `info` level and above by default. - // - Restrict `hyper`, `tonic`, and `reqwest` to `error` level logs only. - // This ensures events generated from these crates within the OTLP Exporter are not looped back, - // thus preventing infinite event generation. - // Note: This will also drop events from these crates used outside the OTLP Exporter. - // For more details, see: https://github.com/open-telemetry/opentelemetry-rust/issues/761 - let filter = EnvFilter::new("info") - .add_directive("hyper=error".parse().unwrap()) - .add_directive("tonic=error".parse().unwrap()) - .add_directive("reqwest=error".parse().unwrap()); - + // - Restrict `opentelemetry`, `hyper`, `tonic`, and `reqwest` completely. + // Note: This will also drop events from crates like `tonic` etc. even when + // they are used outside the OTLP Exporter. For more details, see: + // https://github.com/open-telemetry/opentelemetry-rust/issues/761 + let filter_otel = EnvFilter::new("info") + .add_directive("hyper=off".parse().unwrap()) + .add_directive("opentelemetry=off".parse().unwrap()) + .add_directive("tonic=off".parse().unwrap()) + .add_directive("h2=off".parse().unwrap()) + .add_directive("reqwest=off".parse().unwrap()); + let otel_layer = otel_layer.with_filter(filter_otel); + + // Create a new tracing::Fmt layer to print the logs to stdout. It has a + // default filter of `info` level and above, and `debug` and above for logs + // from OpenTelemtry crates. The filter levels can be customized as needed. + let filter_fmt = EnvFilter::new("info").add_directive("opentelemetry=debug".parse().unwrap()); + let fmt_layer = tracing_subscriber::fmt::layer() + .with_thread_names(true) + .with_filter(filter_fmt); + + // Initialize the tracing subscriber with the OpenTelemetry layer and the + // Fmt layer. tracing_subscriber::registry() - .with(filter) - .with(layer) + .with(otel_layer) + .with(fmt_layer) .init(); let common_scope_attributes = vec![KeyValue::new("scope-key", "scope-value")]; @@ -158,7 +155,7 @@ async fn main() -> Result<(), Box> { tracer_provider.shutdown()?; logger_provider.shutdown()?; - meter_provider.shutdown()?; + // meter_provider.shutdown()?; Ok(()) } diff --git a/opentelemetry-otlp/examples/basic-otlp/Cargo.toml b/opentelemetry-otlp/examples/basic-otlp/Cargo.toml index cbb9efabc5..3f6e00fa7c 100644 --- a/opentelemetry-otlp/examples/basic-otlp/Cargo.toml +++ b/opentelemetry-otlp/examples/basic-otlp/Cargo.toml @@ -15,4 +15,4 @@ tokio = { version = "1.0", features = ["full"] } opentelemetry-appender-tracing = { path = "../../../opentelemetry-appender-tracing", default-features = false} tracing = { workspace = true, features = ["std"]} tracing-core = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter","registry", "std"] } +tracing-subscriber = { workspace = true, features = ["env-filter","registry", "std", "fmt"] } \ No newline at end of file diff --git a/opentelemetry-otlp/examples/basic-otlp/README.md b/opentelemetry-otlp/examples/basic-otlp/README.md index 003f9f2be4..928c36159f 100644 --- a/opentelemetry-otlp/examples/basic-otlp/README.md +++ b/opentelemetry-otlp/examples/basic-otlp/README.md @@ -1,10 +1,43 @@ -# Basic OTLP exporter Example - -This example shows how to setup OpenTelemetry OTLP exporter for logs, metrics -and traces to exports them to the [OpenTelemetry -Collector](https://github.com/open-telemetry/opentelemetry-collector) via OTLP over gRPC. -The Collector then sends the data to the appropriate backend, in this case, -the logging Exporter, which displays data to console. +# Basic OTLP Exporter Example + +This example demonstrates how to set up an OpenTelemetry OTLP exporter for logs, +metrics, and traces to send data to the [OpenTelemetry +Collector](https://github.com/open-telemetry/opentelemetry-collector) via OTLP +over gRPC. The Collector then forwards the data to the configured backend, which +in this case is the logging exporter, displaying data on the console. +Additionally, the example configures a `tracing::fmt` layer to output logs +emitted via `tracing` to `stdout`. For demonstration, this layer uses a filter +to display `DEBUG` level logs from various OpenTelemetry components. In real +applications, these filters should be adjusted appropriately. + +The example employs a `BatchExporter` for logs and traces, which is the +recommended approach when using OTLP exporters. While it can be modified to use +a `SimpleExporter`, this requires the main method to be a `tokio::main` function +since the `tonic` client requires a Tokio runtime. If you prefer not to use +`tokio::main`, the `init_logs`, `init_traces`, and `init_metrics` functions +should be executed within a Tokio runtime. Below is an example: + +```rust +fn main() -> Result<(), Box> { + let rt = tokio::runtime::Runtime::new()?; + let tracer_provider = rt.block_on(async { + init_tracer_provider() + })?; + global::set_tracer_provider(tracer_provider.clone()); + + let meter_provider = rt.block_on(async { + init_metrics() + })?; + global::set_meter_provider(meter_provider.clone()); + + let logger_provider = rt.block_on(async { + init_logs() + })?; + + // Ensure the runtime (`rt`) remains active until the program ends + // Additional code goes here... +} +``` ## Usage diff --git a/opentelemetry-otlp/examples/basic-otlp/src/main.rs b/opentelemetry-otlp/examples/basic-otlp/src/main.rs index 77dd615b85..5b04b3b5f0 100644 --- a/opentelemetry-otlp/examples/basic-otlp/src/main.rs +++ b/opentelemetry-otlp/examples/basic-otlp/src/main.rs @@ -57,50 +57,48 @@ fn init_logs() -> Result { #[tokio::main] async fn main() -> Result<(), Box> { - // By binding the result to an unused variable, the lifetime of the variable - // matches the containing block, reporting traces and metrics during the whole - // execution. - - let result = init_tracer_provider(); - assert!( - result.is_ok(), - "Init tracer failed with error: {:?}", - result.err() - ); - let tracer_provider = result.unwrap(); + let tracer_provider = init_tracer_provider()?; global::set_tracer_provider(tracer_provider.clone()); - let result = init_metrics(); - assert!( - result.is_ok(), - "Init metrics failed with error: {:?}", - result.err() - ); - let meter_provider = result.unwrap(); + let meter_provider = init_metrics()?; global::set_meter_provider(meter_provider.clone()); - // Initialize logs and save the logger_provider. - let logger_provider = init_logs().unwrap(); + let logger_provider = init_logs()?; // Create a new OpenTelemetryTracingBridge using the above LoggerProvider. - let layer = OpenTelemetryTracingBridge::new(&logger_provider); + let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider); - // Add a tracing filter to filter events from crates used by opentelemetry-otlp. - // The filter levels are set as follows: + // For the OpenTelemetry layer, add a tracing filter to filter events from + // OpenTelemetry and its dependent crates (opentelemetry-otlp uses crates + // like reqwest/tonic etc.) from being sent back to OTel itself, thus + // preventing infinite telemetry generation. The filter levels are set as + // follows: // - Allow `info` level and above by default. - // - Restrict `hyper`, `tonic`, and `reqwest` to `error` level logs only. - // This ensures events generated from these crates within the OTLP Exporter are not looped back, - // thus preventing infinite event generation. - // Note: This will also drop events from these crates used outside the OTLP Exporter. - // For more details, see: https://github.com/open-telemetry/opentelemetry-rust/issues/761 - let filter = EnvFilter::new("info") - .add_directive("hyper=error".parse().unwrap()) - .add_directive("tonic=error".parse().unwrap()) - .add_directive("reqwest=error".parse().unwrap()); - + // - Restrict `opentelemetry`, `hyper`, `tonic`, and `reqwest` completely. + // Note: This will also drop events from crates like `tonic` etc. even when + // they are used outside the OTLP Exporter. For more details, see: + // https://github.com/open-telemetry/opentelemetry-rust/issues/761 + let filter_otel = EnvFilter::new("info") + .add_directive("hyper=off".parse().unwrap()) + .add_directive("opentelemetry=off".parse().unwrap()) + .add_directive("tonic=off".parse().unwrap()) + .add_directive("h2=off".parse().unwrap()) + .add_directive("reqwest=off".parse().unwrap()); + let otel_layer = otel_layer.with_filter(filter_otel); + + // Create a new tracing::Fmt layer to print the logs to stdout. It has a + // default filter of `info` level and above, and `debug` and above for logs + // from OpenTelemtry crates. The filter levels can be customized as needed. + let filter_fmt = EnvFilter::new("info").add_directive("opentelemetry=debug".parse().unwrap()); + let fmt_layer = tracing_subscriber::fmt::layer() + .with_thread_names(true) + .with_filter(filter_fmt); + + // Initialize the tracing subscriber with the OpenTelemetry layer and the + // Fmt layer. tracing_subscriber::registry() - .with(filter) - .with(layer) + .with(otel_layer) + .with(fmt_layer) .init(); let common_scope_attributes = vec![KeyValue::new("scope-key", "scope-value")]; From 8bdd5648a6104a5c1459e2a02c07d93342c8ad8d Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Sat, 7 Dec 2024 15:37:41 -0800 Subject: [PATCH 2/5] todo for hyper --- opentelemetry-otlp/examples/basic-otlp-http/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/opentelemetry-otlp/examples/basic-otlp-http/README.md b/opentelemetry-otlp/examples/basic-otlp-http/README.md index 19b646f60c..2299cf0afb 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/README.md +++ b/opentelemetry-otlp/examples/basic-otlp-http/README.md @@ -18,6 +18,7 @@ making the `main()` a normal main and *not* `tokio::main` // TODO: Metrics does not work with non tokio main when using `reqwest-blocking-client` today, fix that when switching // default to use own thread. +// TODO: Document `hyper` feature flag when using SimpleProcessor. ## Usage From c8ccc0f1e72e0756ae8bce34ebe3b87be104c879 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Sat, 7 Dec 2024 15:53:08 -0800 Subject: [PATCH 3/5] nits --- opentelemetry-otlp/examples/basic-otlp-http/src/main.rs | 4 ++-- opentelemetry-otlp/examples/basic-otlp/README.md | 4 ++-- opentelemetry-otlp/examples/basic-otlp/src/main.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs index bf05f0e8e0..6dd756c8b4 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs +++ b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs @@ -43,7 +43,7 @@ fn init_logs() -> Result Result { +fn init_traces() -> Result { let exporter = SpanExporter::builder() .with_http() .with_protocol(Protocol::HttpBinary) //can be changed to `Protocol::HttpJson` to export in JSON format @@ -71,7 +71,7 @@ fn init_metrics() -> Result Result<(), Box> { - let tracer_provider = init_tracer_provider()?; + let tracer_provider = init_traces()?; global::set_tracer_provider(tracer_provider.clone()); let meter_provider = init_metrics()?; diff --git a/opentelemetry-otlp/examples/basic-otlp/README.md b/opentelemetry-otlp/examples/basic-otlp/README.md index 928c36159f..239193622c 100644 --- a/opentelemetry-otlp/examples/basic-otlp/README.md +++ b/opentelemetry-otlp/examples/basic-otlp/README.md @@ -14,8 +14,8 @@ The example employs a `BatchExporter` for logs and traces, which is the recommended approach when using OTLP exporters. While it can be modified to use a `SimpleExporter`, this requires the main method to be a `tokio::main` function since the `tonic` client requires a Tokio runtime. If you prefer not to use -`tokio::main`, the `init_logs`, `init_traces`, and `init_metrics` functions -should be executed within a Tokio runtime. Below is an example: +`tokio::main`, then the `init_logs`, `init_traces`, and `init_metrics` functions +must be executed within a Tokio runtime. Below is an example: ```rust fn main() -> Result<(), Box> { diff --git a/opentelemetry-otlp/examples/basic-otlp/src/main.rs b/opentelemetry-otlp/examples/basic-otlp/src/main.rs index 5b04b3b5f0..e21d758f17 100644 --- a/opentelemetry-otlp/examples/basic-otlp/src/main.rs +++ b/opentelemetry-otlp/examples/basic-otlp/src/main.rs @@ -21,7 +21,7 @@ static RESOURCE: Lazy = Lazy::new(|| { )]) }); -fn init_tracer_provider() -> Result { +fn init_traces() -> Result { let exporter = SpanExporter::builder() .with_tonic() .with_endpoint("http://localhost:4317") @@ -57,7 +57,7 @@ fn init_logs() -> Result { #[tokio::main] async fn main() -> Result<(), Box> { - let tracer_provider = init_tracer_provider()?; + let tracer_provider = init_traces()?; global::set_tracer_provider(tracer_provider.clone()); let meter_provider = init_metrics()?; From 1ebdd0c1b11d571bc5bc0b0a10d09315d0bfe3c1 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Sat, 7 Dec 2024 23:40:24 -0800 Subject: [PATCH 4/5] nit name fix --- opentelemetry-otlp/examples/basic-otlp/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-otlp/examples/basic-otlp/README.md b/opentelemetry-otlp/examples/basic-otlp/README.md index 239193622c..66f7b0f223 100644 --- a/opentelemetry-otlp/examples/basic-otlp/README.md +++ b/opentelemetry-otlp/examples/basic-otlp/README.md @@ -21,7 +21,7 @@ must be executed within a Tokio runtime. Below is an example: fn main() -> Result<(), Box> { let rt = tokio::runtime::Runtime::new()?; let tracer_provider = rt.block_on(async { - init_tracer_provider() + init_traces() })?; global::set_tracer_provider(tracer_provider.clone()); From 7d181aa4954bc3c464b55db56de1d1f4ade92093 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 9 Dec 2024 10:06:24 -0800 Subject: [PATCH 5/5] uncomment shutdown --- opentelemetry-otlp/examples/basic-otlp-http/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs index 6dd756c8b4..3514c643f1 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs +++ b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs @@ -155,7 +155,7 @@ async fn main() -> Result<(), Box> { tracer_provider.shutdown()?; logger_provider.shutdown()?; - // meter_provider.shutdown()?; + meter_provider.shutdown()?; Ok(()) }