Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
24 changes: 19 additions & 5 deletions opentelemetry-otlp/examples/basic-otlp-http/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also mention using reqwest client by default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omitted it intentionally, as I expect request-blocking will be the default when we make BatchProcessor with thread as default in sdk. Will make the changes as sdk changes are done.

`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.
// TODO: Document `hyper` feature flag when using SimpleProcessor.

## Usage

Expand Down
71 changes: 34 additions & 37 deletions opentelemetry-otlp/examples/basic-otlp-http/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn init_logs() -> Result<sdklogs::LoggerProvider, opentelemetry_sdk::logs::LogEr
.build())
}

fn init_tracer_provider() -> Result<sdktrace::TracerProvider, TraceError> {
fn init_traces() -> Result<sdktrace::TracerProvider, TraceError> {
let exporter = SpanExporter::builder()
.with_http()
.with_protocol(Protocol::HttpBinary) //can be changed to `Protocol::HttpJson` to export in JSON format
Expand Down Expand Up @@ -71,51 +71,48 @@ fn init_metrics() -> Result<opentelemetry_sdk::metrics::SdkMeterProvider, Metric

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
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_traces()?;
global::set_tracer_provider(tracer_provider.clone());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be better to set the global provider within the init_traces and init_metrics method itself. That could better convey to the users that setting global providers is required as part of the initialization setup.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to show explicitly that it is cheap and okay to clone the provider here.
Maybe setup another help create_provider, which is invoked from init_traces so we can cover your recommendation, and also show the cloning aspect..?


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")];
Expand Down
2 changes: 1 addition & 1 deletion opentelemetry-otlp/examples/basic-otlp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
47 changes: 40 additions & 7 deletions opentelemetry-otlp/examples/basic-otlp/README.md
Original file line number Diff line number Diff line change
@@ -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`, 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<dyn Error + Send + Sync + 'static>> {
let rt = tokio::runtime::Runtime::new()?;
let tracer_provider = rt.block_on(async {
init_traces()
})?;
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

Expand Down
70 changes: 34 additions & 36 deletions opentelemetry-otlp/examples/basic-otlp/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static RESOURCE: Lazy<Resource> = Lazy::new(|| {
)])
});

fn init_tracer_provider() -> Result<sdktrace::TracerProvider, TraceError> {
fn init_traces() -> Result<sdktrace::TracerProvider, TraceError> {
let exporter = SpanExporter::builder()
.with_tonic()
.with_endpoint("http://localhost:4317")
Expand Down Expand Up @@ -57,50 +57,48 @@ fn init_logs() -> Result<opentelemetry_sdk::logs::LoggerProvider, LogError> {

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
// 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_traces()?;
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")];
Expand Down
Loading