Skip to content

Commit 77d96ba

Browse files
authored
Use exceptions to report shutdown result (#2599)
* Use exceptions to report shutdown result Fixes #2406 * Fix mypy * Make exceptions private * Fix exceptions module path * Remove exceptions and show failed metric readers
1 parent ee2a0f4 commit 77d96ba

File tree

5 files changed

+53
-28
lines changed

5 files changed

+53
-28
lines changed

opentelemetry-sdk/src/opentelemetry/sdk/_metrics/__init__.py

+23-9
Original file line numberDiff line numberDiff line change
@@ -315,25 +315,39 @@ def _shutdown():
315315

316316
if not did_shutdown:
317317
_logger.warning("shutdown can only be called once")
318-
return False
318+
return
319319

320-
overall_result = True
320+
metric_reader_error = {}
321321

322322
for metric_reader in self._sdk_config.metric_readers:
323-
metric_reader_result = metric_reader.shutdown()
323+
try:
324+
metric_reader.shutdown()
324325

325-
if not metric_reader_result:
326-
_logger.warning(
327-
"MetricReader %s failed to shutdown", metric_reader
328-
)
326+
# pylint: disable=broad-except
327+
except Exception as error:
329328

330-
overall_result = overall_result and metric_reader_result
329+
metric_reader_error[metric_reader] = error
331330

332331
if self._atexit_handler is not None:
333332
unregister(self._atexit_handler)
334333
self._atexit_handler = None
335334

336-
return overall_result
335+
if metric_reader_error:
336+
337+
metric_reader_error_string = "\n".join(
338+
[
339+
f"{metric_reader.__class__.__name__}: {repr(error)}"
340+
for metric_reader, error in metric_reader_error.items()
341+
]
342+
)
343+
344+
raise Exception(
345+
(
346+
"MeterProvider.shutdown failed because the following "
347+
"metric readers failed during shutdown:\n"
348+
f"{metric_reader_error_string}"
349+
)
350+
)
337351

338352
def get_meter(
339353
self,

opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ def _receive_metrics(self, metrics: Iterable[Metric]):
122122
with self._lock:
123123
self._metrics = list(metrics)
124124

125-
def shutdown(self) -> bool:
126-
return True
125+
def shutdown(self):
126+
pass
127127

128128

129129
class PeriodicExportingMetricReader(MetricReader):
@@ -193,16 +193,15 @@ def _receive_metrics(self, metrics: Iterable[Metric]) -> None:
193193
_logger.exception("Exception while exporting metrics %s", str(e))
194194
detach(token)
195195

196-
def shutdown(self) -> bool:
196+
def shutdown(self):
197197
def _shutdown():
198198
self._shutdown = True
199199

200200
did_set = self._shutdown_once.do_once(_shutdown)
201201
if not did_set:
202202
_logger.warning("Can't shutdown multiple times")
203-
return False
203+
return
204204

205205
self._shutdown_event.set()
206206
self._daemon_thread.join()
207207
self._exporter.shutdown()
208-
return True

opentelemetry-sdk/src/opentelemetry/sdk/_metrics/metric_reader.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def _receive_metrics(self, metrics: Iterable[Metric]):
6565
"""Called by `MetricReader.collect` when it receives a batch of metrics"""
6666

6767
@abstractmethod
68-
def shutdown(self) -> bool:
68+
def shutdown(self):
6969
"""Shuts down the MetricReader. This method provides a way
7070
for the MetricReader to do any cleanup required. A metric reader can
7171
only be shutdown once, any subsequent calls are ignored and return

opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def test_converts_metrics_to_list(self):
6363

6464
def test_shutdown(self):
6565
# shutdown should always be successful
66-
self.assertTrue(InMemoryMetricReader().shutdown())
66+
self.assertIsNone(InMemoryMetricReader().shutdown())
6767

6868
def test_integration(self):
6969
reader = InMemoryMetricReader()

opentelemetry-sdk/tests/metrics/test_metrics.py

+24-12
Original file line numberDiff line numberDiff line change
@@ -151,33 +151,45 @@ def test_shutdown(self):
151151

152152
mock_metric_reader_0 = MagicMock(
153153
**{
154-
"shutdown.return_value": False,
155-
"__str__.return_value": "mock_metric_reader_0",
154+
"shutdown.side_effect": ZeroDivisionError(),
155+
}
156+
)
157+
mock_metric_reader_1 = MagicMock(
158+
**{
159+
"shutdown.side_effect": AssertionError(),
156160
}
157161
)
158-
mock_metric_reader_1 = Mock(**{"shutdown.return_value": True})
159162

160163
meter_provider = MeterProvider(
161164
metric_readers=[mock_metric_reader_0, mock_metric_reader_1]
162165
)
163166

164-
with self.assertLogs(level=WARNING) as log:
165-
self.assertFalse(meter_provider.shutdown())
166-
self.assertEqual(
167-
log.records[0].getMessage(),
168-
"MetricReader mock_metric_reader_0 failed to shutdown",
169-
)
167+
with self.assertRaises(Exception) as error:
168+
meter_provider.shutdown()
169+
170+
error = error.exception
171+
172+
self.assertEqual(
173+
str(error),
174+
(
175+
"MeterProvider.shutdown failed because the following "
176+
"metric readers failed during shutdown:\n"
177+
"MagicMock: ZeroDivisionError()\n"
178+
"MagicMock: AssertionError()"
179+
),
180+
)
181+
170182
mock_metric_reader_0.shutdown.assert_called_once()
171183
mock_metric_reader_1.shutdown.assert_called_once()
172184

173-
mock_metric_reader_0 = Mock(**{"shutdown.return_value": True})
174-
mock_metric_reader_1 = Mock(**{"shutdown.return_value": True})
185+
mock_metric_reader_0 = Mock()
186+
mock_metric_reader_1 = Mock()
175187

176188
meter_provider = MeterProvider(
177189
metric_readers=[mock_metric_reader_0, mock_metric_reader_1]
178190
)
179191

180-
self.assertTrue(meter_provider.shutdown())
192+
self.assertIsNone(meter_provider.shutdown())
181193
mock_metric_reader_0.shutdown.assert_called_once()
182194
mock_metric_reader_1.shutdown.assert_called_once()
183195

0 commit comments

Comments
 (0)