diff --git a/.cspell.json b/.cspell.json
index fd5a476fb7..bc3a488c8d 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -26,6 +26,7 @@
// workspace dictionary.
"words": [
"actix",
+ "anyvalue",
"appender",
"appenders",
"Bhasin",
@@ -37,6 +38,7 @@
"deque",
"Dirkjan",
"EPYC",
+ "flamegraph",
"hasher",
"Isobel",
"jaegertracing",
@@ -53,6 +55,7 @@
"OTELCOL",
"OTLP",
"periodicreader",
+ "pprof",
"prost",
"protoc",
"quantile",
@@ -60,10 +63,13 @@
"reqwest",
"runtimes",
"rustc",
+ "serde",
"shoppingcart",
"struct",
"Tescher",
+ "testcontainers",
"testresults",
+ "thiserror",
"tracerprovider",
"updown",
"Zhongyang",
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
index badc78fb10..e412f7163c 100644
--- a/.github/workflows/integration_tests.yml
+++ b/.github/workflows/integration_tests.yml
@@ -9,7 +9,6 @@ jobs:
integration_tests:
runs-on: ubuntu-latest
timeout-minutes: 10
- if: ${{ github.event.label.name == 'integration tests' || contains(github.event.pull_request.labels.*.name, 'integration tests') }}
steps:
- name: Free disk space
run: |
diff --git a/Cargo.toml b/Cargo.toml
index 1445047825..3f119360d1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,9 @@ members = [
"stress",
]
resolver = "2"
+# Avoid applying patch to force use of workspace members for this
+# not actively maintained crate
+exclude = ["opentelemetry-prometheus"]
[profile.bench]
# https://doc.rust-lang.org/cargo/reference/profiles.html#bench
@@ -50,3 +53,9 @@ tracing = { version = ">=0.1.40", default-features = false }
tracing-core = { version = ">=0.1.33", default-features = false }
tracing-subscriber = { version = "0.3", default-features = false }
url = { version = "2.5", default-features = false }
+
+# Aviod use of crates.io version of these crates through the tracing-opentelemetry dependencies
+[patch.crates-io]
+opentelemetry = { path = "opentelemetry" }
+opentelemetry_sdk = { path = "opentelemetry-sdk" }
+opentelemetry-stdout = { path = "opentelemetry-stdout" }
diff --git a/opentelemetry-appender-log/src/lib.rs b/opentelemetry-appender-log/src/lib.rs
index 81ec10d129..1cc75de47f 100644
--- a/opentelemetry-appender-log/src/lib.rs
+++ b/opentelemetry-appender-log/src/lib.rs
@@ -116,7 +116,9 @@ use opentelemetry::{
InstrumentationScope, Key,
};
#[cfg(feature = "experimental_metadata_attributes")]
-use opentelemetry_semantic_conventions::attribute::{CODE_FILEPATH, CODE_LINENO, CODE_NAMESPACE};
+use opentelemetry_semantic_conventions::attribute::{
+ CODE_FILEPATH, CODE_LINE_NUMBER, CODE_NAMESPACE,
+};
pub struct OpenTelemetryLogBridge
where
@@ -158,7 +160,7 @@ where
}
if let Some(line_no) = record.line() {
- log_record.add_attribute(Key::new(CODE_LINENO), AnyValue::from(line_no));
+ log_record.add_attribute(Key::new(CODE_LINE_NUMBER), AnyValue::from(line_no));
}
if let Some(module) = record.module_path() {
@@ -769,7 +771,7 @@ mod tests {
use super::OpenTelemetryLogBridge;
use opentelemetry::{logs::AnyValue, StringValue};
- use opentelemetry_sdk::{logs::LoggerProvider, testing::logs::InMemoryLogExporter};
+ use opentelemetry_sdk::{logs::InMemoryLogExporter, logs::LoggerProvider};
use log::Log;
@@ -1171,7 +1173,7 @@ mod tests {
#[test]
fn logbridge_code_attributes() {
use opentelemetry_semantic_conventions::attribute::{
- CODE_FILEPATH, CODE_LINENO, CODE_NAMESPACE,
+ CODE_FILEPATH, CODE_LINE_NUMBER, CODE_NAMESPACE,
};
let exporter = InMemoryLogExporter::default();
@@ -1212,7 +1214,7 @@ mod tests {
Some(AnyValue::String(StringValue::from("service"))),
get(CODE_NAMESPACE)
);
- assert_eq!(Some(AnyValue::Int(101)), get(CODE_LINENO));
+ assert_eq!(Some(AnyValue::Int(101)), get(CODE_LINE_NUMBER));
}
#[test]
diff --git a/opentelemetry-appender-tracing/CHANGELOG.md b/opentelemetry-appender-tracing/CHANGELOG.md
index 9fac13a4b2..0bbeabb275 100644
--- a/opentelemetry-appender-tracing/CHANGELOG.md
+++ b/opentelemetry-appender-tracing/CHANGELOG.md
@@ -3,6 +3,7 @@
## vNext
- Bump msrv to 1.75.0.
+- New experimental feature to use trace\_id & span\_id from spans created through the [tracing](https://crates.io/crates/tracing) crate (experimental_use_tracing_span_context) [#2438](https://github.com/open-telemetry/opentelemetry-rust/pull/2438)
## 0.27.0
diff --git a/opentelemetry-appender-tracing/Cargo.toml b/opentelemetry-appender-tracing/Cargo.toml
index 9e831eb38f..40cd98f801 100644
--- a/opentelemetry-appender-tracing/Cargo.toml
+++ b/opentelemetry-appender-tracing/Cargo.toml
@@ -17,6 +17,7 @@ tracing = { workspace = true, features = ["std"]}
tracing-core = { workspace = true }
tracing-log = { version = "0.2", optional = true }
tracing-subscriber = { workspace = true, features = ["registry", "std"] }
+tracing-opentelemetry = { version = "0.28", optional = true }
[dev-dependencies]
log = { workspace = true }
@@ -28,11 +29,12 @@ criterion = { workspace = true }
tokio = { workspace = true, features = ["full"]}
[target.'cfg(not(target_os = "windows"))'.dev-dependencies]
-pprof = { version = "0.13", features = ["flamegraph", "criterion"] }
+pprof = { version = "0.14", features = ["flamegraph", "criterion"] }
[features]
experimental_metadata_attributes = ["dep:tracing-log"]
spec_unstable_logs_enabled = ["opentelemetry/spec_unstable_logs_enabled"]
+experimental_use_tracing_span_context = ["tracing-opentelemetry"]
[[bench]]
diff --git a/opentelemetry-appender-tracing/benches/logs.rs b/opentelemetry-appender-tracing/benches/logs.rs
index e5fb98273c..6c09f5c966 100644
--- a/opentelemetry-appender-tracing/benches/logs.rs
+++ b/opentelemetry-appender-tracing/benches/logs.rs
@@ -16,8 +16,8 @@
use criterion::{criterion_group, criterion_main, Criterion};
use opentelemetry::InstrumentationScope;
use opentelemetry_appender_tracing::layer as tracing_layer;
-use opentelemetry_sdk::export::logs::{LogBatch, LogExporter};
use opentelemetry_sdk::logs::LogResult;
+use opentelemetry_sdk::logs::{LogBatch, LogExporter};
use opentelemetry_sdk::logs::{LogProcessor, LogRecord, LoggerProvider};
use opentelemetry_sdk::Resource;
use pprof::criterion::{Output, PProfProfiler};
diff --git a/opentelemetry-appender-tracing/src/layer.rs b/opentelemetry-appender-tracing/src/layer.rs
index af752c5f8e..0b77cbd2f5 100644
--- a/opentelemetry-appender-tracing/src/layer.rs
+++ b/opentelemetry-appender-tracing/src/layer.rs
@@ -8,7 +8,7 @@ use tracing_core::Level;
use tracing_core::Metadata;
#[cfg(feature = "experimental_metadata_attributes")]
use tracing_log::NormalizeEvent;
-use tracing_subscriber::Layer;
+use tracing_subscriber::{registry::LookupSpan, Layer};
const INSTRUMENTATION_LIBRARY_NAME: &str = "opentelemetry-appender-tracing";
@@ -149,7 +149,7 @@ where
impl Layer for OpenTelemetryTracingBridge
where
- S: tracing::Subscriber,
+ S: tracing::Subscriber + for<'a> LookupSpan<'a>,
P: LoggerProvider + Send + Sync + 'static,
L: Logger + Send + Sync + 'static,
{
@@ -180,6 +180,26 @@ where
// Visit fields.
event.record(&mut visitor);
+ #[cfg(feature = "experimental_use_tracing_span_context")]
+ if let Some(span) = _ctx.event_span(event) {
+ use tracing_opentelemetry::OtelData;
+ let opt_span_id = span
+ .extensions()
+ .get::()
+ .and_then(|otd| otd.builder.span_id);
+
+ let opt_trace_id = span.scope().last().and_then(|root_span| {
+ root_span
+ .extensions()
+ .get::()
+ .and_then(|otd| otd.builder.trace_id)
+ });
+
+ if let Some((trace_id, span_id)) = opt_trace_id.zip(opt_span_id) {
+ log_record.set_trace_context(trace_id, span_id, None);
+ }
+ }
+
//emit record
self.logger.emit(log_record);
}
@@ -213,9 +233,9 @@ mod tests {
use opentelemetry::trace::TracerProvider as _;
use opentelemetry::trace::{TraceContextExt, TraceFlags, Tracer};
use opentelemetry::{logs::AnyValue, Key};
- use opentelemetry_sdk::export::logs::{LogBatch, LogExporter};
+ use opentelemetry_sdk::logs::InMemoryLogExporter;
+ use opentelemetry_sdk::logs::{LogBatch, LogExporter};
use opentelemetry_sdk::logs::{LogRecord, LogResult, LoggerProvider};
- use opentelemetry_sdk::testing::logs::InMemoryLogExporter;
use opentelemetry_sdk::trace::{Sampler, TracerProvider};
use tracing::{error, warn};
use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
@@ -495,6 +515,67 @@ mod tests {
}
}
+ #[cfg(feature = "experimental_use_tracing_span_context")]
+ #[test]
+ fn tracing_appender_inside_tracing_crate_context() {
+ use opentelemetry_sdk::trace::InMemorySpanExporterBuilder;
+
+ // Arrange
+ let exporter: InMemoryLogExporter = InMemoryLogExporter::default();
+ let logger_provider = LoggerProvider::builder()
+ .with_simple_exporter(exporter.clone())
+ .build();
+
+ // setup tracing layer to compare trace/span IDs against
+ let span_exporter = InMemorySpanExporterBuilder::new().build();
+ let tracer_provider = TracerProvider::builder()
+ .with_simple_exporter(span_exporter.clone())
+ .build();
+ let tracer = tracer_provider.tracer("test-tracer");
+
+ let level_filter = tracing_subscriber::filter::LevelFilter::INFO;
+ let log_layer =
+ layer::OpenTelemetryTracingBridge::new(&logger_provider).with_filter(level_filter);
+
+ let subscriber = tracing_subscriber::registry()
+ .with(log_layer)
+ .with(tracing_opentelemetry::layer().with_tracer(tracer));
+
+ // Avoiding global subscriber.init() as that does not play well with unit tests.
+ let _guard = tracing::subscriber::set_default(subscriber);
+
+ // Act
+ tracing::info_span!("outer-span").in_scope(|| {
+ error!("first-event");
+
+ tracing::info_span!("inner-span").in_scope(|| {
+ error!("second-event");
+ });
+ });
+
+ logger_provider.force_flush();
+
+ let logs = exporter.get_emitted_logs().expect("No emitted logs");
+ assert_eq!(logs.len(), 2);
+
+ let spans = span_exporter.get_finished_spans().unwrap();
+ assert_eq!(spans.len(), 2);
+
+ let trace_id = spans[0].span_context.trace_id();
+ assert_eq!(trace_id, spans[1].span_context.trace_id());
+ let inner_span_id = spans[0].span_context.span_id();
+ let outer_span_id = spans[1].span_context.span_id();
+ assert_eq!(outer_span_id, spans[0].parent_span_id);
+
+ let trace_ctx0 = logs[0].record.trace_context().unwrap();
+ let trace_ctx1 = logs[1].record.trace_context().unwrap();
+
+ assert_eq!(trace_ctx0.trace_id, trace_id);
+ assert_eq!(trace_ctx1.trace_id, trace_id);
+ assert_eq!(trace_ctx0.span_id, outer_span_id);
+ assert_eq!(trace_ctx1.span_id, inner_span_id);
+ }
+
#[test]
fn tracing_appender_standalone_with_tracing_log() {
// Arrange
diff --git a/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml b/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml
index 242ea0c1f8..ef1595b0ae 100644
--- a/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml
+++ b/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml
@@ -6,14 +6,14 @@ license = "Apache-2.0"
publish = false
[features]
+default = ["reqwest-blocking"]
reqwest-blocking = ["opentelemetry-otlp/reqwest-blocking-client"]
-hyper = ["opentelemetry-otlp/hyper-client"]
[dependencies]
once_cell = { workspace = true }
opentelemetry = { path = "../../../opentelemetry" }
-opentelemetry_sdk = { path = "../../../opentelemetry-sdk", features = ["rt-tokio", "experimental_metrics_periodicreader_with_async_runtime"]}
-opentelemetry-otlp = { path = "../..", features = ["http-proto", "http-json", "logs", "internal-logs"]}
+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}
tokio = { workspace = true, features = ["full"] }
diff --git a/opentelemetry-otlp/examples/basic-otlp-http/Dockerfile b/opentelemetry-otlp/examples/basic-otlp-http/Dockerfile
deleted file mode 100644
index f88c276a55..0000000000
--- a/opentelemetry-otlp/examples/basic-otlp-http/Dockerfile
+++ /dev/null
@@ -1,6 +0,0 @@
-FROM rust:1.51
-COPY . /usr/src/basic-otlp-http/
-WORKDIR /usr/src/basic-otlp-http/
-RUN cargo build --release
-RUN cargo install --path .
-CMD ["/usr/local/cargo/bin/basic-otlp-http"]
diff --git a/opentelemetry-otlp/examples/basic-otlp-http/README.md b/opentelemetry-otlp/examples/basic-otlp-http/README.md
index 2d06e6a8fe..78ff779a66 100644
--- a/opentelemetry-otlp/examples/basic-otlp-http/README.md
+++ b/opentelemetry-otlp/examples/basic-otlp-http/README.md
@@ -16,46 +16,25 @@ recommended approach when using OTLP exporters. While it can be modified to use
a `SimpleExporter`, this requires making the main function a regular main and
*not* tokio main.
-// TODO: Document `hyper` feature flag when using SimpleProcessor.
+// TODO: Document how to use hyper client.
## Usage
-### `docker-compose`
-
-By default runs against the `otel/opentelemetry-collector:latest` image, and uses `reqwest-client`
-as the http client, using http as the transport.
-
-```shell
-docker-compose up
-```
-
-In another terminal run the application `cargo run`
-
-The docker-compose terminal will display logs, traces, metrics.
-
-Press Ctrl+C to stop the collector, and then tear it down:
-
-```shell
-docker-compose down
-```
-
-### Manual
-
-If you don't want to use `docker-compose`, you can manually run the `otel/opentelemetry-collector` container
-and inspect the logs to see traces being transferred.
+Run the `otel/opentelemetry-collector` container using docker
+and inspect the logs to see the exported telemetry.
On Unix based systems use:
```shell
# From the current directory, run `opentelemetry-collector`
-docker run --rm -it -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
+docker run --rm -it -p 4317:4317 -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
```
On Windows use:
```shell
# From the current directory, run `opentelemetry-collector`
-docker run --rm -it -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
+docker run --rm -it -p 4317:4317 -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
```
Run the app which exports logs, metrics and traces via OTLP to the collector
@@ -64,11 +43,7 @@ Run the app which exports logs, metrics and traces via OTLP to the collector
cargo run
```
-By default the app will use a `reqwest` client to send. A hyper 0.14 client can be used with the `hyper` feature enabled
-
-```shell
-cargo run --no-default-features --features=hyper
-```
+The app will use a `reqwest-blocking` client to send.
## View results
diff --git a/opentelemetry-otlp/examples/basic-otlp-http/docker-compose.yaml b/opentelemetry-otlp/examples/basic-otlp-http/docker-compose.yaml
deleted file mode 100644
index dc9d1e7a5d..0000000000
--- a/opentelemetry-otlp/examples/basic-otlp-http/docker-compose.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-version: "2"
-services:
-
- # Collector
- otel-collector:
- image: otel/opentelemetry-collector:latest
- command: ["--config=/etc/otel-collector-config.yaml", "${OTELCOL_ARGS}"]
- volumes:
- - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ports:
- - "4318:4318" # OTLP HTTP receiver
-
-
-
-
diff --git a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs
index bf33828091..763add8b50 100644
--- a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs
+++ b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs
@@ -69,9 +69,8 @@ fn init_metrics() -> Result Result<(), Box> {
+#[tokio::main]
+async fn main() -> Result<(), Box> {
let logger_provider = init_logs()?;
// Create a new OpenTelemetryTracingBridge using the above LoggerProvider.
diff --git a/opentelemetry-otlp/examples/basic-otlp/Cargo.toml b/opentelemetry-otlp/examples/basic-otlp/Cargo.toml
index 735a9470d7..f841ae5374 100644
--- a/opentelemetry-otlp/examples/basic-otlp/Cargo.toml
+++ b/opentelemetry-otlp/examples/basic-otlp/Cargo.toml
@@ -8,7 +8,7 @@ publish = false
[dependencies]
once_cell = { workspace = true }
opentelemetry = { path = "../../../opentelemetry" }
-opentelemetry_sdk = { path = "../../../opentelemetry-sdk", features = ["rt-tokio"] }
+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}
diff --git a/opentelemetry-otlp/examples/basic-otlp/Dockerfile b/opentelemetry-otlp/examples/basic-otlp/Dockerfile
deleted file mode 100644
index b63241e283..0000000000
--- a/opentelemetry-otlp/examples/basic-otlp/Dockerfile
+++ /dev/null
@@ -1,6 +0,0 @@
-FROM rust:1.51
-COPY . /usr/src/basic-otlp/
-WORKDIR /usr/src/basic-otlp/
-RUN cargo build --release
-RUN cargo install --path .
-CMD ["/usr/local/cargo/bin/basic-otlp"]
diff --git a/opentelemetry-otlp/examples/basic-otlp/README.md b/opentelemetry-otlp/examples/basic-otlp/README.md
index ca02018ad5..f4ebe150fb 100644
--- a/opentelemetry-otlp/examples/basic-otlp/README.md
+++ b/opentelemetry-otlp/examples/basic-otlp/README.md
@@ -49,42 +49,21 @@ fn main() -> Result<(), Box> {
## Usage
-### `docker-compose`
-
-By default runs against the `otel/opentelemetry-collector:latest` image, and uses the `tonic`'s
-`grpc` example as the transport.
-
-```shell
-docker-compose up
-```
-
-In another terminal run the application `cargo run`
-
-The docker-compose terminal will display logs, traces, metrics.
-
-Press Ctrl+C to stop the collector, and then tear it down:
-
-```shell
-docker-compose down
-```
-
-### Manual
-
-If you don't want to use `docker-compose`, you can manually run the `otel/opentelemetry-collector` container
-and inspect the logs to see traces being transferred.
+Run the `otel/opentelemetry-collector` container using docker
+and inspect the logs to see the exported telemetry.
On Unix based systems use:
```shell
# From the current directory, run `opentelemetry-collector`
-docker run --rm -it -p 4317:4317 -v $(pwd):/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
+docker run --rm -it -p 4317:4317 -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
```
On Windows use:
```shell
# From the current directory, run `opentelemetry-collector`
-docker run --rm -it -p 4317:4317 -v "%cd%":/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
+docker run --rm -it -p 4317:4317 -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
```
Run the app which exports logs, metrics and traces via OTLP to the collector
diff --git a/opentelemetry-otlp/examples/basic-otlp/docker-compose.yaml b/opentelemetry-otlp/examples/basic-otlp/docker-compose.yaml
deleted file mode 100644
index fc9b3f1948..0000000000
--- a/opentelemetry-otlp/examples/basic-otlp/docker-compose.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-version: "2"
-services:
-
- # Collector
- otel-collector:
- image: otel/opentelemetry-collector:latest
- command: ["--config=/etc/otel-collector-config.yaml", "${OTELCOL_ARGS}"]
- volumes:
- - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ports:
- - "4317:4317" # OTLP gRPC receiver
-
-
-
-
diff --git a/opentelemetry-otlp/src/exporter/http/logs.rs b/opentelemetry-otlp/src/exporter/http/logs.rs
index 9d00602eed..8b828730cd 100644
--- a/opentelemetry-otlp/src/exporter/http/logs.rs
+++ b/opentelemetry-otlp/src/exporter/http/logs.rs
@@ -2,7 +2,7 @@ use std::sync::Arc;
use http::{header::CONTENT_TYPE, Method};
use opentelemetry::otel_debug;
-use opentelemetry_sdk::export::logs::{LogBatch, LogExporter};
+use opentelemetry_sdk::logs::{LogBatch, LogExporter};
use opentelemetry_sdk::logs::{LogError, LogResult};
use super::OtlpHttpClient;
diff --git a/opentelemetry-otlp/src/exporter/http/mod.rs b/opentelemetry-otlp/src/exporter/http/mod.rs
index 4d1af8c880..07a70bf57d 100644
--- a/opentelemetry-otlp/src/exporter/http/mod.rs
+++ b/opentelemetry-otlp/src/exporter/http/mod.rs
@@ -14,9 +14,9 @@ use opentelemetry_proto::transform::logs::tonic::group_logs_by_resource_and_scop
#[cfg(feature = "trace")]
use opentelemetry_proto::transform::trace::tonic::group_spans_by_resource_and_scope;
#[cfg(feature = "logs")]
-use opentelemetry_sdk::export::logs::LogBatch;
+use opentelemetry_sdk::logs::LogBatch;
#[cfg(feature = "trace")]
-use opentelemetry_sdk::export::trace::SpanData;
+use opentelemetry_sdk::trace::SpanData;
use prost::Message;
use std::collections::HashMap;
use std::env;
@@ -27,6 +27,9 @@ use std::time::Duration;
#[cfg(feature = "metrics")]
mod metrics;
+#[cfg(feature = "metrics")]
+use opentelemetry_sdk::metrics::data::ResourceMetrics;
+
#[cfg(feature = "logs")]
pub(crate) mod logs;
@@ -336,7 +339,7 @@ impl OtlpHttpClient {
#[cfg(feature = "metrics")]
fn build_metrics_export_body(
&self,
- metrics: &mut opentelemetry_sdk::metrics::data::ResourceMetrics,
+ metrics: &mut ResourceMetrics,
) -> opentelemetry_sdk::metrics::MetricResult<(Vec, &'static str)> {
use opentelemetry_proto::tonic::collector::metrics::v1::ExportMetricsServiceRequest;
diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs
index d188dc8911..cf3411e2e0 100644
--- a/opentelemetry-otlp/src/exporter/http/trace.rs
+++ b/opentelemetry-otlp/src/exporter/http/trace.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
use futures_core::future::BoxFuture;
use http::{header::CONTENT_TYPE, Method};
use opentelemetry::{otel_debug, trace::TraceError};
-use opentelemetry_sdk::export::trace::{ExportResult, SpanData, SpanExporter};
+use opentelemetry_sdk::trace::{ExportResult, SpanData, SpanExporter};
use super::OtlpHttpClient;
diff --git a/opentelemetry-otlp/src/exporter/tonic/logs.rs b/opentelemetry-otlp/src/exporter/tonic/logs.rs
index 053331b428..a4d276fd8f 100644
--- a/opentelemetry-otlp/src/exporter/tonic/logs.rs
+++ b/opentelemetry-otlp/src/exporter/tonic/logs.rs
@@ -3,7 +3,7 @@ use opentelemetry::otel_debug;
use opentelemetry_proto::tonic::collector::logs::v1::{
logs_service_client::LogsServiceClient, ExportLogsServiceRequest,
};
-use opentelemetry_sdk::export::logs::{LogBatch, LogExporter};
+use opentelemetry_sdk::logs::{LogBatch, LogExporter};
use opentelemetry_sdk::logs::{LogError, LogResult};
use tonic::{codegen::CompressionEncoding, service::Interceptor, transport::Channel, Request};
diff --git a/opentelemetry-otlp/src/exporter/tonic/trace.rs b/opentelemetry-otlp/src/exporter/tonic/trace.rs
index 998acafad5..fb72cccf4e 100644
--- a/opentelemetry-otlp/src/exporter/tonic/trace.rs
+++ b/opentelemetry-otlp/src/exporter/tonic/trace.rs
@@ -5,7 +5,7 @@ use opentelemetry::{otel_debug, trace::TraceError};
use opentelemetry_proto::tonic::collector::trace::v1::{
trace_service_client::TraceServiceClient, ExportTraceServiceRequest,
};
-use opentelemetry_sdk::export::trace::{ExportResult, SpanData, SpanExporter};
+use opentelemetry_sdk::trace::{ExportResult, SpanData, SpanExporter};
use tonic::{codegen::CompressionEncoding, service::Interceptor, transport::Channel, Request};
use opentelemetry_proto::transform::trace::tonic::group_spans_by_resource_and_scope;
diff --git a/opentelemetry-otlp/src/lib.rs b/opentelemetry-otlp/src/lib.rs
index 3e02ef16a8..60b9d4c44c 100644
--- a/opentelemetry-otlp/src/lib.rs
+++ b/opentelemetry-otlp/src/lib.rs
@@ -7,10 +7,8 @@
//! order to support open-source telemetry data formats (e.g. Jaeger,
//! Prometheus, etc.) sending to multiple open-source or commercial back-ends.
//!
-//! Currently, this crate only support sending telemetry in OTLP
-//! via grpc and http (in binary format). Supports for other format and protocol
-//! will be added in the future. The details of what's currently offering in this
-//! crate can be found in this doc.
+//! Currently, this crate supports sending telemetry in OTLP
+//! via gRPC and http (binary and json).
//!
//! # Quickstart
//!
@@ -56,34 +54,36 @@
//!
//! ## Performance
//!
-//! For optimal performance, a batch exporter is recommended as the simple
-//! exporter will export each span synchronously on dropping. You can enable the
-//! [`rt-tokio`], [`rt-tokio-current-thread`] or [`rt-async-std`] features and
-//! specify a runtime on the pipeline builder to have a batch exporter
-//! configured for you automatically.
+//! For optimal performance, a batch exporting processor is recommended as the simple
+//! processor will export each span synchronously on dropping, and is only good
+//! for test/debug purposes.
//!
//! ```toml
//! [dependencies]
-//! opentelemetry_sdk = { version = "*", features = ["async-std"] }
//! opentelemetry-otlp = { version = "*", features = ["grpc-tonic"] }
//! ```
//!
//! ```no_run
//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
//! # {
-//! # fn main() -> Result<(), opentelemetry::trace::TraceError> {
-//! let tracer = opentelemetry_sdk::trace::TracerProvider::builder()
-//! .with_batch_exporter(
-//! opentelemetry_otlp::SpanExporter::builder()
-//! .with_tonic()
-//! .build()?,
-//! opentelemetry_sdk::runtime::Tokio,
-//! )
-//! .build();
+//! use opentelemetry::global;
+//! use opentelemetry::trace::Tracer;
//!
-//! # Ok(())
-//! # }
-//! # }
+//! fn main() -> Result<(), Box> {
+//! // First, create a OTLP exporter builder. Configure it as you need.
+//! let otlp_exporter = opentelemetry_otlp::SpanExporter::builder().with_tonic().build()?;
+//! // Then pass it into provider builder
+//! let _ = opentelemetry_sdk::trace::TracerProvider::builder()
+//! .with_batch_exporter(otlp_exporter)
+//! .build();
+//! let tracer = global::tracer("my_tracer");
+//! tracer.in_span("doing_work", |cx| {
+//! // Traced app logic here...
+//! });
+//!
+//! Ok(())
+//! # }
+//! }
//! ```
//!
//! [`tokio`]: https://tokio.rs
@@ -92,7 +92,7 @@
//! # Feature Flags
//! The following feature flags can enable exporters for different telemetry signals:
//!
-//! * `trace`: Includes the trace exporters (enabled by default).
+//! * `trace`: Includes the trace exporters.
//! * `metrics`: Includes the metrics exporters.
//! * `logs`: Includes the logs exporters.
//!
@@ -101,8 +101,8 @@
//!
//! The following feature flags offer additional configurations on gRPC:
//!
-//! For users uses `tonic` as grpc layer:
-//! * `grpc-tonic`: Use `tonic` as grpc layer. This is enabled by default.
+//! For users using `tonic` as grpc layer:
+//! * `grpc-tonic`: Use `tonic` as grpc layer.
//! * `gzip-tonic`: Use gzip compression for `tonic` grpc layer.
//! * `zstd-tonic`: Use zstd compression for `tonic` grpc layer.
//! * `tls-roots`: Adds system trust roots to rustls-based gRPC clients using the rustls-native-certs crate
@@ -110,8 +110,8 @@
//!
//! The following feature flags offer additional configurations on http:
//!
-//! * `http-proto`: Use http as transport layer, protobuf as body format.
-//! * `reqwest-blocking-client`: Use reqwest blocking http client.
+//! * `http-proto`: Use http as transport layer, protobuf as body format. This feature is enabled by default.
+//! * `reqwest-blocking-client`: Use reqwest blocking http client. This feature is enabled by default.
//! * `reqwest-client`: Use reqwest http client.
//! * `reqwest-rustls`: Use reqwest with TLS with system trust roots via `rustls-native-certs` crate.
//! * `reqwest-rustls-webpki-roots`: Use reqwest with TLS with Mozilla's trust roots via `webpki-roots` crate.
@@ -152,7 +152,7 @@
//! .build()?;
//!
//! let tracer_provider = opentelemetry_sdk::trace::TracerProvider::builder()
-//! .with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
+//! .with_batch_exporter(exporter)
//! .with_config(
//! trace::Config::default()
//! .with_sampler(Sampler::AlwaysOn)
@@ -162,7 +162,7 @@
//! .with_max_events_per_span(16)
//! .with_resource(Resource::builder_empty().with_attributes([KeyValue::new("service.name", "example")]).build()),
//! ).build();
-//! global::set_tracer_provider(tracer_provider);
+//! global::set_tracer_provider(tracer_provider.clone());
//! let tracer = global::tracer("tracer-name");
//! # tracer
//! # };
@@ -179,7 +179,7 @@
//!
//! let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(exporter)
//! .with_interval(std::time::Duration::from_secs(3))
-//! .with_timeout(Duration::from_secs(10))
+//! .with_timeout(Duration::from_secs(10))
//! .build();
//!
//! let provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
@@ -262,7 +262,7 @@ pub use crate::exporter::{
OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
};
-use opentelemetry_sdk::export::ExportError;
+use opentelemetry_sdk::ExportError;
/// Type to indicate the builder does not have a client set.
#[derive(Debug, Default, Clone)]
diff --git a/opentelemetry-otlp/src/logs.rs b/opentelemetry-otlp/src/logs.rs
index aa4ea8fa07..3b17c30feb 100644
--- a/opentelemetry-otlp/src/logs.rs
+++ b/opentelemetry-otlp/src/logs.rs
@@ -8,7 +8,7 @@ use std::fmt::Debug;
use opentelemetry_sdk::logs::LogResult;
-use opentelemetry_sdk::export::logs::LogBatch;
+use opentelemetry_sdk::logs::LogBatch;
use crate::{HasExportConfig, NoExporterBuilderSet};
@@ -140,7 +140,7 @@ impl LogExporter {
}
}
-impl opentelemetry_sdk::export::logs::LogExporter for LogExporter {
+impl opentelemetry_sdk::logs::LogExporter for LogExporter {
#[allow(clippy::manual_async_fn)]
fn export(
&self,
diff --git a/opentelemetry-otlp/src/span.rs b/opentelemetry-otlp/src/span.rs
index 190e3fdfce..b8c013f5d4 100644
--- a/opentelemetry-otlp/src/span.rs
+++ b/opentelemetry-otlp/src/span.rs
@@ -5,7 +5,7 @@
use std::fmt::Debug;
use futures_core::future::BoxFuture;
-use opentelemetry_sdk::export::trace::{ExportResult, SpanData};
+use opentelemetry_sdk::trace::{ExportResult, SpanData};
#[cfg(feature = "grpc-tonic")]
use crate::{
@@ -107,7 +107,7 @@ impl HasHttpConfig for SpanExporterBuilder {
/// OTLP exporter that sends tracing information
#[derive(Debug)]
-pub struct SpanExporter(Box);
+pub struct SpanExporter(Box);
impl SpanExporter {
/// Obtain a builder to configure a [SpanExporter].
@@ -116,12 +116,12 @@ impl SpanExporter {
}
/// Build a new span exporter from a client
- pub fn new(client: impl opentelemetry_sdk::export::trace::SpanExporter + 'static) -> Self {
+ pub fn new(client: impl opentelemetry_sdk::trace::SpanExporter + 'static) -> Self {
SpanExporter(Box::new(client))
}
}
-impl opentelemetry_sdk::export::trace::SpanExporter for SpanExporter {
+impl opentelemetry_sdk::trace::SpanExporter for SpanExporter {
fn export(&mut self, batch: Vec) -> BoxFuture<'static, ExportResult> {
self.0.export(batch)
}
diff --git a/opentelemetry-otlp/tests/integration_test/Cargo.toml b/opentelemetry-otlp/tests/integration_test/Cargo.toml
index 5314c1fe61..dc58d5d44b 100644
--- a/opentelemetry-otlp/tests/integration_test/Cargo.toml
+++ b/opentelemetry-otlp/tests/integration_test/Cargo.toml
@@ -14,6 +14,7 @@ testcontainers = { version = "0.23.1", features = ["http_wait"]}
once_cell.workspace = true
anyhow = "1.0.94"
ctor = "0.2.9"
+uuid = { version = "1.3", features = ["v4"] }
tracing-subscriber = { workspace = true, features = ["env-filter","registry", "std", "fmt"] }
tracing = {workspace = true}
diff --git a/opentelemetry-otlp/tests/integration_test/src/test_utils.rs b/opentelemetry-otlp/tests/integration_test/src/test_utils.rs
index bd62674868..d5662407f9 100644
--- a/opentelemetry-otlp/tests/integration_test/src/test_utils.rs
+++ b/opentelemetry-otlp/tests/integration_test/src/test_utils.rs
@@ -20,8 +20,7 @@
use anyhow::Result;
use opentelemetry::{otel_debug, otel_info};
-use std::fs;
-use std::fs::File;
+use std::fs::{self, File, OpenOptions};
use std::os::unix::fs::PermissionsExt;
use std::sync::{Arc, Mutex, Once, OnceLock};
use testcontainers::core::wait::HttpWaitStrategy;
@@ -52,7 +51,7 @@ fn init_tracing() {
// Initialize the tracing subscriber with the OpenTelemetry layer and the
// Fmt layer.
tracing_subscriber::registry().with(fmt_layer).init();
- otel_info!(name: "tracing initializing completed!");
+ otel_info!(name: "tracing::fmt initializing completed! SDK internal logs will be printed to stdout.");
});
}
@@ -125,6 +124,17 @@ fn upsert_empty_file(path: &str) -> File {
file
}
+/// Cleans up file specificed as argument by truncating its content.
+///
+/// This function is meant to cleanup the generated json file before a test starts,
+/// preventing entries from previous tests from interfering with the current test's results.
+pub fn cleanup_file(file_path: &str) {
+ let _ = OpenOptions::new()
+ .write(true)
+ .truncate(true)
+ .open(file_path); // ignore result, as file may not exist
+}
+
///
/// Shuts down our collector container. This should be run as part of each test
/// suite shutting down!
diff --git a/opentelemetry-otlp/tests/integration_test/tests/logs.rs b/opentelemetry-otlp/tests/integration_test/tests/logs.rs
index af78ee9005..26ad95c995 100644
--- a/opentelemetry-otlp/tests/integration_test/tests/logs.rs
+++ b/opentelemetry-otlp/tests/integration_test/tests/logs.rs
@@ -2,15 +2,19 @@
use anyhow::Result;
use ctor::dtor;
-use integration_test_runner::logs_asserter::{read_logs_from_json, LogsAsserter};
use integration_test_runner::test_utils;
+use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_otlp::LogExporter;
use opentelemetry_sdk::logs::LoggerProvider;
use opentelemetry_sdk::{logs as sdklogs, Resource};
use std::fs::File;
-use std::os::unix::fs::MetadataExt;
+use std::io::Read;
+use std::time::Duration;
+use tracing::info;
+use tracing_subscriber::layer::SubscriberExt;
+use uuid::Uuid;
-fn init_logs() -> Result {
+fn init_logs(is_simple: bool) -> Result {
let exporter_builder = LogExporter::builder();
#[cfg(feature = "tonic-client")]
let exporter_builder = exporter_builder.with_tonic();
@@ -24,24 +28,119 @@ fn init_logs() -> Result {
let exporter = exporter_builder.build()?;
- Ok(LoggerProvider::builder()
- .with_batch_exporter(exporter)
+ let mut logger_provider_builder = LoggerProvider::builder();
+ if is_simple {
+ logger_provider_builder = logger_provider_builder.with_simple_exporter(exporter)
+ } else {
+ logger_provider_builder = logger_provider_builder.with_batch_exporter(exporter)
+ };
+
+ let logger_provider = logger_provider_builder
.with_resource(
Resource::builder_empty()
.with_service_name("logs-integration-test")
.build(),
)
- .build())
+ .build();
+
+ Ok(logger_provider)
+}
+
+async fn logs_tokio_helper(is_simple: bool, log_send_outside_rt: bool) -> Result<()> {
+ use crate::{assert_logs_results_contains, init_logs};
+ test_utils::start_collector_container().await?;
+
+ let logger_provider = init_logs(is_simple).unwrap();
+ let layer = OpenTelemetryTracingBridge::new(&logger_provider);
+ // generate a random uuid and store it to expected guid
+ let expected_uuid = std::sync::Arc::new(Uuid::new_v4().to_string());
+ {
+ let clone_uuid = expected_uuid.clone();
+ if log_send_outside_rt {
+ std::thread::spawn(move || {
+ let subscriber = tracing_subscriber::registry().with(layer);
+ let _guard = tracing::subscriber::set_default(subscriber);
+ info!(
+ target: "my-target",
+ uuid = clone_uuid.as_str(),
+ "hello from {}. My price is {}.",
+ "banana",
+ 2.99
+ );
+ })
+ .join()
+ .unwrap();
+ } else {
+ let subscriber = tracing_subscriber::registry().with(layer);
+ let _guard = tracing::subscriber::set_default(subscriber);
+ info!(target: "my-target", uuid = expected_uuid.as_str(), "hello from {}. My price is {}.", "banana", 2.99);
+ }
+ }
+ let _ = logger_provider.shutdown();
+ tokio::time::sleep(Duration::from_secs(5)).await;
+ assert_logs_results_contains(test_utils::LOGS_FILE, expected_uuid.as_str())?;
+ Ok(())
+}
+
+fn logs_non_tokio_helper(is_simple: bool, init_logs_inside_rt: bool) -> Result<()> {
+ let rt = tokio::runtime::Runtime::new()?;
+ let logger_provider = if init_logs_inside_rt {
+ // Initialize the logger provider inside the Tokio runtime
+ rt.block_on(async {
+ // Setup the collector container inside Tokio runtime
+ test_utils::start_collector_container().await?;
+ init_logs(is_simple)
+ })?
+ } else {
+ // Initialize the logger provider outside the Tokio runtime
+ rt.block_on(async {
+ let _ = test_utils::start_collector_container().await;
+ });
+ init_logs(is_simple)?
+ };
+
+ let layer = OpenTelemetryTracingBridge::new(&logger_provider);
+ let subscriber = tracing_subscriber::registry().with(layer);
+
+ // Generate a random UUID and store it to expected guid
+ let expected_uuid = Uuid::new_v4().to_string();
+ {
+ let _guard = tracing::subscriber::set_default(subscriber);
+ info!(
+ target: "my-target",
+ uuid = expected_uuid,
+ "hello from {}. My price is {}.",
+ "banana",
+ 2.99
+ );
+ }
+
+ let _ = logger_provider.shutdown();
+ std::thread::sleep(Duration::from_secs(5));
+ assert_logs_results_contains(test_utils::LOGS_FILE, expected_uuid.as_str())?;
+ Ok(())
+}
+
+fn assert_logs_results_contains(result: &str, expected_content: &str) -> Result<()> {
+ let file = File::open(result)?;
+ let mut contents = String::new();
+ let mut reader = std::io::BufReader::new(&file);
+ reader.read_to_string(&mut contents)?;
+ assert!(contents.contains(expected_content));
+ Ok(())
}
#[cfg(test)]
mod logtests {
+ // The tests in this mod works like below: Emit a log with a UUID,
+ // then read the logs from the file and check if the UUID is present in the
+ // logs. This makes it easy to validate with a single collector and its
+ // output. This is a very simple test but good enough to validate that OTLP
+ // Exporter did work!
+
use super::*;
use integration_test_runner::logs_asserter::{read_logs_from_json, LogsAsserter};
- use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
- use std::{fs::File, time::Duration};
- use tracing::info;
- use tracing_subscriber::layer::SubscriberExt;
+ use std::fs::File;
#[test]
#[should_panic(expected = "assertion `left == right` failed: body does not match")]
@@ -67,78 +166,196 @@ mod logtests {
Ok(())
}
+ // Batch Processor
+
+ // logger initialization - Inside RT
+ // log emission - Inside RT
+ // Client - Tonic, Reqwest-blocking
+ // Worker threads - 4
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
- #[cfg(not(feature = "hyper-client"))]
- #[cfg(not(feature = "reqwest-client"))]
- pub async fn test_logs() -> Result<()> {
- // Make sure the container is running
-
- use integration_test_runner::test_utils;
- use opentelemetry_appender_tracing::layer;
- use tracing::info;
- use tracing_subscriber::layer::SubscriberExt;
-
- use crate::{assert_logs_results, init_logs};
- test_utils::start_collector_container().await?;
-
- let logger_provider = init_logs().unwrap();
- let layer = layer::OpenTelemetryTracingBridge::new(&logger_provider);
- let subscriber = tracing_subscriber::registry().with(layer);
- {
- let _guard = tracing::subscriber::set_default(subscriber);
- info!(target: "my-target", "hello from {}. My price is {}.", "banana", 2.99);
- }
- // TODO: remove below wait before calling logger_provider.shutdown()
- // tokio::time::sleep(Duration::from_secs(10)).await;
- let _ = logger_provider.shutdown();
+ #[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
+ pub async fn logs_batch_tokio_multi_thread() -> Result<()> {
+ logs_tokio_helper(false, false).await
+ }
- tokio::time::sleep(Duration::from_secs(10)).await;
+ // logger initialization - Inside RT
+ // log emission - Inside RT
+ // Client - Tonic, Reqwest-blocking
+ // Worker threads - 1
+ #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+ #[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
+ pub async fn logs_batch_tokio_multi_with_one_worker() -> Result<()> {
+ logs_tokio_helper(false, false).await
+ }
- assert_logs_results(test_utils::LOGS_FILE, "expected/logs.json")?;
+ // logger initialization - Inside RT
+ // log emission - Inside RT
+ // Client - Tonic, Reqwest-blocking
+ // current thread
+ #[tokio::test(flavor = "current_thread")]
+ #[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
+ pub async fn logs_batch_tokio_current() -> Result<()> {
+ logs_tokio_helper(false, false).await
+ }
- Ok(())
+ // logger initialization - Inside RT
+ // Log emission - Outside RT
+ // Client - Tonic, Reqwest-blocking
+ // Worker threads - 4
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+ #[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
+ pub async fn logs_batch_tokio_log_outside_rt_multi_thread() -> Result<()> {
+ logs_tokio_helper(false, true).await
+ }
+
+ // logger initialization - Inside RT
+ // log emission - Outside RT
+ // Client - Tonic, Reqwest-blocking
+ // Worker threads - 1
+ #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+ #[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
+ pub async fn logs_batch_tokio_log_outside_rt_multi_with_one_worker() -> Result<()> {
+ logs_tokio_helper(false, true).await
+ }
+
+ // logger initialization - Inside RT
+ // log emission - Outside RT
+ // Client - Tonic, Reqwest-blocking
+ // current thread
+ #[tokio::test(flavor = "current_thread")]
+ #[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
+ pub async fn logs_batch_tokio_log_outside_rt_current_thread() -> Result<()> {
+ logs_tokio_helper(false, true).await
}
- #[ignore = "TODO: [Fix Me] Failing on CI. Needs to be investigated and resolved."]
+ // logger initialization - Inside RT
+ // Log emission - Inside RT
+ // Client - Tonic, Reqwest-blocking
+ // current thread
#[test]
#[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
- pub fn logs_batch_non_tokio_main() -> Result<()> {
- // Initialize the logger provider inside a tokio runtime
- // as this allows tonic client to capture the runtime,
- // but actual export occurs from the dedicated std::thread
- // created by BatchLogProcessor.
- let rt = tokio::runtime::Runtime::new()?;
- let logger_provider = rt.block_on(async {
- // While we're here setup our collector container too, as this needs tokio to run
- test_utils::start_collector_container().await?;
- init_logs()
- })?;
+ pub fn logs_batch_non_tokio_main_init_logs_inside_rt() -> Result<()> {
+ logs_non_tokio_helper(false, true)
+ }
- info!("LoggerProvider created");
- let layer = OpenTelemetryTracingBridge::new(&logger_provider);
- let subscriber = tracing_subscriber::registry().with(layer);
- {
- let _guard = tracing::subscriber::set_default(subscriber);
- info!(target: "my-target", "hello from {}. My price is {}.", "banana", 2.99);
- }
- let _ = logger_provider.shutdown();
- // tokio::time::sleep(Duration::from_secs(10)).await;
- assert_logs_results(test_utils::LOGS_FILE, "expected/logs.json")?;
+ // logger initialization - Outside RT
+ // log emission - Outside RT
+ // Client - Tonic, Reqwest-blocking
+ // current thread
+ #[test]
+ #[cfg(feature = "reqwest-blocking-client")]
+ pub fn logs_batch_non_tokio_main_with_init_logs_outside_rt() -> Result<()> {
+ logs_non_tokio_helper(false, false)
+ }
- Ok(())
+ // logger initialization - Inside RT
+ // log emission - Outside RT
+ // Client - Tonic, Reqwest-blocking
+ // current thread
+ #[test]
+ #[cfg(feature = "reqwest-blocking-client")]
+ pub fn logs_batch_non_tokio_main_with_init_logs_inside_rt() -> Result<()> {
+ logs_non_tokio_helper(false, true)
}
-}
-pub fn assert_logs_results(result: &str, expected: &str) -> Result<()> {
- let left = read_logs_from_json(File::open(expected)?)?;
- let right = read_logs_from_json(File::open(result)?)?;
+ // **Simple Processor**
- LogsAsserter::new(left, right).assert();
+ // logger initialization - Inside RT
+ // log emission - Outside RT
+ // Client - Tonic, Reqwest-blocking
+ #[test]
+ #[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
+ pub fn logs_simple_non_tokio_main_with_init_logs_inside_rt() -> Result<()> {
+ logs_non_tokio_helper(true, true)
+ }
- assert!(File::open(result).unwrap().metadata().unwrap().size() > 0);
- Ok(())
-}
+ // logger initialization - Inside RT
+ // log emission - Outside RT
+ // Client - reqwest, hyper
+ #[ignore] // request and hyper client does not work without tokio runtime
+ #[test]
+ #[cfg(any(feature = "reqwest-client", feature = "hyper-client"))]
+ pub fn logs_simple_non_tokio_main_with_init_logs_inside_rt() -> Result<()> {
+ logs_non_tokio_helper(true, true)
+ }
+
+ // logger initialization - Outside RT
+ // log emission - Outside RT
+ // Client - Reqwest-blocking
+ #[test]
+ #[cfg(feature = "reqwest-blocking-client")]
+ pub fn logs_simple_non_tokio_main_with_init_logs_outsie_rt() -> Result<()> {
+ logs_non_tokio_helper(true, false)
+ }
+ // logger initialization - Outside RT
+ // log emission - Outside RT
+ // Client - hyper, tonic, reqwest
+ #[ignore] // request, tonic and hyper client does not work without tokio runtime
+ #[test]
+ #[cfg(any(
+ feature = "hyper-client",
+ feature = "tonic-client",
+ feature = "reqwest-client"
+ ))]
+ pub fn logs_simple_non_tokio_main_with_init_logs_outsie_rt() -> Result<()> {
+ logs_non_tokio_helper(true, false)
+ }
+
+ // logger initialization - Inside RT
+ // log emission - Inside RT
+ // Client - reqwest-blocking
+ // Worker threads - 4
+ #[ignore] // request-blocking client does not work with tokio
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+ #[cfg(feature = "reqwest-blocking-client")]
+ pub async fn logs_simple_tokio_multi_thread() -> Result<()> {
+ logs_tokio_helper(true, false).await
+ }
+
+ // logger initialization - Inside RT
+ // log emission - Inside RT
+ // Client - Tonic, Reqwest, hyper
+ // Worker threads - 4
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+ #[cfg(any(
+ feature = "tonic-client",
+ feature = "reqwest-client",
+ feature = "hyper-client"
+ ))]
+ pub async fn logs_simple_tokio_multi_thread() -> Result<()> {
+ logs_tokio_helper(true, false).await
+ }
+
+ // logger initialization - Inside RT
+ // log emission - Inside RT
+ // Client - Tonic, Reqwest, hyper
+ // Worker threads - 1
+ #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+ #[cfg(any(
+ feature = "tonic-client",
+ feature = "reqwest-client",
+ feature = "hyper-client"
+ ))]
+ pub async fn logs_simple_tokio_multi_with_one_worker() -> Result<()> {
+ logs_tokio_helper(true, false).await
+ }
+
+ // logger initialization - Inside RT
+ // log emission - Inside RT
+ // Client - Tonic, Reqwest, hyper
+ // Current thread
+ #[ignore] // https://github.com/open-telemetry/opentelemetry-rust/issues/2539
+ #[tokio::test(flavor = "current_thread")]
+ #[cfg(any(
+ feature = "tonic-client",
+ feature = "reqwest-client",
+ feature = "hyper-client"
+ ))]
+ pub async fn logs_simple_tokio_current() -> Result<()> {
+ logs_tokio_helper(true, false).await
+ }
+}
///
/// Make sure we stop the collector container, otherwise it will sit around hogging our
/// ports and subsequent test runs will fail.
diff --git a/opentelemetry-otlp/tests/integration_test/tests/logs_serialize_deserialize.rs b/opentelemetry-otlp/tests/integration_test/tests/logs_serialize_deserialize.rs
new file mode 100644
index 0000000000..37854ba397
--- /dev/null
+++ b/opentelemetry-otlp/tests/integration_test/tests/logs_serialize_deserialize.rs
@@ -0,0 +1,64 @@
+#![cfg(unix)]
+
+use anyhow::Result;
+use ctor::dtor;
+use integration_test_runner::logs_asserter::{read_logs_from_json, LogsAsserter};
+use integration_test_runner::test_utils;
+use opentelemetry_appender_tracing::layer;
+use opentelemetry_otlp::LogExporter;
+use opentelemetry_sdk::logs::LoggerProvider;
+use opentelemetry_sdk::Resource;
+use std::fs::File;
+use std::os::unix::fs::MetadataExt;
+use tracing::info;
+use tracing_subscriber::layer::SubscriberExt;
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[cfg(feature = "tonic-client")]
+pub async fn test_logs() -> Result<()> {
+ test_utils::start_collector_container().await?;
+ test_utils::cleanup_file("./actual/logs.json"); // Ensure logs.json is empty before the test
+ let exporter_builder = LogExporter::builder().with_tonic();
+ let exporter = exporter_builder.build()?;
+ let mut logger_provider_builder = LoggerProvider::builder();
+ logger_provider_builder = logger_provider_builder.with_batch_exporter(exporter);
+ let logger_provider = logger_provider_builder
+ .with_resource(
+ Resource::builder_empty()
+ .with_service_name("logs-integration-test")
+ .build(),
+ )
+ .build();
+ let layer = layer::OpenTelemetryTracingBridge::new(&logger_provider);
+ let subscriber = tracing_subscriber::registry().with(layer);
+
+ {
+ let _guard = tracing::subscriber::set_default(subscriber);
+ info!(target: "my-target", "hello from {}. My price is {}.", "banana", 2.99);
+ }
+
+ let _ = logger_provider.shutdown();
+ tokio::time::sleep(std::time::Duration::from_secs(5)).await;
+ assert_logs_results(test_utils::LOGS_FILE, "expected/logs.json")?;
+ Ok(())
+}
+
+fn assert_logs_results(result: &str, expected: &str) -> Result<()> {
+ let left = read_logs_from_json(File::open(expected)?)?;
+ let right = read_logs_from_json(File::open(result)?)?;
+
+ LogsAsserter::new(left, right).assert();
+
+ assert!(File::open(result).unwrap().metadata().unwrap().size() > 0);
+ Ok(())
+}
+
+///
+/// Make sure we stop the collector container, otherwise it will sit around hogging our
+/// ports and subsequent test runs will fail.
+///
+#[dtor]
+fn shutdown() {
+ println!("metrics::shutdown");
+ test_utils::stop_collector_container();
+}
diff --git a/opentelemetry-otlp/tests/integration_test/tests/metrics.rs b/opentelemetry-otlp/tests/integration_test/tests/metrics.rs
index 125c501e14..311ddbfae7 100644
--- a/opentelemetry-otlp/tests/integration_test/tests/metrics.rs
+++ b/opentelemetry-otlp/tests/integration_test/tests/metrics.rs
@@ -189,9 +189,8 @@ pub fn validate_metrics_against_results(scope_name: &str) -> Result<()> {
/// TODO - fix this asynchronously.
///
#[cfg(test)]
-#[cfg(not(feature = "hyper-client"))]
-#[cfg(not(feature = "reqwest-client"))]
-mod tests {
+#[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
+mod metrictests {
use super::*;
use opentelemetry::metrics::MeterProvider;
@@ -246,7 +245,6 @@ mod tests {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
- // #[ignore] // skip when running unit test
async fn test_histogram() -> Result<()> {
_ = setup_metrics_test().await;
const METER_NAME: &str = "test_histogram_meter";
@@ -263,7 +261,6 @@ mod tests {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
- // #[ignore] // skip when running unit test
async fn test_up_down_counter() -> Result<()> {
_ = setup_metrics_test().await;
const METER_NAME: &str = "test_up_down_meter";
diff --git a/opentelemetry-otlp/tests/integration_test/tests/traces.rs b/opentelemetry-otlp/tests/integration_test/tests/traces.rs
index e137fa1cad..65f42402e8 100644
--- a/opentelemetry-otlp/tests/integration_test/tests/traces.rs
+++ b/opentelemetry-otlp/tests/integration_test/tests/traces.rs
@@ -48,6 +48,7 @@ const LEMONS_KEY: Key = Key::from_static_str("lemons");
const ANOTHER_KEY: Key = Key::from_static_str("ex.com/another");
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[cfg(any(feature = "tonic-client", feature = "reqwest-blocking-client"))]
pub async fn traces() -> Result<()> {
test_utils::start_collector_container().await?;
diff --git a/opentelemetry-prometheus/Cargo.toml b/opentelemetry-prometheus/Cargo.toml
index fc6c2221d6..4ace0c2a84 100644
--- a/opentelemetry-prometheus/Cargo.toml
+++ b/opentelemetry-prometheus/Cargo.toml
@@ -20,19 +20,19 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
-once_cell = { workspace = true }
+once_cell = { version = "1.13" }
opentelemetry = { version = "0.27", default-features = false, features = ["metrics"] }
opentelemetry_sdk = { version = "0.27", default-features = false, features = ["metrics"] }
prometheus = "0.13"
protobuf = "2.14"
-tracing = {workspace = true, optional = true} # optional for opentelemetry internal logging
+tracing = {version = ">=0.1.40", default-features = false, optional = true} # optional for opentelemetry internal logging
[dev-dependencies]
opentelemetry-semantic-conventions = { version = "0.27" }
-http-body-util = { workspace = true }
-hyper = { workspace = true, features = ["full"] }
-hyper-util = { workspace = true, features = ["full"] }
-tokio = { workspace = true, features = ["full"] }
+http-body-util = { version = "0.1" }
+hyper = { version = "1.3", features = ["full"] }
+hyper-util = { version = "0.1", features = ["full"] }
+tokio = { version = "1", features = ["full"] }
[features]
default = ["internal-logs"]
diff --git a/opentelemetry-proto/src/transform/logs.rs b/opentelemetry-proto/src/transform/logs.rs
index b6f28490d7..8bbefe0c65 100644
--- a/opentelemetry-proto/src/transform/logs.rs
+++ b/opentelemetry-proto/src/transform/logs.rs
@@ -12,7 +12,7 @@ pub mod tonic {
transform::common::{to_nanos, tonic::ResourceAttributesWithSchema},
};
use opentelemetry::logs::{AnyValue as LogsAnyValue, Severity};
- use opentelemetry_sdk::export::logs::LogBatch;
+ use opentelemetry_sdk::logs::LogBatch;
use std::borrow::Cow;
use std::collections::HashMap;
@@ -221,15 +221,39 @@ pub mod tonic {
mod tests {
use crate::transform::common::tonic::ResourceAttributesWithSchema;
use opentelemetry::logs::LogRecord as _;
+ use opentelemetry::logs::Logger as _;
+ use opentelemetry::logs::LoggerProvider as _;
use opentelemetry::InstrumentationScope;
- use opentelemetry_sdk::{export::logs::LogBatch, logs::LogRecord, Resource};
+ use opentelemetry_sdk::logs::LogProcessor;
+ use opentelemetry_sdk::logs::{LogResult, LoggerProvider};
+ use opentelemetry_sdk::{logs::LogBatch, logs::LogRecord, Resource};
use std::time::SystemTime;
+ #[derive(Debug)]
+ struct MockProcessor;
+
+ impl LogProcessor for MockProcessor {
+ fn emit(&self, _record: &mut LogRecord, _instrumentation: &InstrumentationScope) {}
+
+ fn force_flush(&self) -> LogResult<()> {
+ Ok(())
+ }
+
+ fn shutdown(&self) -> LogResult<()> {
+ Ok(())
+ }
+ }
+
fn create_test_log_data(
instrumentation_name: &str,
_message: &str,
) -> (LogRecord, InstrumentationScope) {
- let mut logrecord = LogRecord::default();
+ let processor = MockProcessor {};
+ let logger = LoggerProvider::builder()
+ .with_log_processor(processor)
+ .build()
+ .logger("test");
+ let mut logrecord = logger.create_log_record();
logrecord.set_timestamp(SystemTime::now());
logrecord.set_observed_timestamp(SystemTime::now());
let instrumentation =
diff --git a/opentelemetry-proto/src/transform/metrics.rs b/opentelemetry-proto/src/transform/metrics.rs
index cb135ebf83..680da03b3f 100644
--- a/opentelemetry-proto/src/transform/metrics.rs
+++ b/opentelemetry-proto/src/transform/metrics.rs
@@ -10,8 +10,8 @@ pub mod tonic {
use opentelemetry::{otel_debug, Key, Value};
use opentelemetry_sdk::metrics::data::{
- self, Exemplar as SdkExemplar, ExponentialHistogram as SdkExponentialHistogram,
- Gauge as SdkGauge, Histogram as SdkHistogram, Metric as SdkMetric,
+ Exemplar as SdkExemplar, ExponentialHistogram as SdkExponentialHistogram,
+ Gauge as SdkGauge, Histogram as SdkHistogram, Metric as SdkMetric, ResourceMetrics,
ScopeMetrics as SdkScopeMetrics, Sum as SdkSum,
};
use opentelemetry_sdk::metrics::Temporality;
@@ -110,8 +110,8 @@ pub mod tonic {
}
}
- impl From<&data::ResourceMetrics> for ExportMetricsServiceRequest {
- fn from(rm: &data::ResourceMetrics) -> Self {
+ impl From<&ResourceMetrics> for ExportMetricsServiceRequest {
+ fn from(rm: &ResourceMetrics) -> Self {
ExportMetricsServiceRequest {
resource_metrics: vec![TonicResourceMetrics {
resource: Some((&rm.resource).into()),
diff --git a/opentelemetry-proto/src/transform/trace.rs b/opentelemetry-proto/src/transform/trace.rs
index 8806af41c9..ab70bedd1e 100644
--- a/opentelemetry-proto/src/transform/trace.rs
+++ b/opentelemetry-proto/src/transform/trace.rs
@@ -8,7 +8,7 @@ pub mod tonic {
};
use opentelemetry::trace;
use opentelemetry::trace::{Link, SpanId, SpanKind};
- use opentelemetry_sdk::export::trace::SpanData;
+ use opentelemetry_sdk::trace::SpanData;
use std::collections::HashMap;
impl From for span::SpanKind {
@@ -45,8 +45,8 @@ pub mod tonic {
}
}
}
- impl From for Span {
- fn from(source_span: opentelemetry_sdk::export::trace::SpanData) -> Self {
+ impl From for Span {
+ fn from(source_span: opentelemetry_sdk::trace::SpanData) -> Self {
let span_kind: span::SpanKind = source_span.span_kind.into();
Span {
trace_id: source_span.span_context.trace_id().to_bytes().to_vec(),
@@ -198,8 +198,8 @@ mod tests {
};
use opentelemetry::InstrumentationScope;
use opentelemetry::KeyValue;
- use opentelemetry_sdk::export::trace::SpanData;
use opentelemetry_sdk::resource::Resource;
+ use opentelemetry_sdk::trace::SpanData;
use opentelemetry_sdk::trace::{SpanEvents, SpanLinks};
use std::borrow::Cow;
use std::time::{Duration, SystemTime};
diff --git a/opentelemetry-proto/src/transform/tracez.rs b/opentelemetry-proto/src/transform/tracez.rs
index 525064efc9..3147a82cbb 100644
--- a/opentelemetry-proto/src/transform/tracez.rs
+++ b/opentelemetry-proto/src/transform/tracez.rs
@@ -1,7 +1,7 @@
#[cfg(all(feature = "gen-tonic-messages", feature = "zpages"))]
mod tonic {
use opentelemetry::trace::{Event, Status};
- use opentelemetry_sdk::export::trace::SpanData;
+ use opentelemetry_sdk::trace::SpanData;
use crate::proto::tonic::{
trace::v1::{span::Event as SpanEvent, Status as SpanStatus},
diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md
index b7a3f2a0da..3d27d1ef1c 100644
--- a/opentelemetry-sdk/CHANGELOG.md
+++ b/opentelemetry-sdk/CHANGELOG.md
@@ -17,6 +17,7 @@
- *Feature*: Add `ResourceBuilder` for an easy way to create new `Resource`s
- *Breaking*: Remove `Resource::{new,empty,from_detectors,new_with_defaults,from_schema_url,merge,default}` from public api. To create Resources you should only use `Resource::builder()` or `Resource::builder_empty()`. See [#2322](https://github.com/open-telemetry/opentelemetry-rust/pull/2322) for a migration guide.
Example Usage:
+
```rust
// old
Resource::default().with_attributes([
@@ -30,6 +31,7 @@
.with_attribute(KeyValue::new("key", "value"))
.build();
```
+
- *Breaking* The LogExporter::export() method no longer requires a mutable reference to self.:
Before:
async fn export(&mut self, _batch: LogBatch<'_>) -> LogResult<()>
@@ -44,7 +46,7 @@
- *Breaking* Removed the following deprecated methods:
- `Logger::provider()` : Previously deprecated in version 0.27.1
- `Logger::instrumentation_scope()` : Previously deprecated in version 0.27.1.
- Migration Guidance:
+ Migration Guidance:
- These methods were intended for log appenders. Keep the clone of the provider handle, instead of depending on above methods.
- *Breaking* - `PeriodicReader` Updates
@@ -58,38 +60,50 @@
**`experimental_metrics_periodicreader_with_async_runtime`**.
Migration Guide:
-
- 1. *Default Implementation, requires no async runtime* (**Recommended**) The
+ 1. *Default Implementation, requires no async runtime* (**Recommended**) The
new default implementation does not require a runtime argument. Replace the
builder method accordingly:
- - *Before:*
- ```rust
- let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(exporter, runtime::Tokio).build();
- ```
- - *After:*
- ```rust
- let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(exporter).build();
+ *Before:*
+
+ ```rust
+ let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(exporter, runtime::Tokio).build();
```
+ *After:*
+
+ ```rust
+ let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(exporter).build();
+ ```
+
+ The new PeriodicReader can be used with OTLP Exporter, and supports
+ following exporter features:
+ - `grpc-tonic`: This requires `MeterProvider` to be created within a tokio
+ runtime.
+ - `reqwest-blocking-client`: Works with a regular `main` or `tokio::main`.
+
+ In other words, other clients like `reqwest` and `hyper` are not supported.
+
2. *Async Runtime Support*
If your application cannot spin up new threads or you prefer using async
runtimes, enable the
"experimental_metrics_periodicreader_with_async_runtime" feature flag and
- adjust code as below.
+ adjust code as below.
- *Before:*
+
```rust
let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(exporter, runtime::Tokio).build();
```
- *After:*
+
```rust
let reader = opentelemetry_sdk::metrics::periodic_reader_with_async_runtime::PeriodicReader::builder(exporter, runtime::Tokio).build();
- ```
+ ```
*Requirements:*
- Enable the feature flag:
- `experimental_metrics_periodicreader_with_async_runtime`.
+ `experimental_metrics_periodicreader_with_async_runtime`.
- Continue enabling one of the async runtime feature flags: `rt-tokio`,
`rt-tokio-current-thread`, or `rt-async-std`.
@@ -104,11 +118,10 @@
- Getter methods have been introduced to access field values.
This change impacts custom exporter and processor developers by requiring updates to code that directly accessed LogRecord fields. They must now use the provided getter methods (e.g., `log_record.event_name()` instead of `log_record.event_name`).
-- Upgrade the tracing crate used for internal logging to version 0.1.40 or later. This is necessary because the internal logging macros utilize the name field as
+- Upgrade the tracing crate used for internal logging to version 0.1.40 or later. This is necessary because the internal logging macros utilize the name field as
metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/open-telemetry/opentelemetry-rust/pull/2418)
-- **Breaking** [#2436](https://github.com/open-telemetry/opentelemetry-rust/pull/2436)
-
+- *Breaking* - `BatchLogProcessor` Updates [#2436](https://github.com/open-telemetry/opentelemetry-rust/pull/2436)
`BatchLogProcessor` no longer requires an async runtime by default. Instead, a dedicated
background thread is created to do the batch processing and exporting.
@@ -120,6 +133,7 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
new default implementation does not require a runtime argument. Replace the
builder method accordingly:
- *Before:*
+
```rust
let logger_provider = LoggerProvider::builder()
.with_log_processor(BatchLogProcessor::builder(exporter, runtime::Tokio).build())
@@ -127,12 +141,21 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
```
- *After:*
+
```rust
let logger_provider = LoggerProvider::builder()
.with_log_processor(BatchLogProcessor::builder(exporter).build())
.build();
```
+ The new BatchLogProcessor can be used with OTLP Exporter, and supports
+ following exporter features:
+ - `grpc-tonic`: This requires `MeterProvider` to be created within a tokio
+ runtime.
+ - `reqwest-blocking-client`: Works with a regular `main` or `tokio::main`.
+
+ In other words, other clients like `reqwest` and `hyper` are not supported.
+
2. *Async Runtime Support*
If your application cannot spin up new threads or you prefer using async
runtimes, enable the
@@ -140,6 +163,7 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
adjust code as below.
- *Before:*
+
```rust
let logger_provider = LoggerProvider::builder()
.with_log_processor(BatchLogProcessor::builder(exporter, runtime::Tokio).build())
@@ -147,6 +171,7 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
```
- *After:*
+
```rust
let logger_provider = LoggerProvider::builder()
.with_log_processor(log_processor_with_async_runtime::BatchLogProcessor::builder(exporter, runtime::Tokio).build())
@@ -155,11 +180,11 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
*Requirements:*
- Enable the feature flag:
- `experimental_logs_batch_log_processor_with_async_runtime`.
+ `experimental_logs_batch_log_processor_with_async_runtime`.
- Continue enabling one of the async runtime feature flags: `rt-tokio`,
`rt-tokio-current-thread`, or `rt-async-std`.
-- **Breaking** [#2456](https://github.com/open-telemetry/opentelemetry-rust/pull/2456)
+- *Breaking* - `BatchSpanProcessor` Updates [#2435](https://github.com/open-telemetry/opentelemetry-rust/pull/2456)
`BatchSpanProcessor` no longer requires an async runtime by default. Instead, a dedicated
background thread is created to do the batch processing and exporting.
@@ -172,6 +197,7 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
new default implementation does not require a runtime argument. Replace the
builder method accordingly:
- *Before:*
+
```rust
let tracer_provider = TracerProvider::builder()
.with_span_processor(BatchSpanProcessor::builder(exporter, runtime::Tokio).build())
@@ -179,12 +205,24 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
```
- *After:*
+
```rust
let tracer_provider = TracerProvider::builder()
.with_span_processor(BatchSpanProcessor::builder(exporter).build())
.build();
```
+ This implementation does not support multiple concurrent exports
+ (`with_max_concurrent_exports` is not supported).
+
+ The new BatchLogProcessor can be used with OTLP Exporter, and supports
+ following exporter features:
+ - `grpc-tonic`: This requires `MeterProvider` to be created within a tokio
+ runtime.
+ - `reqwest-blocking-client`: Works with a regular `main` or `tokio::main`.
+
+ In other words, other clients like `reqwest` and `hyper` are not supported.
+
2. *Async Runtime Support*
If your application cannot spin up new threads or you prefer using async
runtimes, enable the
@@ -192,6 +230,7 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
adjust code as below.
- *Before:*
+
```rust
let tracer_provider = TracerProvider::builder()
.with_span_processor(BatchSpanProcessor::builder(exporter, runtime::Tokio).build())
@@ -199,6 +238,7 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
```
- *After:*
+
```rust
let tracer_provider = TracerProvider::builder()
.with_span_processor(span_processor_with_async_runtime::BatchSpanProcessor::builder(exporter, runtime::Tokio).build())
@@ -207,13 +247,62 @@ metadata, a feature introduced in version 0.1.40. [#2418](https://github.com/ope
*Requirements:*
- Enable the feature flag:
- `experimental_trace_batch_span_processor_with_async_runtime`.
+ `experimental_trace_batch_span_processor_with_async_runtime`.
- Continue enabling one of the async runtime feature flags: `rt-tokio`,
`rt-tokio-current-thread`, or `rt-async-std`.
- Bug fix: Empty Tracer names are retained as-is instead of replacing with
"rust.opentelemetry.io/sdk/tracer"
[#2486](https://github.com/open-telemetry/opentelemetry-rust/pull/2486)
+- Update `EnvResourceDetector` to allow resource attribute values containing
+ equal signs (`"="`). [#2120](https://github.com/open-telemetry/opentelemetry-rust/pull/2120)
+
+- **Breaking** Introduced `experimental_async_runtime` feature for runtime-specific traits.
+ - Runtime-specific features (`rt-tokio`, `rt-tokio-current-thread`, and `rt-async-std`)
+ now depend on the `experimental_async_runtime` feature.
+ - For most users, no action is required. Enabling runtime features such as `rt-tokio`, `rt-tokio-current-thread`,
+ or `rt-async-std` will automatically enable the `experimental_async_runtime` feature.
+ - If you're implementing a custom runtime, you must explicitly enable the experimental_async_runtime` feature in your
+ Cargo.toml and implement the required `Runtime` traits.
+
+- Removed Metrics Cardinality Limit feature. This was originally introduced in
+[#1066](https://github.com/open-telemetry/opentelemetry-rust/pull/1066) with a
+hardcoded limit of 2000 and no ability to change it. This feature will be
+re-introduced in a future date, along with the ability to change the cardinality
+limit.
+
+- *Breaking* Removed unused `opentelemetry_sdk::Error` enum.
+- *Breaking* Resource.get() modified to require reference to Key instead of owned.
+ Replace `get(Key::from_static_str("key"))` with `get(&Key::from_static_str("key"))`
+- *Breaking* (Affects custom Exporter authors only) Moved `ExportError` trait from `opentelemetry::export::ExportError` to `opentelemetry_sdk::ExportError`
+- *Breaking (Affects custom SpanExporter, SpanProcessor authors only)*: Rename namespaces for Span exporter structs/traits
+ before:
+ `opentelemetry_sdk::export::spans::{ExportResult, SpanData, SpanExporter};`
+ now:
+ `opentelemetry_sdk::spans::{ExportResult, SpanData, SpanExporter};`
+
+- *Breaking (Affects custom LogExporter, LogProcessor authors only)*: Rename namespaces for Log exporter structs/traits.
+ before:
+ `opentelemetry_sdk::export::logs::{ExportResult, LogBatch, LogExporter};`
+ now:
+ `opentelemetry_sdk::logs::{ExportResult, LogBatch, LogExporter};`
+
+- *Breaking* `opentelemetry_sdk::LogRecord::default()` method is removed.
+ The only way to create log record outside opentelemetry_sdk crate is using
+ `Logger::create_log_record()` method.
+
+- Rename `opentelemetry_sdk::logs::Builder` to `opentelemetry_sdk::logs::LoggerProviderBuilder`.
+- Rename `opentelemetry_sdk::trace::Builder` to `opentelemetry_sdk::trace::TracerProviderBuilder`.
+
+- *Breaking*: Rename namespaces for InMemoryExporters. (The module is still under "testing" feature flag)
+ before:
+ `opentelemetry_sdk::testing::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};`
+ `opentelemetry_sdk::testing::trace::{InMemorySpanExporter, InMemorySpanExporterBuilder};`
+ `opentelemetry_sdk::testing::metrics::{InMemoryMetricExporter, InMemoryMetricExporterBuilder};`
+ now:
+ `opentelemetry_sdk::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};`
+ `opentelemetry_sdk::trace::{InMemorySpanExporter, InMemorySpanExporterBuilder};`
+ `opentelemetry_sdk::metrics::{InMemoryMetricExporter, InMemoryMetricExporterBuilder};`
## 0.27.1
@@ -223,6 +312,7 @@ Released 2024-Nov-27
- `trace::Config` methods are moving onto `TracerProvider` Builder to be consistent with other signals. See https://github.com/open-telemetry/opentelemetry-rust/pull/2303 for migration guide.
`trace::Config` is scheduled to be removed from public API in `v0.28.0`.
example:
+
```rust
// old
let tracer_provider: TracerProvider = TracerProvider::builder()
@@ -234,6 +324,7 @@ Released 2024-Nov-27
.with_resource(Resource::empty())
.build();
```
+
- `logs::LogData` struct is deprecated, and scheduled to be removed from public API in `v0.28.0`.
- Bug fix: Empty Meter names are retained as-is instead of replacing with
"rust.opentelemetry.io/sdk/meter"
@@ -242,10 +333,10 @@ Released 2024-Nov-27
- Bug fix: Empty Logger names are retained as-is instead of replacing with
"rust.opentelemetry.io/sdk/logger"
[#2316](https://github.com/open-telemetry/opentelemetry-rust/pull/2316)
-
+
- `Logger::provider`: This method is deprecated as of version `0.27.1`. To be removed in `0.28.0`.
- `Logger::instrumentation_scope`: This method is deprecated as of version `0.27.1`. To be removed in `0.28.0`
- Migration Guidance:
+ Migration Guidance:
- These methods are intended for log appenders. Keep the clone of the provider handle, instead of depending on above methods.
@@ -271,7 +362,7 @@ Released 2024-Nov-11
- **Replaced**
- ([#2217](https://github.com/open-telemetry/opentelemetry-rust/pull/2217)): Removed `{Delta,Cumulative}TemporalitySelector::new()` in favor of directly using `Temporality` enum to simplify the configuration of MetricsExporterBuilder with different temporalities.
- **Renamed**
- - ([#2232](https://github.com/open-telemetry/opentelemetry-rust/pull/2232)): The `init` method used to create instruments has been renamed to `build`.
+ - ([#2232](https://github.com/open-telemetry/opentelemetry-rust/pull/2232)): The `init` method used to create instruments has been renamed to `build`.
Before:
```rust
let counter = meter.u64_counter("my_counter").init();
diff --git a/opentelemetry-sdk/Cargo.toml b/opentelemetry-sdk/Cargo.toml
index 167846dce1..8d4d6da1ed 100644
--- a/opentelemetry-sdk/Cargo.toml
+++ b/opentelemetry-sdk/Cargo.toml
@@ -39,7 +39,7 @@ rstest = "0.23.0"
temp-env = { workspace = true }
[target.'cfg(not(target_os = "windows"))'.dev-dependencies]
-pprof = { version = "0.13", features = ["flamegraph", "criterion"] }
+pprof = { version = "0.14", features = ["flamegraph", "criterion"] }
[features]
default = ["trace", "metrics", "logs", "internal-logs"]
@@ -49,9 +49,10 @@ logs = ["opentelemetry/logs", "serde_json"]
spec_unstable_logs_enabled = ["logs", "opentelemetry/spec_unstable_logs_enabled"]
metrics = ["opentelemetry/metrics", "glob", "async-trait"]
testing = ["opentelemetry/testing", "trace", "metrics", "logs", "rt-async-std", "rt-tokio", "rt-tokio-current-thread", "tokio/macros", "tokio/rt-multi-thread"]
-rt-tokio = ["tokio", "tokio-stream"]
-rt-tokio-current-thread = ["tokio", "tokio-stream"]
-rt-async-std = ["async-std"]
+experimental_async_runtime = []
+rt-tokio = ["tokio", "tokio-stream", "experimental_async_runtime"]
+rt-tokio-current-thread = ["tokio", "tokio-stream", "experimental_async_runtime"]
+rt-async-std = ["async-std", "experimental_async_runtime"]
internal-logs = ["tracing"]
experimental_metrics_periodicreader_with_async_runtime = ["metrics"]
spec_unstable_metrics_views = ["metrics"]
diff --git a/opentelemetry-sdk/benches/batch_span_processor.rs b/opentelemetry-sdk/benches/batch_span_processor.rs
index d57ef26157..7cd9ff5c1c 100644
--- a/opentelemetry-sdk/benches/batch_span_processor.rs
+++ b/opentelemetry-sdk/benches/batch_span_processor.rs
@@ -2,8 +2,8 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use opentelemetry::trace::{
SpanContext, SpanId, SpanKind, Status, TraceFlags, TraceId, TraceState,
};
-use opentelemetry_sdk::export::trace::SpanData;
use opentelemetry_sdk::testing::trace::NoopSpanExporter;
+use opentelemetry_sdk::trace::SpanData;
use opentelemetry_sdk::trace::{
BatchConfigBuilder, BatchSpanProcessor, SpanEvents, SpanLinks, SpanProcessor,
};
diff --git a/opentelemetry-sdk/benches/context.rs b/opentelemetry-sdk/benches/context.rs
index 87f49942a6..8a9108639a 100644
--- a/opentelemetry-sdk/benches/context.rs
+++ b/opentelemetry-sdk/benches/context.rs
@@ -9,7 +9,7 @@ use opentelemetry::{
Context, ContextGuard,
};
use opentelemetry_sdk::{
- export::trace::{ExportResult, SpanData, SpanExporter},
+ trace::{ExportResult, SpanData, SpanExporter},
trace::{Sampler, TracerProvider},
};
#[cfg(not(target_os = "windows"))]
diff --git a/opentelemetry-sdk/benches/log_exporter.rs b/opentelemetry-sdk/benches/log_exporter.rs
index c2ecb78ce9..523725fc7e 100644
--- a/opentelemetry-sdk/benches/log_exporter.rs
+++ b/opentelemetry-sdk/benches/log_exporter.rs
@@ -20,7 +20,7 @@ use opentelemetry::logs::{LogRecord as _, Logger as _, LoggerProvider as _, Seve
use opentelemetry_sdk::logs::LogResult;
use opentelemetry::InstrumentationScope;
-use opentelemetry_sdk::export::logs::LogBatch;
+use opentelemetry_sdk::logs::LogBatch;
use opentelemetry_sdk::logs::LogProcessor;
use opentelemetry_sdk::logs::LogRecord;
use opentelemetry_sdk::logs::LoggerProvider;
diff --git a/opentelemetry-sdk/benches/span_builder.rs b/opentelemetry-sdk/benches/span_builder.rs
index 6f0c828b07..7f78b738f9 100644
--- a/opentelemetry-sdk/benches/span_builder.rs
+++ b/opentelemetry-sdk/benches/span_builder.rs
@@ -5,8 +5,8 @@ use opentelemetry::{
KeyValue,
};
use opentelemetry_sdk::{
- export::trace::{ExportResult, SpanData, SpanExporter},
trace as sdktrace,
+ trace::{ExportResult, SpanData, SpanExporter},
};
#[cfg(not(target_os = "windows"))]
use pprof::criterion::{Output, PProfProfiler};
diff --git a/opentelemetry-sdk/benches/trace.rs b/opentelemetry-sdk/benches/trace.rs
index 36f6acec4a..bdbd4a5ee3 100644
--- a/opentelemetry-sdk/benches/trace.rs
+++ b/opentelemetry-sdk/benches/trace.rs
@@ -5,8 +5,8 @@ use opentelemetry::{
KeyValue,
};
use opentelemetry_sdk::{
- export::trace::{ExportResult, SpanData, SpanExporter},
trace as sdktrace,
+ trace::{ExportResult, SpanData, SpanExporter},
};
#[cfg(not(target_os = "windows"))]
use pprof::criterion::{Output, PProfProfiler};
diff --git a/opentelemetry-sdk/src/error.rs b/opentelemetry-sdk/src/error.rs
index 115da17b78..6a108f0cc9 100644
--- a/opentelemetry-sdk/src/error.rs
+++ b/opentelemetry-sdk/src/error.rs
@@ -1,46 +1,7 @@
//! Wrapper for error from trace, logs and metrics part of open telemetry.
-use std::sync::PoisonError;
-#[cfg(feature = "logs")]
-use crate::logs::LogError;
-#[cfg(feature = "metrics")]
-use crate::metrics::MetricError;
-use opentelemetry::propagation::PropagationError;
-#[cfg(feature = "trace")]
-use opentelemetry::trace::TraceError;
-
-/// Wrapper for error from both tracing and metrics part of open telemetry.
-#[derive(thiserror::Error, Debug)]
-#[non_exhaustive]
-pub enum Error {
- #[cfg(feature = "trace")]
- #[cfg_attr(docsrs, doc(cfg(feature = "trace")))]
- #[error(transparent)]
- /// Failed to export traces.
- Trace(#[from] TraceError),
- #[cfg(feature = "metrics")]
- #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
- #[error(transparent)]
- /// An issue raised by the metrics module.
- Metric(#[from] MetricError),
-
- #[cfg(feature = "logs")]
- #[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
- #[error(transparent)]
- /// Failed to export logs.
- Log(#[from] LogError),
-
- #[error(transparent)]
- /// Error happens when injecting and extracting information using propagators.
- Propagation(#[from] PropagationError),
-
- #[error("{0}")]
- /// Other types of failures not covered by the variants above.
- Other(String),
-}
-
-impl From> for Error {
- fn from(err: PoisonError) -> Self {
- Error::Other(err.to_string())
- }
+/// Trait for errors returned by exporters
+pub trait ExportError: std::error::Error + Send + Sync + 'static {
+ /// The name of exporter that returned this error
+ fn exporter_name(&self) -> &'static str;
}
diff --git a/opentelemetry-sdk/src/export/mod.rs b/opentelemetry-sdk/src/export/mod.rs
deleted file mode 100644
index 21dc2b570c..0000000000
--- a/opentelemetry-sdk/src/export/mod.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//! Telemetry Export
-
-#[cfg(feature = "logs")]
-#[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
-pub mod logs;
-
-#[cfg(feature = "trace")]
-#[cfg_attr(docsrs, doc(cfg(feature = "trace")))]
-pub mod trace;
-
-/// Trait for errors returned by exporters
-pub trait ExportError: std::error::Error + Send + Sync + 'static {
- /// The name of exporter that returned this error
- fn exporter_name(&self) -> &'static str;
-}
diff --git a/opentelemetry-sdk/src/lib.rs b/opentelemetry-sdk/src/lib.rs
index 4afda7deb7..bc57acff9f 100644
--- a/opentelemetry-sdk/src/lib.rs
+++ b/opentelemetry-sdk/src/lib.rs
@@ -16,7 +16,7 @@
//!
//! fn main() {
//! // Choose an exporter like `opentelemetry_stdout::SpanExporter`
-//! # fn example(new_exporter: impl Fn() -> T) {
+//! # fn example(new_exporter: impl Fn() -> T) {
//! let exporter = new_exporter();
//!
//! // Create a new trace pipeline that prints to stdout
@@ -44,10 +44,7 @@
//! [examples]: https://github.com/open-telemetry/opentelemetry-rust/tree/main/examples
//! [`trace`]: https://docs.rs/opentelemetry/latest/opentelemetry/trace/index.html
//!
-//! # Metrics (Alpha)
-//!
-//! Note: the metrics implementation is **still in progress** and **subject to major
-//! changes**.
+//! # Metrics
//!
//! ### Creating instruments and recording measurements
//!
@@ -94,6 +91,7 @@
//! Support for recording and exporting telemetry asynchronously and perform
//! metrics aggregation can be added via the following flags:
//!
+//! * `experimental_async_runtime`: Enables the experimental `Runtime` trait and related functionality.
//! * `rt-tokio`: Spawn telemetry tasks using [tokio]'s multi-thread runtime.
//! * `rt-tokio-current-thread`: Spawn telemetry tasks on a separate runtime so that the main runtime won't be blocked.
//! * `rt-async-std`: Spawn telemetry tasks using [async-std]'s runtime.
@@ -120,7 +118,6 @@
)]
#![cfg_attr(test, deny(warnings))]
-pub mod export;
pub(crate) mod growable_array;
#[cfg(feature = "logs")]
@@ -133,6 +130,7 @@ pub mod metrics;
#[cfg_attr(docsrs, doc(cfg(feature = "trace")))]
pub mod propagation;
pub mod resource;
+#[cfg(feature = "experimental_async_runtime")]
pub mod runtime;
#[cfg(any(feature = "testing", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "testing", test))))]
@@ -150,3 +148,4 @@ pub mod util;
pub use resource::Resource;
pub mod error;
+pub use error::ExportError;
diff --git a/opentelemetry-sdk/src/logs/error.rs b/opentelemetry-sdk/src/logs/error.rs
index 4f33ba6dbf..d9b1b42157 100644
--- a/opentelemetry-sdk/src/logs/error.rs
+++ b/opentelemetry-sdk/src/logs/error.rs
@@ -1,4 +1,4 @@
-use crate::export::ExportError;
+use crate::ExportError;
use std::{sync::PoisonError, time::Duration};
use thiserror::Error;
diff --git a/opentelemetry-sdk/src/export/logs/mod.rs b/opentelemetry-sdk/src/logs/export.rs
similarity index 100%
rename from opentelemetry-sdk/src/export/logs/mod.rs
rename to opentelemetry-sdk/src/logs/export.rs
diff --git a/opentelemetry-sdk/src/testing/logs/in_memory_exporter.rs b/opentelemetry-sdk/src/logs/in_memory_exporter.rs
similarity index 93%
rename from opentelemetry-sdk/src/testing/logs/in_memory_exporter.rs
rename to opentelemetry-sdk/src/logs/in_memory_exporter.rs
index dff6d93c7e..0df3a0caeb 100644
--- a/opentelemetry-sdk/src/testing/logs/in_memory_exporter.rs
+++ b/opentelemetry-sdk/src/logs/in_memory_exporter.rs
@@ -1,6 +1,5 @@
-use crate::export::logs::{LogBatch, LogExporter};
use crate::logs::LogRecord;
-use crate::logs::{LogError, LogResult};
+use crate::logs::{LogBatch, LogError, LogExporter, LogResult};
use crate::Resource;
use opentelemetry::InstrumentationScope;
use std::borrow::Cow;
@@ -16,7 +15,7 @@ use std::sync::{Arc, Mutex};
/// ```no_run
///# use opentelemetry_sdk::logs::{BatchLogProcessor, LoggerProvider};
///# use opentelemetry_sdk::runtime;
-///# use opentelemetry_sdk::testing::logs::InMemoryLogExporter;
+///# use opentelemetry_sdk::logs::InMemoryLogExporter;
///
///# #[tokio::main]
///# async fn main() {
@@ -73,7 +72,7 @@ pub struct LogDataWithResource {
/// # Example
///
/// ```no_run
-///# use opentelemetry_sdk::testing::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
+///# use opentelemetry_sdk::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
///# use opentelemetry_sdk::logs::{BatchLogProcessor, LoggerProvider};
///# use opentelemetry_sdk::runtime;
///
@@ -140,7 +139,7 @@ impl InMemoryLogExporter {
/// # Example
///
/// ```
- /// use opentelemetry_sdk::testing::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
+ /// use opentelemetry_sdk::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
///
/// let exporter = InMemoryLogExporterBuilder::default().build();
/// let emitted_logs = exporter.get_emitted_logs().unwrap();
@@ -165,7 +164,7 @@ impl InMemoryLogExporter {
/// # Example
///
/// ```
- /// use opentelemetry_sdk::testing::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
+ /// use opentelemetry_sdk::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
///
/// let exporter = InMemoryLogExporterBuilder::default().build();
/// exporter.reset();
diff --git a/opentelemetry-sdk/src/logs/log_processor.rs b/opentelemetry-sdk/src/logs/log_processor.rs
index c70654ef56..222c9e186b 100644
--- a/opentelemetry-sdk/src/logs/log_processor.rs
+++ b/opentelemetry-sdk/src/logs/log_processor.rs
@@ -32,8 +32,7 @@
//! ```
use crate::{
- export::logs::{ExportResult, LogBatch, LogExporter},
- logs::{LogError, LogRecord, LogResult},
+ logs::{ExportResult, LogBatch, LogError, LogExporter, LogRecord, LogResult},
Resource,
};
use std::sync::mpsc::{self, RecvTimeoutError, SyncSender};
@@ -104,16 +103,26 @@ pub trait LogProcessor: Send + Sync + Debug {
}
/// A [`LogProcessor`] designed for testing and debugging purpose, that immediately
-/// exports log records as they are emitted.
+/// exports log records as they are emitted. Log records are exported synchronously
+/// in the same thread that emits the log record.
+/// When using this processor with the OTLP Exporter, the following exporter
+/// features are supported:
+/// - `grpc-tonic`: This requires LoggerProvider to be created within a tokio
+/// runtime. Logs can be emitted from any thread, including tokio runtime
+/// threads.
+/// - `reqwest-blocking-client`: LoggerProvider may be created anywhere, but
+/// logs must be emitted from a non-tokio runtime thread.
+/// - `reqwest-client`: LoggerProvider may be created anywhere, but logs must be
+/// emitted from a tokio runtime thread.
+///
/// ## Example
///
/// ### Using a SimpleLogProcessor
///
/// ```rust
-/// use opentelemetry_sdk::logs::{SimpleLogProcessor, LoggerProvider};
+/// use opentelemetry_sdk::logs::{SimpleLogProcessor, LoggerProvider, LogExporter};
/// use opentelemetry::global;
-/// use opentelemetry_sdk::export::logs::LogExporter;
-/// use opentelemetry_sdk::testing::logs::InMemoryLogExporter;
+/// use opentelemetry_sdk::logs::InMemoryLogExporter;
///
/// let exporter = InMemoryLogExporter::default(); // Replace with an actual exporter
/// let provider = LoggerProvider::builder()
@@ -223,13 +232,21 @@ type LogsData = Box<(LogRecord, InstrumentationScope)>;
/// - **Export timeout**: Maximum duration allowed for an export operation.
/// - **Scheduled delay**: Frequency at which the batch is exported.
///
+/// When using this processor with the OTLP Exporter, the following exporter
+/// features are supported:
+/// - `grpc-tonic`: This requires `MeterProvider` to be created within a tokio
+/// runtime.
+/// - `reqwest-blocking-client`: Works with a regular `main` or `tokio::main`.
+///
+/// In other words, other clients like `reqwest` and `hyper` are not supported.
+///
/// ### Using a BatchLogProcessor:
///
/// ```rust
/// use opentelemetry_sdk::logs::{BatchLogProcessor, BatchConfigBuilder, LoggerProvider};
/// use opentelemetry::global;
/// use std::time::Duration;
-/// use opentelemetry_sdk::testing::logs::InMemoryLogExporter;
+/// use opentelemetry_sdk::logs::InMemoryLogExporter;
///
/// let exporter = InMemoryLogExporter::default(); // Replace with an actual exporter
/// let processor = BatchLogProcessor::builder(exporter)
@@ -799,19 +816,17 @@ mod tests {
BatchLogProcessor, OTEL_BLRP_EXPORT_TIMEOUT, OTEL_BLRP_MAX_EXPORT_BATCH_SIZE,
OTEL_BLRP_MAX_QUEUE_SIZE, OTEL_BLRP_SCHEDULE_DELAY,
};
- use crate::export::logs::{LogBatch, LogExporter};
- use crate::logs::LogRecord;
use crate::logs::LogResult;
- use crate::testing::logs::InMemoryLogExporterBuilder;
+ use crate::logs::{LogBatch, LogExporter, LogRecord};
use crate::{
logs::{
log_processor::{
OTEL_BLRP_EXPORT_TIMEOUT_DEFAULT, OTEL_BLRP_MAX_EXPORT_BATCH_SIZE_DEFAULT,
OTEL_BLRP_MAX_QUEUE_SIZE_DEFAULT, OTEL_BLRP_SCHEDULE_DELAY_DEFAULT,
},
- BatchConfig, BatchConfigBuilder, LogProcessor, LoggerProvider, SimpleLogProcessor,
+ BatchConfig, BatchConfigBuilder, InMemoryLogExporter, InMemoryLogExporterBuilder,
+ LogProcessor, LoggerProvider, SimpleLogProcessor,
},
- testing::logs::InMemoryLogExporter,
Resource,
};
use opentelemetry::logs::AnyValue;
@@ -1064,7 +1079,7 @@ mod tests {
.build();
let processor = BatchLogProcessor::new(exporter.clone(), BatchConfig::default());
- let mut record = LogRecord::default();
+ let mut record = LogRecord::new();
let instrumentation = InstrumentationScope::default();
processor.emit(&mut record, &instrumentation);
@@ -1082,7 +1097,7 @@ mod tests {
.build();
let processor = SimpleLogProcessor::new(exporter.clone());
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor.emit(&mut record, &instrumentation);
@@ -1240,7 +1255,7 @@ mod tests {
let exporter = InMemoryLogExporterBuilder::default().build();
let processor = SimpleLogProcessor::new(exporter.clone());
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor.emit(&mut record, &instrumentation);
@@ -1253,7 +1268,7 @@ mod tests {
let exporter = InMemoryLogExporterBuilder::default().build();
let processor = SimpleLogProcessor::new(exporter.clone());
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor.emit(&mut record, &instrumentation);
@@ -1270,7 +1285,7 @@ mod tests {
for _ in 0..10 {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor_clone.emit(&mut record, &instrumentation);
});
@@ -1289,7 +1304,7 @@ mod tests {
let exporter = InMemoryLogExporterBuilder::default().build();
let processor = SimpleLogProcessor::new(exporter.clone());
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor.emit(&mut record, &instrumentation);
@@ -1341,7 +1356,7 @@ mod tests {
let exporter = LogExporterThatRequiresTokio::new();
let processor = SimpleLogProcessor::new(exporter.clone());
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
// This will panic because an tokio async operation within exporter without a runtime.
@@ -1397,7 +1412,7 @@ mod tests {
for _ in 0..concurrent_emit {
let processor_clone = Arc::clone(&processor);
let handle = tokio::spawn(async move {
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor_clone.emit(&mut record, &instrumentation);
});
@@ -1421,7 +1436,7 @@ mod tests {
let exporter = LogExporterThatRequiresTokio::new();
let processor = SimpleLogProcessor::new(exporter.clone());
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor.emit(&mut record, &instrumentation);
@@ -1440,7 +1455,7 @@ mod tests {
let processor = SimpleLogProcessor::new(exporter.clone());
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor.emit(&mut record, &instrumentation);
@@ -1460,7 +1475,7 @@ mod tests {
let processor = SimpleLogProcessor::new(exporter.clone());
- let mut record: LogRecord = Default::default();
+ let mut record: LogRecord = LogRecord::new();
let instrumentation: InstrumentationScope = Default::default();
processor.emit(&mut record, &instrumentation);
diff --git a/opentelemetry-sdk/src/logs/log_processor_with_async_runtime.rs b/opentelemetry-sdk/src/logs/log_processor_with_async_runtime.rs
index b5a3df2197..765d1ebd98 100644
--- a/opentelemetry-sdk/src/logs/log_processor_with_async_runtime.rs
+++ b/opentelemetry-sdk/src/logs/log_processor_with_async_runtime.rs
@@ -1,6 +1,5 @@
use crate::{
- export::logs::{ExportResult, LogBatch, LogExporter},
- logs::{LogError, LogRecord, LogResult},
+ logs::{ExportResult, LogBatch, LogError, LogExporter, LogRecord, LogResult},
Resource,
};
@@ -282,25 +281,25 @@ where
#[cfg(all(test, feature = "testing", feature = "logs"))]
mod tests {
- use crate::export::logs::{LogBatch, LogExporter};
use crate::logs::log_processor::{
OTEL_BLRP_EXPORT_TIMEOUT, OTEL_BLRP_MAX_EXPORT_BATCH_SIZE, OTEL_BLRP_MAX_QUEUE_SIZE,
OTEL_BLRP_SCHEDULE_DELAY,
};
use crate::logs::log_processor_with_async_runtime::BatchLogProcessor;
+ use crate::logs::InMemoryLogExporterBuilder;
use crate::logs::LogRecord;
use crate::logs::LogResult;
+ use crate::logs::{LogBatch, LogExporter};
use crate::runtime;
- use crate::testing::logs::InMemoryLogExporterBuilder;
use crate::{
logs::{
log_processor::{
OTEL_BLRP_EXPORT_TIMEOUT_DEFAULT, OTEL_BLRP_MAX_EXPORT_BATCH_SIZE_DEFAULT,
OTEL_BLRP_MAX_QUEUE_SIZE_DEFAULT, OTEL_BLRP_SCHEDULE_DELAY_DEFAULT,
},
- BatchConfig, BatchConfigBuilder, LogProcessor, LoggerProvider, SimpleLogProcessor,
+ BatchConfig, BatchConfigBuilder, InMemoryLogExporter, LogProcessor, LoggerProvider,
+ SimpleLogProcessor,
},
- testing::logs::InMemoryLogExporter,
Resource,
};
use opentelemetry::logs::AnyValue;
@@ -556,7 +555,7 @@ mod tests {
let processor =
BatchLogProcessor::new(exporter.clone(), BatchConfig::default(), runtime::Tokio);
- let mut record = LogRecord::default();
+ let mut record = LogRecord::new();
let instrumentation = InstrumentationScope::default();
processor.emit(&mut record, &instrumentation);
@@ -818,7 +817,7 @@ mod tests {
let processor =
BatchLogProcessor::new(exporter.clone(), BatchConfig::default(), runtime::Tokio);
- let mut record = LogRecord::default();
+ let mut record = LogRecord::new();
let instrumentation = InstrumentationScope::default();
processor.emit(&mut record, &instrumentation);
diff --git a/opentelemetry-sdk/src/logs/log_emitter.rs b/opentelemetry-sdk/src/logs/logger_provider.rs
similarity index 93%
rename from opentelemetry-sdk/src/logs/log_emitter.rs
rename to opentelemetry-sdk/src/logs/logger_provider.rs
index ba63896277..6287387d1b 100644
--- a/opentelemetry-sdk/src/logs/log_emitter.rs
+++ b/opentelemetry-sdk/src/logs/logger_provider.rs
@@ -1,12 +1,12 @@
use super::{BatchLogProcessor, LogProcessor, LogRecord, SimpleLogProcessor, TraceContext};
-use crate::{export::logs::LogExporter, Resource};
-use crate::{logs::LogError, logs::LogResult};
+use crate::logs::{LogError, LogExporter, LogResult};
+use crate::Resource;
use opentelemetry::{otel_debug, otel_info, trace::TraceContextExt, Context, InstrumentationScope};
#[cfg(feature = "spec_unstable_logs_enabled")]
use opentelemetry::logs::Severity;
-use std::time::SystemTime;
+use opentelemetry::time::now;
use std::{
borrow::Cow,
sync::{
@@ -78,8 +78,8 @@ impl opentelemetry::logs::LoggerProvider for LoggerProvider {
impl LoggerProvider {
/// Create a new `LoggerProvider` builder.
- pub fn builder() -> Builder {
- Builder::default()
+ pub fn builder() -> LoggerProviderBuilder {
+ LoggerProviderBuilder::default()
}
pub(crate) fn log_processors(&self) -> &[Box] {
@@ -179,37 +179,67 @@ impl Drop for LoggerProviderInner {
#[derive(Debug, Default)]
/// Builder for provider attributes.
-pub struct Builder {
+pub struct LoggerProviderBuilder {
processors: Vec>,
resource: Option,
}
-impl Builder {
- /// The `LogExporter` that this provider should use.
+impl LoggerProviderBuilder {
+ /// Adds a [SimpleLogProcessor] with the configured exporter to the pipeline.
+ ///
+ /// # Arguments
+ ///
+ /// * `exporter` - The exporter to be used by the SimpleLogProcessor.
+ ///
+ /// # Returns
+ ///
+ /// A new `Builder` instance with the SimpleLogProcessor added to the pipeline.
+ ///
+ /// Processors are invoked in the order they are added.
pub fn with_simple_exporter(self, exporter: T) -> Self {
let mut processors = self.processors;
processors.push(Box::new(SimpleLogProcessor::new(exporter)));
- Builder { processors, ..self }
+ LoggerProviderBuilder { processors, ..self }
}
- /// The `LogExporter` setup using a default `BatchLogProcessor` that this provider should use.
+ /// Adds a [BatchLogProcessor] with the configured exporter to the pipeline.
+ ///
+ /// # Arguments
+ ///
+ /// * `exporter` - The exporter to be used by the BatchLogProcessor.
+ ///
+ /// # Returns
+ ///
+ /// A new `Builder` instance with the BatchLogProcessor added to the pipeline.
+ ///
+ /// Processors are invoked in the order they are added.
pub fn with_batch_exporter(self, exporter: T) -> Self {
let batch = BatchLogProcessor::builder(exporter).build();
self.with_log_processor(batch)
}
- /// The `LogProcessor` that this provider should use.
+ /// Adds a custom [LogProcessor] to the pipeline.
+ ///
+ /// # Arguments
+ ///
+ /// * `processor` - The `LogProcessor` to be added.
+ ///
+ /// # Returns
+ ///
+ /// A new `Builder` instance with the custom `LogProcessor` added to the pipeline.
+ ///
+ /// Processors are invoked in the order they are added.
pub fn with_log_processor(self, processor: T) -> Self {
let mut processors = self.processors;
processors.push(Box::new(processor));
- Builder { processors, ..self }
+ LoggerProviderBuilder { processors, ..self }
}
/// The `Resource` to be associated with this Provider.
pub fn with_resource(self, resource: Resource) -> Self {
- Builder {
+ LoggerProviderBuilder {
resource: Some(resource),
..self
}
@@ -263,7 +293,7 @@ impl opentelemetry::logs::Logger for Logger {
type LogRecord = LogRecord;
fn create_log_record(&self) -> Self::LogRecord {
- LogRecord::default()
+ LogRecord::new()
}
/// Emit a `LogRecord`.
@@ -283,7 +313,7 @@ impl opentelemetry::logs::Logger for Logger {
}
}
if record.observed_timestamp.is_none() {
- record.observed_timestamp = Some(SystemTime::now());
+ record.observed_timestamp = Some(now());
}
for p in processors {
@@ -293,23 +323,20 @@ impl opentelemetry::logs::Logger for Logger {
#[cfg(feature = "spec_unstable_logs_enabled")]
fn event_enabled(&self, level: Severity, target: &str) -> bool {
- let provider = &self.provider;
-
- let mut enabled = false;
- for processor in provider.log_processors() {
- enabled = enabled || processor.event_enabled(level, target, self.scope.name().as_ref());
- }
- enabled
+ self.provider
+ .log_processors()
+ .iter()
+ .any(|processor| processor.event_enabled(level, target, self.scope.name().as_ref()))
}
}
#[cfg(test)]
mod tests {
use crate::{
+ logs::InMemoryLogExporter,
resource::{
SERVICE_NAME, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_NAME, TELEMETRY_SDK_VERSION,
},
- testing::logs::InMemoryLogExporter,
trace::TracerProvider,
Resource,
};
@@ -376,22 +403,22 @@ mod tests {
assert_eq!(
provider
.resource()
- .get(Key::from_static_str(resource_key))
+ .get(&Key::from_static_str(resource_key))
.map(|v| v.to_string()),
expect.map(|s| s.to_string())
);
};
let assert_telemetry_resource = |provider: &super::LoggerProvider| {
assert_eq!(
- provider.resource().get(TELEMETRY_SDK_LANGUAGE.into()),
+ provider.resource().get(&TELEMETRY_SDK_LANGUAGE.into()),
Some(Value::from("rust"))
);
assert_eq!(
- provider.resource().get(TELEMETRY_SDK_NAME.into()),
+ provider.resource().get(&TELEMETRY_SDK_NAME.into()),
Some(Value::from("opentelemetry"))
);
assert_eq!(
- provider.resource().get(TELEMETRY_SDK_VERSION.into()),
+ provider.resource().get(&TELEMETRY_SDK_VERSION.into()),
Some(Value::from(env!("CARGO_PKG_VERSION")))
);
};
diff --git a/opentelemetry-sdk/src/logs/mod.rs b/opentelemetry-sdk/src/logs/mod.rs
index 97ae74ee85..ce546fc938 100644
--- a/opentelemetry-sdk/src/logs/mod.rs
+++ b/opentelemetry-sdk/src/logs/mod.rs
@@ -1,15 +1,25 @@
//! # OpenTelemetry Log SDK
mod error;
-mod log_emitter;
+mod export;
mod log_processor;
+mod logger_provider;
pub(crate) mod record;
+/// In-Memory log exporter for testing purpose.
+#[cfg(any(feature = "testing", test))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "testing", test))))]
+pub mod in_memory_exporter;
+#[cfg(any(feature = "testing", test))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "testing", test))))]
+pub use in_memory_exporter::{InMemoryLogExporter, InMemoryLogExporterBuilder};
+
pub use error::{LogError, LogResult};
-pub use log_emitter::{Builder, Logger, LoggerProvider};
+pub use export::{ExportResult, LogBatch, LogExporter};
pub use log_processor::{
BatchConfig, BatchConfigBuilder, BatchLogProcessor, BatchLogProcessorBuilder, LogProcessor,
SimpleLogProcessor,
};
+pub use logger_provider::{Logger, LoggerProvider, LoggerProviderBuilder};
pub use record::{LogRecord, TraceContext};
#[cfg(feature = "experimental_logs_batch_log_processor_with_async_runtime")]
@@ -19,7 +29,6 @@ pub mod log_processor_with_async_runtime;
#[cfg(all(test, feature = "testing"))]
mod tests {
use super::*;
- use crate::testing::logs::InMemoryLogExporter;
use crate::Resource;
use opentelemetry::logs::LogRecord;
use opentelemetry::logs::{Logger, LoggerProvider as _, Severity};
diff --git a/opentelemetry-sdk/src/logs/record.rs b/opentelemetry-sdk/src/logs/record.rs
index 3e4f5c8c18..740ee14c97 100644
--- a/opentelemetry-sdk/src/logs/record.rs
+++ b/opentelemetry-sdk/src/logs/record.rs
@@ -18,7 +18,7 @@ const PREALLOCATED_ATTRIBUTE_CAPACITY: usize = 5;
pub(crate) type LogRecordAttributes =
GrowableArray