Skip to content

Commit

Permalink
0.28 migration guide and OTLP Example fixes (#2622)
Browse files Browse the repository at this point in the history
  • Loading branch information
cijothomas authored Feb 7, 2025
1 parent 61e539f commit 1fc86da
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 56 deletions.
188 changes: 188 additions & 0 deletions docs/migration_0.28.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# Migration guide from 0.27 to 0.28

OpenTelemetry Rust 0.28 introduces a large number of breaking changes that
impact all signals (logs/metrics/traces). This guide is intended to help with a
smooth migration for the common use cases of using `opentelemetry`,
`opentelemetry_sdk` `opentelemetry-otlp`, `opentelemetry-appender-tracing`
crates. The detailed changelog for each crate that you use can be consulted for
the full set of changes. This doc covers only the common scenario.

## Tracing Shutdown changes

`opentelemetry::global::shutdown_tracer_provider()` is removed. Now, you should
explicitly call shutdown() on the created tracer provider.

Before (0.27):

```rust
opentelemetry::global::shutdown_tracer_provider();
```

After (0.28):

```rust
let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
.build();

// Clone and set the tracer provider globally. Retain the original to invoke shutdown later.
opentelemetry::global::set_tracer_provider(tracer_provider.clone());

// Shutdown the provider when application is exiting.
tracer_provider.shutdown();
```

This now makes shutdown consistent across signals.

## Rename SDK Structs

`LoggerProvider`, `TracerProvider` are renamed to `SdkLoggerProvider` and
`SdkTracerProvider` respectively. `MeterProvider` was already named
`SdkMeterProvider` and this now ensures consistency across signals.

### Async Runtime Requirements removed

When using OTLP Exporter for Logs, Traces a "batching" exporter is recommended.
Also, metrics always required a component named `PeriodicReader`. These
components previously needed user to pass in an async runtime and enable
appropriate feature flag depending on the runtime.

These components have been re-written to no longer require an async runtime.
Instead they operate by spawning dedicated background thread, and making
blocking calls from the same.

PeriodicReader, BatchSpanProcessor, BatchLogProcessor are the components
affected.

For Logs,Traces replace `.with_batch_exporter(exporter, runtime::Tokio)` with
`.with_batch_exporter(exporter)`.

For Metrics, replace `let reader =
PeriodicReader::builder(exporter, runtime::Tokio).build();` with `let reader =
PeriodicReader::builder(exporter).build();` or more conveniently,
`.with_periodic_exporter(exporter)`.

Please note the following:

* With the new approach, only the following grpc/http clients are supported in
`opentelemetry-otlp`.

`grpc-tonic` (OTLP
Exporter must be created from within a Tokio runtime)

`reqwest-blocking-client`

In other words,
`reqwest` and `hyper` are not supported.
If using exporters other than `opentelemetry-otlp`, consult the docs
for the same to know if there are any restrictions/requirements on async
runtime.

* Timeout enforcement is now moved to Exporters. i.e
BatchProcessor,PeriodicReader does not enforce timeouts. For logs and traces,
`max_export_timeout` (on Processors) or `OTEL_BLRP_EXPORT_TIMEOUT` or
`OTEL_BSP_EXPORT_TIMEOUT` is no longer supported. For metrics, `with_timeout` on
PeriodicReader is no longer supported.

`OTEL_EXPORTER_OTLP_TIMEOUT` can be used to setup timeout for OTLP Exporters
via environment variables, or `.with_tonic().with_timeout()` or
`.with_http().with_timeout()` programmatically.

* If you need the old behavior (your application cannot spawn a new thread, or
need to use another networking client etc.) use appropriate feature flag(s) from
below.
“experimental_metrics_periodicreader_with_async_runtime”
"experimental_logs_batch_log_processor_with_async_runtime"
"experimental_trace_batch_span_processor_with_async_runtime"

**and** adjust the namespace:

Example, when using Tokio runtime.

```rust
let reader = opentelemetry_sdk::metrics::periodic_reader_with_async_runtime::PeriodicReader::builder(exporter, runtime::Tokio).build();
let tracer_provider = SdkTracerProvider::builder()
.with_span_processor(span_processor_with_async_runtime::BatchSpanProcessor::builder(exporter, runtime::Tokio).build())
.build();
let logger_provider = SdkLoggerProvider::builder()
.with_log_processor(log_processor_with_async_runtime::BatchLogProcessor::builder(exporter, runtime::Tokio).build())
.build();
```

## OTLP Default change

"grpc-tonic" feature flag is no longer enabled by default in
`opentelemetry-otlp`. "http-proto" and "reqwest-blocking-client" features are
added as default, to align with the OTel specification.

## Resource Changes

`Resource` creation is moved to a builder pattern, and `Resource::{new, empty,
from_detectors, new_with_defaults, from_schema_url, merge, default}` are
replaced with `Resource::builder()`.

Before:

```rust
Resource::default().with_attributes([
KeyValue::new("service.name", "test_service"),
KeyValue::new("key", "value"),
]);
```

After:

```rust
Resource::builder()
.with_service_name("test_service")
.with_attribute(KeyValue::new("key", "value"))
.build();
```

## Improved internal logging

OpenTelemetry internally used `tracing` to emit its internal logs. This is under
feature-flag "internal-logs" that is enabled by default in all crates. When
using OTel Logging, care must be taken to avoid OTel's own internal log being
fed back to OTel, creating an circular dependency. This can be achieved via proper
filtering. The OTLP Examples in the repo shows how to achieve this. It also
shows how to send OTel's internal logs to stdout using `tracing::Fmt`.

## Full example

A fully runnable example application using OTLP Exporter is provided in this
repo. Comparing the 0.27 vs 0.28 of the example would give a good overview of
the changes required to be made.

[Basic OTLP Example
(0.27)](https://github.com/open-telemetry/opentelemetry-rust/tree/opentelemetry-otlp-0.27.0/opentelemetry-otlp/examples)
[Basic OTLP Example
(0.28)](https://github.com/open-telemetry/opentelemetry-rust/tree/opentelemetry-otlp-0.27.0/opentelemetry-otlp/examples)
// TODO: Update this link after github tag is created.

This guide covers only the most common breaking changes. If you’re using custom
exporters or processors (or authoring one), please consult the changelog for
additional migration details.

## Notes on Breaking Changes and the Path to 1.0

We understand that breaking changes can be challenging, but they are essential
for the growth and stability of the project. With the release of 0.28, the
Metric API (`opentelemetry` crate, "metrics" feature flag) and LogBridge API
(`opentelemetry` crate, "logs" feature flag) are now stable, and we do not
anticipate further breaking changes for these components.

Moreover, the `opentelemetry_sdk` crate for "logs" and "metrics" will have a
very high bar for any future breaking changes. Any changes are expected to
primarily impact those developing custom components, such as custom exporters.
In the upcoming releases, we aim to bring the "traces" feature to the same level
of stability as "logs" and "metrics". Additionally, "opentelemetry-otlp", the
official exporter, will also receive stability guarantees.

We are excited to announce that a 1.0 release, encompassing logs, metrics, and
traces, is planned for June 2025. We appreciate your patience and support as we
work towards this milestone. The 1.0 release will cover the API
(`opentelemetry`), SDK (`opentelemetry_sdk`), OTLP Exporter
(`opentelemetry-otlp`), and Tracing-Bridge (`opentelemetry-appender-tracing`).

We encourage you to share your feedback via GitHub issues or the OTel-Rust Slack
channel [here](https://cloud-native.slack.com/archives/C03GDP0H023).
4 changes: 2 additions & 2 deletions opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ reqwest-blocking = ["opentelemetry-otlp/reqwest-blocking-client"]
once_cell = { workspace = true }
opentelemetry = { path = "../../../opentelemetry" }
opentelemetry_sdk = { path = "../../../opentelemetry-sdk" }
opentelemetry-otlp = { path = "../..", features = ["http-proto", "http-json", "logs", "internal-logs"], default-features = false}
opentelemetry-appender-tracing = { path = "../../../opentelemetry-appender-tracing", default-features = false}
opentelemetry-otlp = { path = "../.."}
opentelemetry-appender-tracing = { path = "../../../opentelemetry-appender-tracing"}

tokio = { workspace = true, features = ["full"] }
tracing = { workspace = true, features = ["std"]}
Expand Down
67 changes: 38 additions & 29 deletions opentelemetry-otlp/examples/basic-otlp-http/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
/// To use hyper as the HTTP client - cargo run --features="hyper" --no-default-features
use once_cell::sync::Lazy;
use opentelemetry::{
global,
trace::{TraceContextExt, TraceError, Tracer},
trace::{TraceContextExt, Tracer},
InstrumentationScope, KeyValue,
};
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_otlp::{LogExporter, MetricExporter, Protocol, SpanExporter};
use opentelemetry_sdk::Resource;
use opentelemetry_sdk::{
logs::SdkLoggerProvider,
metrics::{MetricError, SdkMeterProvider},
trace::{self as sdktrace, SdkTracerProvider},
};
use opentelemetry_sdk::{
logs::{self as sdklogs},
Resource,
logs::SdkLoggerProvider, metrics::SdkMeterProvider, trace::SdkTracerProvider,
};
use std::error::Error;
use tracing::info;
Expand All @@ -24,52 +18,52 @@ use tracing_subscriber::EnvFilter;

static RESOURCE: Lazy<Resource> = Lazy::new(|| {
Resource::builder()
.with_service_name("basic-otlp-example")
.with_service_name("basic-otlp-example-http")
.build()
});

fn init_logs() -> Result<sdklogs::SdkLoggerProvider, opentelemetry_sdk::logs::LogError> {
fn init_logs() -> SdkLoggerProvider {
let exporter = LogExporter::builder()
.with_http()
.with_endpoint("http://localhost:4318/v1/logs")
.with_protocol(Protocol::HttpBinary)
.build()?;
.build()
.expect("Failed to create log exporter");

Ok(SdkLoggerProvider::builder()
SdkLoggerProvider::builder()
.with_batch_exporter(exporter)
.with_resource(RESOURCE.clone())
.build())
.build()
}

fn init_traces() -> Result<sdktrace::SdkTracerProvider, TraceError> {
fn init_traces() -> SdkTracerProvider {
let exporter = SpanExporter::builder()
.with_http()
.with_protocol(Protocol::HttpBinary) //can be changed to `Protocol::HttpJson` to export in JSON format
.with_endpoint("http://localhost:4318/v1/traces")
.build()?;
.build()
.expect("Failed to create trace exporter");

Ok(SdkTracerProvider::builder()
SdkTracerProvider::builder()
.with_batch_exporter(exporter)
.with_resource(RESOURCE.clone())
.build())
.build()
}

fn init_metrics() -> Result<opentelemetry_sdk::metrics::SdkMeterProvider, MetricError> {
fn init_metrics() -> SdkMeterProvider {
let exporter = MetricExporter::builder()
.with_http()
.with_protocol(Protocol::HttpBinary) //can be changed to `Protocol::HttpJson` to export in JSON format
.with_endpoint("http://localhost:4318/v1/metrics")
.build()?;
.build()
.expect("Failed to create metric exporter");

Ok(SdkMeterProvider::builder()
SdkMeterProvider::builder()
.with_periodic_exporter(exporter)
.with_resource(RESOURCE.clone())
.build())
.build()
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
let logger_provider = init_logs()?;
let logger_provider = init_logs();

// Create a new OpenTelemetryTracingBridge using the above LoggerProvider.
let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider);
Expand Down Expand Up @@ -107,10 +101,25 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
.with(fmt_layer)
.init();

let tracer_provider = init_traces()?;
// At this point Logs (OTel Logs and Fmt Logs) are initialized, which will
// allow internal-logs from Tracing/Metrics initializer to be captured.

let tracer_provider = init_traces();
// Set the global tracer provider using a clone of the tracer_provider.
// Setting global tracer provider is required if other parts of the application
// uses global::tracer() or global::tracer_with_version() to get a tracer.
// Cloning simply creates a new reference to the same tracer provider. It is
// important to hold on to the tracer_provider here, so as to invoke
// shutdown on it when application ends.
global::set_tracer_provider(tracer_provider.clone());

let meter_provider = init_metrics()?;
let meter_provider = init_metrics();
// Set the global meter provider using a clone of the meter_provider.
// Setting global meter provider is required if other parts of the application
// uses global::meter() or global::meter_with_version() to get a meter.
// Cloning simply creates a new reference to the same meter provider. It is
// important to hold on to the meter_provider here, so as to invoke
// shutdown on it when application ends.
global::set_meter_provider(meter_provider.clone());

let common_scope_attributes = vec![KeyValue::new("scope-key", "scope-value")];
Expand Down Expand Up @@ -152,8 +161,8 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
info!(target: "my-target", "hello from {}. My price is {}", "apple", 1.99);

tracer_provider.shutdown()?;
logger_provider.shutdown()?;
meter_provider.shutdown()?;
logger_provider.shutdown()?;

Ok(())
}
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 @@ -11,6 +11,6 @@ opentelemetry = { path = "../../../opentelemetry" }
opentelemetry_sdk = { path = "../../../opentelemetry-sdk" }
opentelemetry-otlp = { path = "../../../opentelemetry-otlp", features = ["grpc-tonic"] }
tokio = { version = "1.0", features = ["full"] }
opentelemetry-appender-tracing = { path = "../../../opentelemetry-appender-tracing", default-features = false}
opentelemetry-appender-tracing = { path = "../../../opentelemetry-appender-tracing"}
tracing = { workspace = true, features = ["std"]}
tracing-subscriber = { workspace = true, features = ["env-filter","registry", "std", "fmt"] }
Loading

0 comments on commit 1fc86da

Please sign in to comment.