From 6acf5fdace2f4c5a0d688c6b3872a4caa5c26fe3 Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Tue, 12 Nov 2024 15:47:31 +0100 Subject: [PATCH 1/3] Update to OpenTelemetry 0.27 --- CHANGELOG.md | 2 + Cargo.toml | 18 +++--- examples/metrics.rs | 4 +- src/convert.rs | 1 + src/lib.rs | 33 +++++++---- src/logs.rs | 11 ++-- src/metrics.rs | 59 ++++++++----------- src/quick_pulse.rs | 2 +- src/tags.rs | 19 +++--- .../http_requests__live_metrics.snap | 20 +++---- tests/snapshots/http_requests__logs.snap | 4 +- ...http_requests__traces_batch_async_std.snap | 4 +- .../http_requests__traces_batch_tokio.snap | 4 +- 13 files changed, 87 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f6be52..7cbc601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +- Upgrade `opentelemetry` dependencies to `v0.27`. + ## [0.36.0] - 2024-10-15 - Upgrade `opentelemetry` dependencies to `v0.26`. Thanks, [sezna@](https://github.com/sezna). diff --git a/Cargo.toml b/Cargo.toml index c8d18f2..b7893e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,19 +42,19 @@ async-trait = "0.1" bytes = "1" chrono = "0.4" flate2 = "1" +futures-util = { version = "0.3", default-features = false, optional = true } http = "1" once_cell = "1" -futures-util = { version = "0.3", default-features = false, optional = true } -opentelemetry = "0.26" -opentelemetry_sdk = "0.26" -opentelemetry-http = "0.26" -opentelemetry-semantic-conventions = { version = "0.26", features = ["semconv_experimental"] } +opentelemetry = "0.27" +opentelemetry-http = "0.27" +opentelemetry-semantic-conventions = { version = "0.27", features = ["semconv_experimental"] } +opentelemetry_sdk = "0.27" reqwest = { version = "0.12", default-features = false, features = ["blocking"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" serde_repr = "0.1" -thiserror = "1" sysinfo = { version = "0.30", optional = true } +thiserror = "1" [dev-dependencies] async-std = { version = "1.13.0", features = ["attributes"] } @@ -62,9 +62,9 @@ doc-comment = "0.3.3" env_logger = "0.11.3" insta = "1.39.0" log = { version = "0.4", features = ["kv", "kv_sval"] } -opentelemetry_sdk = { version = "0.26", features = ["rt-async-std", "rt-tokio", "rt-tokio-current-thread", "logs_level_enabled"] } -opentelemetry-http = { version = "0.26", features = ["reqwest"] } -opentelemetry-appender-log = { version = "0.26", features = ["with-serde"] } +opentelemetry_sdk = { version = "0.27", features = ["rt-async-std", "rt-tokio", "rt-tokio-current-thread", "spec_unstable_logs_enabled"] } +opentelemetry-http = { version = "0.27", features = ["reqwest"] } +opentelemetry-appender-log = { version = "0.27", features = ["with-serde"] } rand = "0.8.5" regex = "1.10.5" reqwest = { version = "0.12", features = ["blocking"] } diff --git a/examples/metrics.rs b/examples/metrics.rs index ac523a6..707566e 100644 --- a/examples/metrics.rs +++ b/examples/metrics.rs @@ -32,13 +32,13 @@ async fn main() -> Result<(), Box> { &[KeyValue::new("state", "idle"), KeyValue::new("cpu", 0)], ) }) - .init(); + .build(); // Recorder let server_duration = meter .u64_histogram("http.server.duration") .with_unit("milliseconds") - .init(); + .build(); let mut rng = thread_rng(); for _ in 1..10 { server_duration.record( diff --git a/src/convert.rs b/src/convert.rs index eacbd92..296406b 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -194,6 +194,7 @@ impl AttrValue for AnyValue { s.push('}'); s.into() } + _ => format!("{:?}", self).into(), } } } diff --git a/src/lib.rs b/src/lib.rs index 122c5f4..b8d643f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,7 +87,7 @@ //! //! // Record value //! let meter = global::meter("example"); -//! let histogram = meter.f64_histogram("pi").init(); +//! let histogram = meter.f64_histogram("pi").build(); //! histogram.record(3.14, &[]); //! //! // Simulate work, during which metrics will periodically be reported. @@ -372,10 +372,12 @@ mod uploader_quick_pulse; use connection_string::DEFAULT_LIVE_ENDPOINT; use connection_string::{ConnectionString, DEFAULT_BREEZE_ENDPOINT}; pub use models::context_tag_keys::attrs; +use opentelemetry::trace::ExportError as TraceExportError; +use opentelemetry::InstrumentationScope; #[cfg(feature = "trace")] use opentelemetry::{global, trace::TracerProvider as _, KeyValue, Value}; pub use opentelemetry_http::HttpClient; -use opentelemetry_sdk::export::ExportError; +use opentelemetry_sdk::export::ExportError as SdkExportError; #[cfg(any(feature = "trace", feature = "logs"))] use opentelemetry_sdk::Resource; #[cfg(feature = "trace")] @@ -701,11 +703,7 @@ where /// that. pub fn install_simple(self) -> Tracer { let trace_provider = self.build_simple(); - let tracer = trace_provider - .tracer_builder("opentelemetry-application-insights") - .with_version(env!("CARGO_PKG_VERSION")) - .with_schema_url(semcov::SCHEMA_URL) - .build(); + let tracer = trace_provider.tracer_with_scope(scope()); let _previous_provider = global::set_tracer_provider(trace_provider); tracer } @@ -716,11 +714,7 @@ where /// that. pub fn install_batch(self, runtime: R) -> Tracer { let trace_provider = self.build_batch(runtime); - let tracer = trace_provider - .tracer_builder("opentelemetry-application-insights") - .with_version(env!("CARGO_PKG_VERSION")) - .with_schema_url(semcov::SCHEMA_URL) - .build(); + let tracer = trace_provider.tracer_with_scope(scope()); let _previous_provider = global::set_tracer_provider(trace_provider); tracer } @@ -818,6 +812,13 @@ fn append_v2_track(uri: impl ToString) -> Result InstrumentationScope { + InstrumentationScope::builder("opentelemetry-application-insights") + .with_version(env!("CARGO_PKG_VERSION")) + .with_schema_url(semcov::SCHEMA_URL) + .build() +} + /// Errors that occurred during span export. #[derive(thiserror::Error, Debug)] #[non_exhaustive] @@ -867,7 +868,13 @@ pub enum Error { QuickPulseShutdown(opentelemetry_sdk::runtime::TrySendError), } -impl ExportError for Error { +impl SdkExportError for Error { + fn exporter_name(&self) -> &'static str { + "application-insights" + } +} + +impl TraceExportError for Error { fn exporter_name(&self) -> &'static str { "application-insights" } diff --git a/src/logs.rs b/src/logs.rs index e9a8a11..144f643 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -7,14 +7,11 @@ use crate::{ Exporter, }; use async_trait::async_trait; -use opentelemetry::{ - logs::{LogResult, Severity}, - InstrumentationLibrary, -}; +use opentelemetry::{logs::Severity, InstrumentationScope}; use opentelemetry_http::HttpClient; use opentelemetry_sdk::{ export::logs::{LogBatch, LogExporter}, - logs::LogRecord, + logs::{LogRecord, LogResult}, Resource, }; use opentelemetry_semantic_conventions as semcov; @@ -30,7 +27,7 @@ fn is_exception(record: &LogRecord) -> bool { impl Exporter { fn create_envelope_for_log( &self, - (record, instrumentation_lib): (&LogRecord, &InstrumentationLibrary), + (record, instrumentation_scope): (&LogRecord, &InstrumentationScope), ) -> Envelope { let (data, name) = if is_exception(record) { ( @@ -57,7 +54,7 @@ impl Exporter { i_key: Some(self.instrumentation_key.clone().into()), tags: Some(get_tags_for_log( record, - instrumentation_lib, + instrumentation_scope, &self.resource, )), data: Some(data), diff --git a/src/metrics.rs b/src/metrics.rs index eb15b6e..634e55c 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -5,47 +5,22 @@ use crate::{ Exporter, }; use async_trait::async_trait; -use opentelemetry::{ - global, - metrics::{MetricsError, Result as MetricsResult}, - KeyValue, -}; +use opentelemetry::{otel_warn, KeyValue}; use opentelemetry_http::HttpClient; use opentelemetry_sdk::metrics::{ - data::{ExponentialHistogram, Gauge, Histogram, Metric, ResourceMetrics, Sum, Temporality}, - exporter::PushMetricsExporter, - reader::TemporalitySelector, - InstrumentKind, + data::{ExponentialHistogram, Gauge, Histogram, Metric, ResourceMetrics, Sum}, + exporter::PushMetricExporter, + MetricResult, Temporality, }; use std::{convert::TryInto, sync::Arc, time::SystemTime}; -#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] -impl TemporalitySelector for Exporter -where - C: Send + Sync, -{ - fn temporality(&self, kind: InstrumentKind) -> Temporality { - // See https://github.com/frigus02/opentelemetry-application-insights/issues/74#issuecomment-2108488385 - match kind { - InstrumentKind::Counter - | InstrumentKind::Histogram - | InstrumentKind::ObservableCounter - | InstrumentKind::Gauge - | InstrumentKind::ObservableGauge => Temporality::Delta, - InstrumentKind::UpDownCounter | InstrumentKind::ObservableUpDownCounter => { - Temporality::Cumulative - } - } - } -} - #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] #[async_trait] -impl PushMetricsExporter for Exporter +impl PushMetricExporter for Exporter where C: HttpClient + 'static, { - async fn export(&self, metrics: &mut ResourceMetrics) -> MetricsResult<()> { + async fn export(&self, metrics: &mut ResourceMetrics) -> MetricResult<()> { let client = Arc::clone(&self.client); let endpoint = Arc::clone(&self.endpoint); @@ -62,8 +37,7 @@ where .chain( scope_metrics .scope - .attributes - .iter() + .attributes() .map(|kv| (&kv.key, &kv.value)), ) .chain(data.attrs.iter().map(|kv| (&kv.key, &kv.value))) @@ -89,13 +63,26 @@ where Ok(()) } - async fn force_flush(&self) -> MetricsResult<()> { + async fn force_flush(&self) -> MetricResult<()> { Ok(()) } - fn shutdown(&self) -> MetricsResult<()> { + fn shutdown(&self) -> MetricResult<()> { Ok(()) } + + fn temporality(&self) -> Temporality { + // Application Insights only supports Delta temporality as defined in the spec: + // + // > Choose Delta aggregation temporality for Counter, Asynchronous Counter and Histogram + // > instrument kinds, choose Cumulative aggregation for UpDownCounter and Asynchronous + // > UpDownCounter instrument kinds. + // + // See: + // - https://github.com/open-telemetry/opentelemetry-specification/blob/58bfe48eabe887545198d66c43f44071b822373f/specification/metrics/sdk_exporters/otlp.md?plain=1#L46-L47 + // - https://github.com/frigus02/opentelemetry-application-insights/issues/74#issuecomment-2108488385 + Temporality::Delta + } } struct EnvelopeData { @@ -153,7 +140,7 @@ fn map_metric(metric: &Metric) -> Vec { } else if let Some(sum) = data.downcast_ref::>() { map_sum(metric, sum) } else { - global::handle_error(MetricsError::Other("unknown aggregator".into())); + otel_warn!(name: "ApplicationInsights.ExportMetrics.UnknownAggregator"); Vec::new() } } diff --git a/src/quick_pulse.rs b/src/quick_pulse.rs index c5275d7..747727d 100644 --- a/src/quick_pulse.rs +++ b/src/quick_pulse.rs @@ -145,7 +145,7 @@ impl SpanProcessor for QuickPulseManager { impl Drop for QuickPulseManager { fn drop(&mut self) { if let Err(err) = self.shutdown() { - opentelemetry::global::handle_error(err); + opentelemetry::otel_warn!(name: "ApplicationInsights.LiveMetrics.ShutdownFailed", error = err); } } } diff --git a/src/tags.rs b/src/tags.rs index 3e80928..ccb819f 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -6,7 +6,7 @@ use crate::{ use opentelemetry::trace::{SpanId, SpanKind}; #[cfg(feature = "metrics")] use opentelemetry::KeyValue; -use opentelemetry::{InstrumentationLibrary, Key}; +use opentelemetry::{InstrumentationScope, Key}; #[cfg(feature = "trace")] use opentelemetry_sdk::export::trace::SpanData; #[cfg(feature = "logs")] @@ -18,7 +18,7 @@ use std::collections::HashMap; #[cfg(feature = "trace")] pub(crate) fn get_tags_for_span(span: &SpanData, resource: &Resource) -> Tags { let mut tags = Tags::new(); - build_tags_from_resource_attrs(&mut tags, resource, &span.instrumentation_lib); + build_tags_from_resource_attrs(&mut tags, resource, &span.instrumentation_scope); let attrs_map = build_tags_from_attrs( &mut tags, @@ -67,7 +67,7 @@ pub(crate) fn get_tags_for_span(span: &SpanData, resource: &Resource) -> Tags { #[cfg(feature = "trace")] pub(crate) fn get_tags_for_event(span: &SpanData, resource: &Resource) -> Tags { let mut tags = Tags::new(); - build_tags_from_resource_attrs(&mut tags, resource, &span.instrumentation_lib); + build_tags_from_resource_attrs(&mut tags, resource, &span.instrumentation_scope); tags.insert(tags::OPERATION_ID, span.span_context.trace_id().to_string()); tags.insert( @@ -80,7 +80,7 @@ pub(crate) fn get_tags_for_event(span: &SpanData, resource: &Resource) -> Tags { #[cfg(feature = "metrics")] pub(crate) fn get_tags_for_metric( resource: &Resource, - scope: &InstrumentationLibrary, + scope: &InstrumentationScope, attrs: &[KeyValue], ) -> Tags { let mut tags = Tags::new(); @@ -97,11 +97,11 @@ pub(crate) fn get_tags_for_metric( #[cfg(feature = "logs")] pub(crate) fn get_tags_for_log( record: &LogRecord, - instrumentation_lib: &InstrumentationLibrary, + instrumentation_scope: &InstrumentationScope, resource: &Resource, ) -> Tags { let mut tags = Tags::new(); - build_tags_from_resource_attrs(&mut tags, resource, instrumentation_lib); + build_tags_from_resource_attrs(&mut tags, resource, instrumentation_scope); build_tags_from_attrs( &mut tags, @@ -151,15 +151,14 @@ where fn build_tags_from_resource_attrs( tags: &mut Tags, resource: &Resource, - instrumentation_lib: &InstrumentationLibrary, + instrumentation_scope: &InstrumentationScope, ) { let attrs = resource .iter() .map(|(k, v)| (k, v as &dyn AttrValue)) .chain( - instrumentation_lib - .attributes - .iter() + instrumentation_scope + .attributes() .map(|kv| (&kv.key, &kv.value as &dyn AttrValue)), ); let attrs_map = build_tags_from_attrs(tags, attrs); diff --git a/tests/snapshots/http_requests__live_metrics.snap b/tests/snapshots/http_requests__live_metrics.snap index b6d355e..4ca2345 100644 --- a/tests/snapshots/http_requests__live_metrics.snap +++ b/tests/snapshots/http_requests__live_metrics.snap @@ -21,7 +21,7 @@ x-ms-qps-role-name: unknown_service "RoleName": "unknown_service", "StreamId": "STRIPPED", "Timestamp": "STRIPPED", - "Version": "opentelemetry:0.26.0" + "Version": "opentelemetry:0.27.0" } @@ -87,7 +87,7 @@ content-encoding: gzip "RoleName": "unknown_service", "StreamId": "STRIPPED", "Timestamp": "STRIPPED", - "Version": "opentelemetry:0.26.0" + "Version": "opentelemetry:0.27.0" } ] @@ -144,7 +144,7 @@ content-encoding: gzip "RoleName": "unknown_service", "StreamId": "STRIPPED", "Timestamp": "STRIPPED", - "Version": "opentelemetry:0.26.0" + "Version": "opentelemetry:0.27.0" } ] @@ -165,7 +165,7 @@ content-encoding: gzip "service.name": "unknown_service", "telemetry.sdk.language": "rust", "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "0.26.0" + "telemetry.sdk.version": "0.27.0" }, "resultCode": "2", "success": false, @@ -178,7 +178,7 @@ content-encoding: gzip "sampleRate": 100.0, "tags": { "ai.cloud.role": "unknown_service", - "ai.internal.sdkVersion": "opentelemetry:0.26.0", + "ai.internal.sdkVersion": "opentelemetry:0.27.0", "ai.operation.id": "STRIPPED" }, "time": "STRIPPED" @@ -201,7 +201,7 @@ content-encoding: gzip "sampleRate": 100.0, "tags": { "ai.cloud.role": "unknown_service", - "ai.internal.sdkVersion": "opentelemetry:0.26.0", + "ai.internal.sdkVersion": "opentelemetry:0.27.0", "ai.operation.id": "STRIPPED", "ai.operation.parentId": "STRIPPED" }, @@ -217,7 +217,7 @@ content-encoding: gzip "service.name": "unknown_service", "telemetry.sdk.language": "rust", "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "0.26.0" + "telemetry.sdk.version": "0.27.0" }, "responseCode": "2", "success": false, @@ -230,7 +230,7 @@ content-encoding: gzip "sampleRate": 100.0, "tags": { "ai.cloud.role": "unknown_service", - "ai.internal.sdkVersion": "opentelemetry:0.26.0", + "ai.internal.sdkVersion": "opentelemetry:0.27.0", "ai.operation.id": "STRIPPED" }, "time": "STRIPPED" @@ -245,7 +245,7 @@ content-encoding: gzip "service.name": "unknown_service", "telemetry.sdk.language": "rust", "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "0.26.0" + "telemetry.sdk.version": "0.27.0" }, "responseCode": "0", "success": true, @@ -258,7 +258,7 @@ content-encoding: gzip "sampleRate": 100.0, "tags": { "ai.cloud.role": "unknown_service", - "ai.internal.sdkVersion": "opentelemetry:0.26.0", + "ai.internal.sdkVersion": "opentelemetry:0.27.0", "ai.operation.id": "STRIPPED" }, "time": "STRIPPED" diff --git a/tests/snapshots/http_requests__logs.snap b/tests/snapshots/http_requests__logs.snap index b7e4c24..f007ea3 100644 --- a/tests/snapshots/http_requests__logs.snap +++ b/tests/snapshots/http_requests__logs.snap @@ -121,7 +121,7 @@ content-encoding: gzip "service.name": "unknown_service", "telemetry.sdk.language": "rust", "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "0.26.0" + "telemetry.sdk.version": "0.27.0" }, "resultCode": "0", "type": "InProc", @@ -134,7 +134,7 @@ content-encoding: gzip "sampleRate": 100.0, "tags": { "ai.cloud.role": "unknown_service", - "ai.internal.sdkVersion": "opentelemetry:0.26.0", + "ai.internal.sdkVersion": "opentelemetry:0.27.0", "ai.operation.id": "STRIPPED" }, "time": "STRIPPED" diff --git a/tests/snapshots/http_requests__traces_batch_async_std.snap b/tests/snapshots/http_requests__traces_batch_async_std.snap index f644f80..429952c 100644 --- a/tests/snapshots/http_requests__traces_batch_async_std.snap +++ b/tests/snapshots/http_requests__traces_batch_async_std.snap @@ -18,7 +18,7 @@ content-encoding: gzip "service.name": "unknown_service", "telemetry.sdk.language": "rust", "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "0.26.0" + "telemetry.sdk.version": "0.27.0" }, "resultCode": "0", "type": "InProc", @@ -31,7 +31,7 @@ content-encoding: gzip "sampleRate": 100.0, "tags": { "ai.cloud.role": "unknown_service", - "ai.internal.sdkVersion": "opentelemetry:0.26.0", + "ai.internal.sdkVersion": "opentelemetry:0.27.0", "ai.operation.id": "STRIPPED" }, "time": "STRIPPED" diff --git a/tests/snapshots/http_requests__traces_batch_tokio.snap b/tests/snapshots/http_requests__traces_batch_tokio.snap index 0e0ad8d..874be7a 100644 --- a/tests/snapshots/http_requests__traces_batch_tokio.snap +++ b/tests/snapshots/http_requests__traces_batch_tokio.snap @@ -18,7 +18,7 @@ content-encoding: gzip "service.name": "unknown_service", "telemetry.sdk.language": "rust", "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "0.26.0" + "telemetry.sdk.version": "0.27.0" }, "resultCode": "0", "type": "InProc", @@ -31,7 +31,7 @@ content-encoding: gzip "sampleRate": 100.0, "tags": { "ai.cloud.role": "unknown_service", - "ai.internal.sdkVersion": "opentelemetry:0.26.0", + "ai.internal.sdkVersion": "opentelemetry:0.27.0", "ai.operation.id": "STRIPPED" }, "time": "STRIPPED" From d9849924219ba015ff496f88d0e359aebc01b65d Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Tue, 12 Nov 2024 15:53:11 +0100 Subject: [PATCH 2/3] Fix logs-only build --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b8d643f..42439da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -373,6 +373,7 @@ use connection_string::DEFAULT_LIVE_ENDPOINT; use connection_string::{ConnectionString, DEFAULT_BREEZE_ENDPOINT}; pub use models::context_tag_keys::attrs; use opentelemetry::trace::ExportError as TraceExportError; +#[cfg(feature = "trace")] use opentelemetry::InstrumentationScope; #[cfg(feature = "trace")] use opentelemetry::{global, trace::TracerProvider as _, KeyValue, Value}; @@ -812,6 +813,7 @@ fn append_v2_track(uri: impl ToString) -> Result InstrumentationScope { InstrumentationScope::builder("opentelemetry-application-insights") .with_version(env!("CARGO_PKG_VERSION")) From 3a23927d96d7b5b4c67c4b2fd10ae7271458c833 Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Tue, 12 Nov 2024 15:55:26 +0100 Subject: [PATCH 3/3] Update thiserror to 2 --- CHANGELOG.md | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cbc601..a85b0e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - Upgrade `opentelemetry` dependencies to `v0.27`. +- Upgrade `thiserror` dependency to `v2`. ## [0.36.0] - 2024-10-15 diff --git a/Cargo.toml b/Cargo.toml index b7893e2..e8e45fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" serde_repr = "0.1" sysinfo = { version = "0.30", optional = true } -thiserror = "1" +thiserror = "2" [dev-dependencies] async-std = { version = "1.13.0", features = ["attributes"] }