Skip to content

Commit aa829d3

Browse files
authored
Update core/tonic, update metric options, remove core tracing (#380)
1 parent 40daaaa commit aa829d3

File tree

9 files changed

+472
-433
lines changed

9 files changed

+472
-433
lines changed

temporalio/bridge/Cargo.lock

Lines changed: 329 additions & 335 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

temporalio/bridge/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ temporal-sdk-core-api = { version = "0.1.0", path = "./sdk-core/core-api" }
2121
temporal-sdk-core-protos = { version = "0.1.0", path = "./sdk-core/sdk-core-protos" }
2222
tokio = "1.26"
2323
tokio-stream = "0.1"
24-
tonic = "0.8"
24+
tonic = "0.9"
2525
tracing = "0.1"
2626
url = "2.2"
2727

temporalio/bridge/runtime.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@ def __init__(self, *, telemetry: TelemetryConfig) -> None:
2626
self._ref = temporalio.bridge.temporal_sdk_bridge.init_runtime(telemetry)
2727

2828

29-
@dataclass(frozen=True)
30-
class TracingConfig:
31-
"""Python representation of the Rust struct for tracing config."""
32-
33-
filter: str
34-
opentelemetry: OpenTelemetryConfig
35-
36-
3729
@dataclass(frozen=True)
3830
class LoggingConfig:
3931
"""Python representation of the Rust struct for logging config."""
@@ -48,6 +40,9 @@ class MetricsConfig:
4840

4941
opentelemetry: Optional[OpenTelemetryConfig]
5042
prometheus: Optional[PrometheusConfig]
43+
attach_service_name: bool
44+
global_tags: Optional[Mapping[str, str]]
45+
metric_prefix: Optional[str]
5146

5247

5348
@dataclass(frozen=True)
@@ -57,20 +52,21 @@ class OpenTelemetryConfig:
5752
url: str
5853
headers: Mapping[str, str]
5954
metric_periodicity_millis: Optional[int]
55+
metric_temporality_delta: bool
6056

6157

6258
@dataclass(frozen=True)
6359
class PrometheusConfig:
6460
"""Python representation of the Rust struct for Prometheus config."""
6561

6662
bind_address: str
63+
counters_total_suffix: bool
64+
unit_suffix: bool
6765

6866

6967
@dataclass(frozen=True)
7068
class TelemetryConfig:
7169
"""Python representation of the Rust struct for telemetry config."""
7270

73-
tracing: Optional[TracingConfig]
7471
logging: Optional[LoggingConfig]
7572
metrics: Optional[MetricsConfig]
76-
global_tags: Mapping[str, str]

temporalio/bridge/src/client.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ pub fn connect_client<'a>(
7878
runtime_ref.runtime.future_into_py(py, async move {
7979
Ok(ClientRef {
8080
retry_client: opts
81-
.connect_no_namespace(runtime.core.metric_meter().as_deref(), headers)
81+
.connect_no_namespace(
82+
runtime.core.telemetry().get_temporal_metric_meter(),
83+
headers,
84+
)
8285
.await
8386
.map_err(|err| {
8487
PyRuntimeError::new_err(format!("Failed client connect: {}", err))

temporalio/bridge/src/runtime.rs

Lines changed: 100 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ use std::pin::Pin;
88
use std::str::FromStr;
99
use std::sync::Arc;
1010
use std::time::Duration;
11+
use temporal_sdk_core::telemetry::{build_otlp_metric_exporter, start_prometheus_metric_exporter};
1112
use temporal_sdk_core::CoreRuntime;
13+
use temporal_sdk_core_api::telemetry::metrics::CoreMeter;
1214
use temporal_sdk_core_api::telemetry::{
13-
Logger, MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
14-
TraceExportConfig, TraceExporter,
15+
Logger, MetricTemporality, OtelCollectorOptionsBuilder, PrometheusExporterOptionsBuilder,
16+
TelemetryOptions, TelemetryOptionsBuilder,
1517
};
1618
use url::Url;
1719

@@ -27,16 +29,8 @@ pub(crate) struct Runtime {
2729

2830
#[derive(FromPyObject)]
2931
pub struct TelemetryConfig {
30-
tracing: Option<TracingConfig>,
3132
logging: Option<LoggingConfig>,
3233
metrics: Option<MetricsConfig>,
33-
global_tags: Option<HashMap<String, String>>
34-
}
35-
36-
#[derive(FromPyObject)]
37-
pub struct TracingConfig {
38-
filter: String,
39-
opentelemetry: OpenTelemetryConfig,
4034
}
4135

4236
#[derive(FromPyObject)]
@@ -49,32 +43,44 @@ pub struct LoggingConfig {
4943
pub struct MetricsConfig {
5044
opentelemetry: Option<OpenTelemetryConfig>,
5145
prometheus: Option<PrometheusConfig>,
46+
attach_service_name: bool,
47+
global_tags: Option<HashMap<String, String>>,
48+
metric_prefix: Option<String>,
5249
}
5350

5451
#[derive(FromPyObject)]
5552
pub struct OpenTelemetryConfig {
5653
url: String,
5754
headers: HashMap<String, String>,
5855
metric_periodicity_millis: Option<u64>,
56+
metric_temporality_delta: bool,
5957
}
6058

6159
#[derive(FromPyObject)]
6260
pub struct PrometheusConfig {
6361
bind_address: String,
62+
counters_total_suffix: bool,
63+
unit_suffix: bool,
6464
}
6565

6666
pub fn init_runtime(telemetry_config: TelemetryConfig) -> PyResult<RuntimeRef> {
67+
let mut core = CoreRuntime::new(
68+
// We don't move telemetry config here because we need it for
69+
// late-binding metrics
70+
(&telemetry_config).try_into()?,
71+
tokio::runtime::Builder::new_multi_thread(),
72+
)
73+
.map_err(|err| PyRuntimeError::new_err(format!("Failed initializing telemetry: {}", err)))?;
74+
// We late-bind the metrics after core runtime is created since it needs
75+
// the Tokio handle
76+
if let Some(metrics_conf) = telemetry_config.metrics {
77+
let _guard = core.tokio_handle().enter();
78+
core.telemetry_mut()
79+
.attach_late_init_metrics(metrics_conf.try_into()?);
80+
}
6781
Ok(RuntimeRef {
6882
runtime: Runtime {
69-
core: Arc::new(
70-
CoreRuntime::new(
71-
telemetry_config.try_into()?,
72-
tokio::runtime::Builder::new_multi_thread(),
73-
)
74-
.map_err(|err| {
75-
PyRuntimeError::new_err(format!("Failed initializing telemetry: {}", err))
76-
})?,
77-
),
83+
core: Arc::new(core),
7884
},
7985
})
8086
}
@@ -94,61 +100,97 @@ impl Runtime {
94100
}
95101
}
96102

97-
impl TryFrom<TelemetryConfig> for TelemetryOptions {
103+
impl TryFrom<&TelemetryConfig> for TelemetryOptions {
98104
type Error = PyErr;
99105

100-
fn try_from(conf: TelemetryConfig) -> PyResult<Self> {
106+
fn try_from(conf: &TelemetryConfig) -> PyResult<Self> {
101107
let mut build = TelemetryOptionsBuilder::default();
102-
if let Some(v) = conf.tracing {
103-
build.tracing(TraceExportConfig {
104-
filter: v.filter,
105-
exporter: TraceExporter::Otel(v.opentelemetry.try_into()?),
106-
});
107-
}
108-
if let Some(v) = conf.logging {
109-
build.logging(if v.forward {
110-
Logger::Forward { filter: v.filter }
111-
} else {
112-
Logger::Console { filter: v.filter }
113-
});
114-
}
115-
if let Some(v) = conf.metrics {
116-
build.metrics(if let Some(t) = v.opentelemetry {
117-
if v.prometheus.is_some() {
118-
return Err(PyValueError::new_err(
119-
"Cannot have OpenTelemetry and Prometheus metrics",
120-
));
108+
if let Some(logging_conf) = &conf.logging {
109+
build.logging(if logging_conf.forward {
110+
Logger::Forward {
111+
filter: logging_conf.filter.to_string(),
121112
}
122-
MetricsExporter::Otel(t.try_into()?)
123-
} else if let Some(t) = v.prometheus {
124-
MetricsExporter::Prometheus(SocketAddr::from_str(&t.bind_address).map_err(
125-
|err| PyValueError::new_err(format!("Invalid Prometheus address: {}", err)),
126-
)?)
127113
} else {
128-
return Err(PyValueError::new_err(
129-
"Either OpenTelemetry or Prometheus config must be provided",
130-
));
114+
Logger::Console {
115+
filter: logging_conf.filter.to_string(),
116+
}
131117
});
132118
}
133-
if let Some(v) = conf.global_tags {
134-
build.global_tags(v);
119+
if let Some(metrics_conf) = &conf.metrics {
120+
// Note, actual metrics instance is late-bound in init_runtime
121+
build.attach_service_name(metrics_conf.attach_service_name);
122+
if let Some(prefix) = &metrics_conf.metric_prefix {
123+
build.metric_prefix(prefix.to_string());
124+
}
135125
}
136126
build
137127
.build()
138128
.map_err(|err| PyValueError::new_err(format!("Invalid telemetry config: {}", err)))
139129
}
140130
}
141131

142-
impl TryFrom<OpenTelemetryConfig> for OtelCollectorOptions {
132+
impl TryFrom<MetricsConfig> for Arc<dyn CoreMeter> {
143133
type Error = PyErr;
144134

145-
fn try_from(conf: OpenTelemetryConfig) -> PyResult<Self> {
146-
Ok(OtelCollectorOptions {
147-
url: Url::parse(&conf.url)
148-
.map_err(|err| PyValueError::new_err(format!("Invalid OTel URL: {}", err)))?,
149-
headers: conf.headers,
150-
metric_periodicity: conf.metric_periodicity_millis.map(Duration::from_millis),
151-
})
135+
fn try_from(conf: MetricsConfig) -> PyResult<Self> {
136+
if let Some(otel_conf) = conf.opentelemetry {
137+
if !conf.prometheus.is_none() {
138+
return Err(PyValueError::new_err(
139+
"Cannot have OpenTelemetry and Prometheus metrics",
140+
));
141+
}
142+
143+
// Build OTel exporter
144+
let mut build = OtelCollectorOptionsBuilder::default();
145+
build
146+
.url(
147+
Url::parse(&otel_conf.url).map_err(|err| {
148+
PyValueError::new_err(format!("Invalid OTel URL: {}", err))
149+
})?,
150+
)
151+
.headers(otel_conf.headers);
152+
if let Some(period) = otel_conf.metric_periodicity_millis {
153+
build.metric_periodicity(Duration::from_millis(period));
154+
}
155+
if otel_conf.metric_temporality_delta {
156+
build.metric_temporality(MetricTemporality::Delta);
157+
}
158+
if let Some(global_tags) = conf.global_tags {
159+
build.global_tags(global_tags);
160+
}
161+
let otel_options = build
162+
.build()
163+
.map_err(|err| PyValueError::new_err(format!("Invalid OTel config: {}", err)))?;
164+
Ok(Arc::new(build_otlp_metric_exporter(otel_options).map_err(
165+
|err| PyValueError::new_err(format!("Failed building OTel exporter: {}", err)),
166+
)?))
167+
} else if let Some(prom_conf) = conf.prometheus {
168+
// Start prom exporter
169+
let mut build = PrometheusExporterOptionsBuilder::default();
170+
build
171+
.socket_addr(
172+
SocketAddr::from_str(&prom_conf.bind_address).map_err(|err| {
173+
PyValueError::new_err(format!("Invalid Prometheus address: {}", err))
174+
})?,
175+
)
176+
.counters_total_suffix(prom_conf.counters_total_suffix)
177+
.unit_suffix(prom_conf.unit_suffix);
178+
if let Some(global_tags) = conf.global_tags {
179+
build.global_tags(global_tags);
180+
}
181+
let prom_options = build.build().map_err(|err| {
182+
PyValueError::new_err(format!("Invalid Prometheus config: {}", err))
183+
})?;
184+
Ok(start_prometheus_metric_exporter(prom_options)
185+
.map_err(|err| {
186+
PyValueError::new_err(format!("Failed starting Prometheus exporter: {}", err))
187+
})?
188+
.meter)
189+
} else {
190+
Err(PyValueError::new_err(
191+
"Either OpenTelemetry or Prometheus config must be provided",
192+
))
193+
}
152194
}
153195
}
154196

temporalio/bridge/src/worker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub struct WorkerConfig {
5050
macro_rules! enter_sync {
5151
($runtime:expr) => {
5252
temporal_sdk_core::telemetry::set_trace_subscriber_for_current_thread(
53-
$runtime.core.trace_subscriber(),
53+
$runtime.core.telemetry().trace_subscriber(),
5454
);
5555
let _guard = $runtime.core.tokio_handle().enter();
5656
};

0 commit comments

Comments
 (0)