From 28b45ddbbb87b8c256c2f1e0a4b5fcb7d87ecf1c Mon Sep 17 00:00:00 2001 From: Stefano Baghino Date: Sat, 22 Nov 2025 11:58:47 +0100 Subject: [PATCH 1/4] Handle shutdown in logs exporter --- opentelemetry-otlp/src/exporter/tonic/logs.rs | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/tonic/logs.rs b/opentelemetry-otlp/src/exporter/tonic/logs.rs index fbe033d09e..4d8f13f6ae 100644 --- a/opentelemetry-otlp/src/exporter/tonic/logs.rs +++ b/opentelemetry-otlp/src/exporter/tonic/logs.rs @@ -5,9 +5,8 @@ use opentelemetry_proto::tonic::collector::logs::v1::{ }; use opentelemetry_sdk::error::{OTelSdkError, OTelSdkResult}; use opentelemetry_sdk::logs::{LogBatch, LogExporter}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time; -use tokio::sync::Mutex; use tonic::{codegen::CompressionEncoding, service::Interceptor, transport::Channel, Request}; use opentelemetry_proto::transform::logs::tonic::group_logs_by_resource_and_scope; @@ -85,24 +84,26 @@ impl LogExporter for TonicLogsClient { let batch_clone = Arc::clone(&batch); // Execute the export operation - let (mut client, metadata, extensions) = match self.inner.lock().await.as_mut() { - Some(inner) => { - let (m, e, _) = inner - .interceptor - .call(Request::new(())) - .map_err(|e| { - // Convert interceptor errors to tonic::Status for retry classification - tonic::Status::internal(format!("interceptor error: {e:?}")) - })? - .into_parts(); - (inner.client.clone(), m, e) - } - None => { - return Err(tonic::Status::failed_precondition( - "exporter already shutdown", - )) - } - }; + let (mut client, metadata, extensions) = self + .inner + .lock() + .map_err(|e| tonic::Status::internal(format!("Failed to acquire lock: {e:?}"))) + .and_then(|mut inner| match &mut *inner { + Some(inner) => { + let (m, e, _) = inner + .interceptor + .call(Request::new(())) + .map_err(|e| { + // Convert interceptor errors to tonic::Status for retry classification + tonic::Status::internal(format!("interceptor error: {e:?}")) + })? + .into_parts(); + Ok((inner.client.clone(), m, e)) + } + None => Err(tonic::Status::failed_precondition( + "exporter is already shut down", + )), + })?; let resource_logs = group_logs_by_resource_and_scope(&batch_clone, &self.resource); @@ -143,13 +144,11 @@ impl LogExporter for TonicLogsClient { } fn shutdown_with_timeout(&self, _timeout: time::Duration) -> OTelSdkResult { - // TODO: Implement actual shutdown - // Due to the use of tokio::sync::Mutex to guard - // the inner client, we need to await the call to lock the mutex - // and that requires async runtime. - // It is possible to fix this by using - // a dedicated thread just to handle shutdown. - // But for now, we just return Ok. + self.inner + .lock() + .map_err(|e| OTelSdkError::InternalFailure(format!("Failed to acquire lock: {e}")))? + .take(); + Ok(()) } From e6fae39d449736b0644ca0583fe1576a21ac3adc Mon Sep 17 00:00:00 2001 From: Stefano Baghino Date: Sat, 22 Nov 2025 12:12:29 +0100 Subject: [PATCH 2/4] Differentiate between exporter error messages --- opentelemetry-otlp/src/exporter/tonic/logs.rs | 2 +- opentelemetry-otlp/src/exporter/tonic/metrics.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/tonic/logs.rs b/opentelemetry-otlp/src/exporter/tonic/logs.rs index 4d8f13f6ae..cd8ae75c9f 100644 --- a/opentelemetry-otlp/src/exporter/tonic/logs.rs +++ b/opentelemetry-otlp/src/exporter/tonic/logs.rs @@ -101,7 +101,7 @@ impl LogExporter for TonicLogsClient { Ok((inner.client.clone(), m, e)) } None => Err(tonic::Status::failed_precondition( - "exporter is already shut down", + "log exporter is already shut down", )), })?; diff --git a/opentelemetry-otlp/src/exporter/tonic/metrics.rs b/opentelemetry-otlp/src/exporter/tonic/metrics.rs index 5239ed5460..a38fd834f7 100644 --- a/opentelemetry-otlp/src/exporter/tonic/metrics.rs +++ b/opentelemetry-otlp/src/exporter/tonic/metrics.rs @@ -93,7 +93,7 @@ impl MetricsClient for TonicMetricsClient { Ok((inner.client.clone(), m, e)) } None => Err(tonic::Status::failed_precondition( - "exporter is already shut down", + "metrics exporter is already shut down", )), })?; From 8181574bcbdef875d64a5a8670ad81cfc76b4a6c Mon Sep 17 00:00:00 2001 From: Stefano Baghino Date: Sat, 22 Nov 2025 12:19:19 +0100 Subject: [PATCH 3/4] Add entry in the changelog --- opentelemetry-otlp/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/opentelemetry-otlp/CHANGELOG.md b/opentelemetry-otlp/CHANGELOG.md index fda200dea0..5fea2bc2c3 100644 --- a/opentelemetry-otlp/CHANGELOG.md +++ b/opentelemetry-otlp/CHANGELOG.md @@ -4,6 +4,9 @@ - Add partial success response handling for OTLP exporters (traces, metrics, logs) per OTLP spec. Exporters now log warnings when the server returns partial success responses with rejected items and error messages. [#865](https://github.com/open-telemetry/opentelemetry-rust/issues/865) - Refactor `internal-logs` feature in `opentelemetry-otlp` to reduce unnecessary dependencies[3191](https://github.com/open-telemetry/opentelemetry-rust/pull/3192) +- Fixed + [#2777](https://github.com/open-telemetry/opentelemetry-rust/issues/2777) + to properly handle `shutdown_with_timeout()` when using `tonic`. ## 0.31.0 From 4df15dedcdd07df9db04600ea63e28ac87f46289 Mon Sep 17 00:00:00 2001 From: Stefano Baghino Date: Tue, 25 Nov 2025 09:12:02 +0100 Subject: [PATCH 4/4] Update opentelemetry-otlp/CHANGELOG.md Co-authored-by: Scott Gerring --- opentelemetry-otlp/CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/opentelemetry-otlp/CHANGELOG.md b/opentelemetry-otlp/CHANGELOG.md index 5fea2bc2c3..53a1d5b260 100644 --- a/opentelemetry-otlp/CHANGELOG.md +++ b/opentelemetry-otlp/CHANGELOG.md @@ -4,9 +4,7 @@ - Add partial success response handling for OTLP exporters (traces, metrics, logs) per OTLP spec. Exporters now log warnings when the server returns partial success responses with rejected items and error messages. [#865](https://github.com/open-telemetry/opentelemetry-rust/issues/865) - Refactor `internal-logs` feature in `opentelemetry-otlp` to reduce unnecessary dependencies[3191](https://github.com/open-telemetry/opentelemetry-rust/pull/3192) -- Fixed - [#2777](https://github.com/open-telemetry/opentelemetry-rust/issues/2777) - to properly handle `shutdown_with_timeout()` when using `tonic`. +- Fixed [#2777](https://github.com/open-telemetry/opentelemetry rust/issues/2777) to properly handle `shutdown_with_timeout()` when using `grpc-tonic`. ## 0.31.0