Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4402](https://github.com/open-telemetry/opentelemetry-python/pull/4402))
- Tolerates exceptions when loading resource detectors via `OTEL_EXPERIMENTAL_RESOURCE_DETECTORS`
([#4373](https://github.com/open-telemetry/opentelemetry-python/pull/4373))
- Disconnect gRPC client stub when shutting down `OTLPSpanExporter`
([#4370](https://github.com/open-telemetry/opentelemetry-python/pull/4370))
- opentelemetry-sdk: fix OTLP exporting of Histograms with explicit buckets advisory
([#4434](https://github.com/open-telemetry/opentelemetry-python/pull/4434))
- opentelemetry-exporter-otlp-proto-grpc: better dependency version range for Python 3.13
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,8 @@ def __init__(
) or Compression.NoCompression

if insecure:
self._client = self._stub(
insecure_channel(self._endpoint, compression=compression)
self._channel = insecure_channel(
self._endpoint, compression=compression
)
else:
credentials = _get_credentials(
Expand All @@ -253,11 +253,10 @@ def __init__(
OTEL_EXPORTER_OTLP_CLIENT_KEY,
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
)
self._client = self._stub(
secure_channel(
self._endpoint, credentials, compression=compression
)
self._channel = secure_channel(
self._endpoint, credentials, compression=compression
)
self._client = self._stub(self._channel)

self._export_lock = threading.Lock()
self._shutdown = False
Expand Down Expand Up @@ -360,6 +359,7 @@ def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
# wait for the last export if any
self._export_lock.acquire(timeout=timeout_millis / 1e3)
self._shutdown = True
self._channel.close()
self._export_lock.release()

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,21 @@ def test_shutdown_wait_last_export(self):
finally:
export_thread.join()

def test_export_over_closed_grpc_channel(self):
# pylint: disable=protected-access

add_MetricsServiceServicer_to_server(
MetricsServiceServicerSUCCESS(), self.server
)
self.exporter.export(self.metrics["sum_int"])
self.exporter.shutdown()
data = self.exporter._translate_data(self.metrics["sum_int"])
with self.assertRaises(ValueError) as err:
self.exporter._client.Export(request=data)
self.assertEqual(
str(err.exception), "Cannot invoke RPC on closed channel!"
)

def test_aggregation_temporality(self):
# pylint: disable=protected-access

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,21 @@ def test_shutdown_wait_last_export(self):
finally:
export_thread.join()

def test_export_over_closed_grpc_channel(self):
# pylint: disable=protected-access

add_TraceServiceServicer_to_server(
TraceServiceServicerSUCCESS(), self.server
)
self.exporter.export([self.span])
self.exporter.shutdown()
data = self.exporter._translate_data([self.span])
with self.assertRaises(ValueError) as err:
self.exporter._client.Export(request=data)
self.assertEqual(
str(err.exception), "Cannot invoke RPC on closed channel!"
)


def _create_span_with_status(status: SDKStatus):
span = _Span(
Expand Down