Skip to content

Commit 11c51db

Browse files
committed
Add timeouts to metric SDK
1 parent 5456988 commit 11c51db

File tree

9 files changed

+65
-30
lines changed

9 files changed

+65
-30
lines changed

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_metric_exporter/__init__.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,13 @@ def _translate_data(
168168
)
169169
)
170170

171-
def export(self, metrics: Sequence[Metric]) -> MetricExportResult:
171+
def export(
172+
self,
173+
metrics: Sequence[Metric],
174+
timeout_millis: Optional[float] = None,
175+
**kwargs
176+
) -> MetricExportResult:
172177
return self._export(metrics)
173178

174-
def shutdown(self):
179+
def shutdown(self, *args, **kwargs):
175180
pass

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from abc import ABC, abstractmethod
1919
from collections.abc import Sequence
2020
from os import environ
21-
from time import sleep
21+
from time import sleep, time_ns
2222
from typing import Any, Callable, Dict, Generic, List, Optional, Tuple, Union
2323
from typing import Sequence as TypingSequence
2424
from typing import TypeVar
@@ -277,7 +277,15 @@ def _translate_attributes(self, attributes) -> TypingSequence[KeyValue]:
277277
logger.exception(error)
278278
return output
279279

280-
def _export(self, data: TypingSequence[SDKDataT]) -> ExportResultT:
280+
def _export(
281+
self,
282+
data: TypingSequence[SDKDataT],
283+
timeout_millis: Optional[float] = None,
284+
) -> ExportResultT:
285+
if timeout_millis is not None:
286+
timeout_seconds = timeout_millis / 10**3
287+
else:
288+
timeout_seconds = self._timeout
281289

282290
max_value = 64
283291
# expo returns a generator that yields delay values which grow
@@ -292,7 +300,7 @@ def _export(self, data: TypingSequence[SDKDataT]) -> ExportResultT:
292300
self._client.Export(
293301
request=self._translate_data(data),
294302
metadata=self._headers,
295-
timeout=self._timeout,
303+
timeout=timeout_seconds,
296304
)
297305

298306
return self._result.SUCCESS

exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,8 @@ def _receive_metrics(self, metrics: Iterable[Metric]) -> None:
115115
return
116116
self._collector.add_metrics_data(metrics)
117117

118-
def shutdown(self) -> bool:
118+
def shutdown(self, *args, **kwargs) -> None:
119119
REGISTRY.unregister(self._collector)
120-
return True
121120

122121

123122
class _CustomCollector:

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ def force_flush(self) -> bool:
374374
metric_reader.collect()
375375
return True
376376

377-
def shutdown(self):
377+
def shutdown(self, timeout_millis: float = 10_000):
378378
# FIXME implement a timeout
379379

380380
def _shutdown():

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

+27-9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from opentelemetry.sdk._metrics.metric_reader import MetricReader
3232
from opentelemetry.sdk._metrics.point import AggregationTemporality, Metric
3333
from opentelemetry.util._once import Once
34+
from opentelemetry.util._time import time_ns
3435

3536
_logger = logging.getLogger(__name__)
3637

@@ -52,7 +53,12 @@ class MetricExporter(ABC):
5253
"""
5354

5455
@abstractmethod
55-
def export(self, metrics: Sequence[Metric]) -> "MetricExportResult":
56+
def export(
57+
self,
58+
metrics: Sequence[Metric],
59+
timeout_millis: Optional[float] = None,
60+
**kwargs
61+
) -> "MetricExportResult":
5662
"""Exports a batch of telemetry data.
5763
5864
Args:
@@ -63,7 +69,9 @@ def export(self, metrics: Sequence[Metric]) -> "MetricExportResult":
6369
"""
6470

6571
@abstractmethod
66-
def shutdown(self) -> None:
72+
def shutdown(
73+
self, timeout_millis: Optional[float] = None, **kwargs
74+
) -> None:
6775
"""Shuts down the exporter.
6876
6977
Called when the SDK is shut down.
@@ -87,13 +95,15 @@ def __init__(
8795
self.out = out
8896
self.formatter = formatter
8997

90-
def export(self, metrics: Sequence[Metric]) -> MetricExportResult:
98+
def export(
99+
self, metrics: Sequence[Metric], *args, **kwargs
100+
) -> MetricExportResult:
91101
for metric in metrics:
92102
self.out.write(self.formatter(metric))
93103
self.out.flush()
94104
return MetricExportResult.SUCCESS
95105

96-
def shutdown(self) -> None:
106+
def shutdown(self, *args, **kwargs) -> None:
97107
pass
98108

99109

@@ -127,7 +137,7 @@ def _receive_metrics(self, metrics: Iterable[Metric]):
127137
with self._lock:
128138
self._metrics = list(metrics)
129139

130-
def shutdown(self):
140+
def shutdown(self, **kwargs):
131141
pass
132142

133143

@@ -198,12 +208,20 @@ def _receive_metrics(self, metrics: Iterable[Metric]) -> None:
198208
return
199209
token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
200210
try:
201-
self._exporter.export(metrics)
211+
self._exporter.export(
212+
metrics, timeout_millis=self._export_timeout_millis
213+
)
202214
except Exception as e: # pylint: disable=broad-except,invalid-name
203215
_logger.exception("Exception while exporting metrics %s", str(e))
204216
detach(token)
205217

206-
def shutdown(self):
218+
def shutdown(
219+
self, timeout_millis: Optional[float] = None, **kwargs
220+
) -> None:
221+
if timeout_millis is None:
222+
timeout_millis = self._export_timeout_millis
223+
deadline_nanos = time_ns() + timeout_millis * 10**3
224+
207225
def _shutdown():
208226
self._shutdown = True
209227

@@ -213,5 +231,5 @@ def _shutdown():
213231
return
214232

215233
self._shutdown_event.set()
216-
self._daemon_thread.join()
217-
self._exporter.shutdown()
234+
self._daemon_thread.join((deadline_nanos - time_ns()) / 10**9)
235+
self._exporter.shutdown((deadline_nanos - time_ns()) / 10**9)

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

+10-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from abc import ABC, abstractmethod
1616
from logging import getLogger
1717
from os import environ
18-
from typing import Callable, Dict, Iterable
18+
from typing import Callable, Dict, Iterable, Optional
1919

2020
from typing_extensions import final
2121

@@ -69,9 +69,6 @@ class MetricReader(ABC):
6969
default aggregations. The aggregation defined here will be
7070
overriden by an aggregation defined by a view that is not
7171
`DefaultAggregation`.
72-
73-
.. document protected _receive_metrics which is a intended to be overriden by subclass
74-
.. automethod:: _receive_metrics
7572
"""
7673

7774
# FIXME add :std:envvar:`OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE`
@@ -162,11 +159,17 @@ def _set_collect_callback(
162159
self._collect = func
163160

164161
@abstractmethod
165-
def _receive_metrics(self, metrics: Iterable[Metric]):
166-
"""Called by `MetricReader.collect` when it receives a batch of metrics"""
162+
def _receive_metrics(self, metrics: Iterable[Metric]) -> None:
163+
"""Called by `MetricReader.collect` when it receives a batch of metrics. Custom
164+
implementations of `MetricReader` should implement this method.
165+
166+
:meta public:
167+
"""
167168

168169
@abstractmethod
169-
def shutdown(self):
170+
def shutdown(
171+
self, timeout_millis: Optional[float] = None, **kwargs
172+
) -> None:
170173
"""Shuts down the MetricReader. This method provides a way
171174
for the MetricReader to do any cleanup required. A metric reader can
172175
only be shutdown once, any subsequent calls are ignored and return

opentelemetry-sdk/tests/metrics/test_metric_reader.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def __init__(
5151
def _receive_metrics(self, metrics):
5252
pass
5353

54-
def shutdown(self):
54+
def shutdown(self, *args, **kwargs):
5555
return True
5656

5757

opentelemetry-sdk/tests/metrics/test_metrics.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(self):
4949
def _receive_metrics(self, metrics):
5050
pass
5151

52-
def shutdown(self):
52+
def shutdown(self, *args, **kwargs):
5353
return True
5454

5555

@@ -415,12 +415,14 @@ def __init__(self):
415415
self.metrics = {}
416416
self._counter = 0
417417

418-
def export(self, metrics: Sequence[Metric]) -> MetricExportResult:
418+
def export(
419+
self, metrics: Sequence[Metric], *args, **kwargs
420+
) -> MetricExportResult:
419421
self.metrics[self._counter] = metrics
420422
self._counter += 1
421423
return MetricExportResult.SUCCESS
422424

423-
def shutdown(self) -> None:
425+
def shutdown(self, *args, **kwargs) -> None:
424426
pass
425427

426428

opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ def __init__(self, wait=0):
3333
self.metrics = []
3434
self._shutdown = False
3535

36-
def export(self, metrics):
36+
def export(self, metrics, *args, **kwargs):
3737
time.sleep(self.wait)
3838
self.metrics.extend(metrics)
3939
return True
4040

41-
def shutdown(self):
41+
def shutdown(self, *args, **kwargs):
4242
self._shutdown = True
4343

4444

0 commit comments

Comments
 (0)