Skip to content

Commit fa04bc6

Browse files
authored
Add OpenTelemetry metrics exporter built on MetricBuffer (#1701)
1 parent 17b44ab commit fa04bc6

6 files changed

Lines changed: 568 additions & 5 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ to include examples, links to docs, or any other relevant information.
174174
This lets types with transfer type converters delegate their wire representation to the
175175
configured payload converter, preserving SDK behavior such as serialization
176176
contexts.
177+
- Added `temporalio.contrib.opentelemetry.MetricsExporter`, which drains a
178+
`temporalio.runtime.MetricBuffer` on a fixed interval and exports through a
179+
real OpenTelemetry `MeterProvider`, giving SDK/Core metrics access to
180+
standard OTel features (views, resource, exemplars). Experimental.
177181
- Added `TLSConfig.verification_server_name` to verify the server certificate against a fixed name
178182
instead of the connection's server name. Unlike `domain`, it does not change the TLS SNI or
179183
HTTP/2 authority values, which keep following the connected host, so it can be used when the

‎README.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ informal introduction to the features and their implementation.
113113
- [Observability](#observability)
114114
- [Metrics](#metrics)
115115
- [OpenTelemetry Tracing](#opentelemetry-tracing)
116+
- [OpenTelemetry Metrics](#opentelemetry-metrics)
116117
- [Protobuf 3.x vs 4.x](#protobuf-3x-vs-4x)
117118
- [Known Compatibility Issues](#known-compatibility-issues)
118119
- [gevent Patching](#gevent-patching)
@@ -1990,6 +1991,40 @@ as an interceptor on the `interceptors` argument of `Client.connect`. When set,
19901991
calls and for all activity and workflow invocations on the worker, spans will be created and properly serialized through
19911992
the server to give one proper trace for a workflow execution.
19921993

1994+
#### OpenTelemetry Metrics
1995+
1996+
Metrics support also requires the `opentelemetry` extra (see above). If you want your Temporal SDK/Core metrics (and
1997+
any custom metrics recorded via `activity.metric_meter()`/`workflow.metric_meter()`) to flow through the standard
1998+
OpenTelemetry metrics pipeline (views, resources, any OTel-compatible backend) rather than only through
1999+
`PrometheusConfig`/`OpenTelemetryConfig`, set a `temporalio.runtime.MetricBuffer` as the `metrics` on
2000+
`TelemetryConfig`, then drain it into a real OpenTelemetry `MeterProvider` using
2001+
`temporalio.contrib.opentelemetry.MetricsExporter`:
2002+
2003+
```python
2004+
from datetime import timedelta
2005+
2006+
from opentelemetry.sdk.metrics import MeterProvider
2007+
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader
2008+
2009+
from temporalio.client import Client
2010+
from temporalio.contrib.opentelemetry import MetricsExporter
2011+
from temporalio.runtime import MetricBuffer, Runtime, TelemetryConfig
2012+
2013+
buffer = MetricBuffer(10_000)
2014+
runtime = Runtime(telemetry=TelemetryConfig(metrics=buffer))
2015+
meter_provider = MeterProvider(
2016+
metric_readers=[PeriodicExportingMetricReader(ConsoleMetricExporter(), export_interval_millis=5000)]
2017+
)
2018+
2019+
async with MetricsExporter(buffer, meter_provider):
2020+
client = await Client.connect("localhost:7233", runtime=runtime)
2021+
# ... run workers/workflows while the exporter drains the buffer in the background
2022+
```
2023+
2024+
`MetricsExporter` must be running (via `async with` or manual `run()`/`shutdown()`) for as long as metrics should be
2025+
exported, since it works by draining the buffer on a fixed interval (`poll_interval`, default one second) -- per the
2026+
warning on `MetricBuffer`, updates are dropped if the buffer isn't drained regularly.
2027+
19932028
### Protobuf 3.x vs 4.x
19942029

19952030
Python currently has two somewhat-incompatible protobuf library versions - the 3.x series and the 4.x series. Python

‎temporalio/contrib/opentelemetry/README.md‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
# OpenTelemetry Integration for Temporal Python SDK
22

3-
This package provides OpenTelemetry tracing integration for Temporal workflows, activities, and other operations. It includes automatic span creation and propagation for distributed tracing across your Temporal applications.
3+
This package provides OpenTelemetry tracing and metrics integration for Temporal workflows, activities, and other operations. It includes automatic span creation and propagation for distributed tracing, and a `MetricsExporter` for exporting Temporal SDK/Core metrics through the standard OpenTelemetry metrics API, across your Temporal applications.
4+
5+
## Metrics
6+
7+
`MetricsExporter` drains a `temporalio.runtime.MetricBuffer` into an OpenTelemetry `MeterProvider`, so Temporal's own SDK/Core metrics (and any custom metrics recorded via `activity.metric_meter()`/`workflow.metric_meter()`) can be exported through the standard OpenTelemetry metrics pipeline (views, resources, any OTel-compatible backend) instead of only through `PrometheusConfig`/`OpenTelemetryConfig`.
8+
9+
```python
10+
from opentelemetry.sdk.metrics import MeterProvider
11+
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader
12+
from temporalio.contrib.opentelemetry import MetricsExporter
13+
from temporalio.runtime import MetricBuffer, Runtime, TelemetryConfig
14+
15+
buffer = MetricBuffer(10_000)
16+
runtime = Runtime(telemetry=TelemetryConfig(metrics=buffer))
17+
meter_provider = MeterProvider(
18+
metric_readers=[PeriodicExportingMetricReader(ConsoleMetricExporter())]
19+
)
20+
21+
async with MetricsExporter(buffer, meter_provider):
22+
client = await Client.connect("localhost:7233", runtime=runtime)
23+
...
24+
```
25+
26+
**Note:** the `Runtime` must be constructed with the buffer attached *before* `MetricsExporter` is started, and the exporter must keep running (it polls on a fixed interval) for as long as metrics should be exported.
427

528
## Overview
629

‎temporalio/contrib/opentelemetry/__init__.py‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""OpenTelemetry v2 integration for Temporal SDK.
22
3-
This package provides OpenTelemetry tracing integration for Temporal workflows,
4-
activities, and other operations. It includes automatic span creation and
5-
propagation for distributed tracing. It also provides replay-safe wrappers for
6-
the global OpenTelemetry tracer, meter, and logger providers.
3+
This package provides OpenTelemetry tracing and metrics integration for
4+
Temporal workflows, activities, and other operations. It includes automatic
5+
span creation and propagation for distributed tracing, a
6+
:py:class:`MetricsExporter` for exporting Temporal SDK/Core metrics through
7+
the standard OpenTelemetry metrics API, and replay-safe wrappers for the
8+
global OpenTelemetry tracer, meter, and logger providers.
79
"""
810

911
from temporalio.contrib.opentelemetry._interceptor import (
@@ -16,6 +18,7 @@
1618
from temporalio.contrib.opentelemetry._meter_provider import (
1719
ReplaySafeMeterProvider,
1820
)
21+
from temporalio.contrib.opentelemetry._metrics_exporter import MetricsExporter
1922
from temporalio.contrib.opentelemetry._otel_interceptor import OpenTelemetryInterceptor
2023
from temporalio.contrib.opentelemetry._plugin import OpenTelemetryPlugin
2124
from temporalio.contrib.opentelemetry._tracer_provider import (
@@ -28,6 +31,7 @@
2831
"TracingWorkflowInboundInterceptor",
2932
"OpenTelemetryInterceptor",
3033
"OpenTelemetryPlugin",
34+
"MetricsExporter",
3135
"ReplaySafeLoggerProvider",
3236
"ReplaySafeMeterProvider",
3337
"ReplaySafeTracerProvider",

0 commit comments

Comments
 (0)