Skip to content

Commit ae651d4

Browse files
fix(sdk): force_flush returns meaningful bool on MetricReader
Fixes #5020 Signed-off-by: Ravi Theja <ravitheja4531@gmail.com>
1 parent 7477b10 commit ae651d4

File tree

2 files changed

+27
-14
lines changed

2 files changed

+27
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212

1313
## Unreleased
1414

15+
- `opentelemetry-sdk`: Fix `force_flush` on `MetricReader` and `PeriodicExportingMetricReader` to return a meaningful `bool` reflecting actual export success/failure instead of always returning `True`. Also fixes `detach(token)` not being called when export raises an exception. ([#5020](https://github.com/open-telemetry/opentelemetry-python/issues/5020))
1516
- `opentelemetry-sdk`: Add `create_logger_provider`/`configure_logger_provider` to declarative file configuration, enabling LoggerProvider instantiation from config files without reading env vars
1617
([#4990](https://github.com/open-telemetry/opentelemetry-python/pull/4990))
1718
- `opentelemetry-sdk`: Add `service` resource detector support to declarative file configuration via `detection_development.detectors[].service`

opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ def __init__(
336336
)
337337

338338
@final
339-
def collect(self, timeout_millis: float = 10_000) -> None:
339+
def collect(self, timeout_millis: float = 10_000) -> Optional[bool]:
340340
"""Collects the metrics from the internal SDK state and
341341
invokes the `_receive_metrics` with the collection.
342342
@@ -361,10 +361,11 @@ def collect(self, timeout_millis: float = 10_000) -> None:
361361
self._metrics.record_collection(perf_counter() - start_time)
362362

363363
if metrics is not None:
364-
self._receive_metrics(
364+
return self._receive_metrics(
365365
metrics,
366366
timeout_millis=timeout_millis,
367367
)
368+
return None
368369

369370
@final
370371
def _set_collect_callback(
@@ -386,17 +387,25 @@ def _receive_metrics(
386387
metrics_data: MetricsData,
387388
timeout_millis: float = 10_000,
388389
**kwargs,
389-
) -> None:
390-
"""Called by `MetricReader.collect` when it receives a batch of metrics"""
390+
) -> bool:
391+
"""Called by `MetricReader.collect` when it receives a batch of metrics.
392+
393+
Subclasses must return ``True`` on success and ``False`` on failure.
394+
395+
.. note::
396+
Existing subclasses that return ``None`` (the old implicit default)
397+
will be treated as vacuous success by ``force_flush``, preserving
398+
backward-compatible behaviour.
399+
"""
391400

392401
def _set_meter_provider(self, meter_provider: MeterProvider) -> None:
393402
self._metrics = MetricReaderMetrics(
394403
self._otel_component_type, meter_provider
395404
)
396405

397406
def force_flush(self, timeout_millis: float = 10_000) -> bool:
398-
self.collect(timeout_millis=timeout_millis)
399-
return True
407+
result = self.collect(timeout_millis=timeout_millis)
408+
return result is not False
400409

401410
@abstractmethod
402411
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
@@ -449,9 +458,10 @@ def _receive_metrics(
449458
metrics_data: MetricsData,
450459
timeout_millis: float = 10_000,
451460
**kwargs,
452-
) -> None:
461+
) -> bool:
453462
with self._lock:
454463
self._metrics_data = metrics_data
464+
return True
455465

456466
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
457467
pass
@@ -567,17 +577,19 @@ def _receive_metrics(
567577
metrics_data: MetricsData,
568578
timeout_millis: float = 10_000,
569579
**kwargs,
570-
) -> None:
580+
) -> bool:
571581
token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
572-
# pylint: disable=broad-exception-caught,invalid-name
573582
try:
574583
with self._export_lock:
575-
self._exporter.export(
584+
result = self._exporter.export(
576585
metrics_data, timeout_millis=timeout_millis
577586
)
587+
return result is MetricExportResult.SUCCESS
578588
except Exception:
579589
_logger.exception("Exception while exporting metrics")
580-
detach(token)
590+
return False
591+
finally:
592+
detach(token)
581593

582594
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
583595
deadline_ns = time_ns() + timeout_millis * 10**6
@@ -596,6 +608,6 @@ def _shutdown():
596608
self._exporter.shutdown(timeout=(deadline_ns - time_ns()) / 10**6)
597609

598610
def force_flush(self, timeout_millis: float = 10_000) -> bool:
599-
super().force_flush(timeout_millis=timeout_millis)
600-
self._exporter.force_flush(timeout_millis=timeout_millis)
601-
return True
611+
collect_ok = super().force_flush(timeout_millis=timeout_millis)
612+
exporter_ok = self._exporter.force_flush(timeout_millis=timeout_millis)
613+
return collect_ok and exporter_ok

0 commit comments

Comments
 (0)