Skip to content

Commit ca6ccdb

Browse files
authored
feat(worker): Make OTel export period configurable (#977)
1 parent b39f7f3 commit ca6ccdb

File tree

4 files changed

+65
-7
lines changed

4 files changed

+65
-7
lines changed

packages/core-bridge/index.d.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ import type { TLSConfig } from '@temporalio/common/lib/internal-non-workflow';
33

44
export { TLSConfig };
55

6+
type Shadow<Base, New> = Base extends object
7+
? New extends object
8+
? {
9+
[K in keyof Base | keyof New]: K extends keyof Base
10+
? K extends keyof New
11+
? Shadow<Base[K], New[K]>
12+
: Base[K]
13+
: New[K];
14+
}
15+
: New
16+
: New;
17+
618
export interface RetryOptions {
719
/** Initial wait time before the first retry. */
820
initialInterval: number;
@@ -98,9 +110,22 @@ export interface OtelCollectorExporter {
98110
* Optional set of HTTP request headers to send to Collector (e.g. for authentication)
99111
*/
100112
headers?: Record<string, string>;
113+
/**
114+
* Specify how frequently in metrics should be exported.
115+
*
116+
* @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
117+
* @defaults 1 second
118+
*/
119+
metricsExportInterval?: string | number;
101120
};
102121
}
103122

123+
/** @experimental */
124+
export type CompiledOtelTraceExporter = Shadow<OtelCollectorExporter, { otel: { metricsExportInterval?: never } }>;
125+
126+
/** @experimental */
127+
export type CompiledOtelMetricsExporter = Shadow<OtelCollectorExporter, { otel: { metricsExportInterval: number } }>;
128+
104129
/**
105130
* Prometheus metrics exporter options
106131
*
@@ -224,8 +249,10 @@ export type CompiledTelemetryOptions = {
224249
);
225250
tracing?: {
226251
filter: string;
227-
} & TraceExporter;
228-
metrics?: MetricsExporter;
252+
} & CompiledOtelTraceExporter;
253+
metrics?: {
254+
temporality?: 'cumulative' | 'delta';
255+
} & (PrometheusMetricsExporter | CompiledOtelMetricsExporter);
229256
};
230257

231258
export interface WorkerOptions {

packages/core-bridge/src/conversions.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,14 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
231231
} else {
232232
Default::default()
233233
};
234+
let metric_periodicity = Some(Duration::from_millis(js_value_getter!(
235+
cx,
236+
otel,
237+
"metricPeriodicity",
238+
JsNumber
239+
) as u64));
234240
telemetry_opts
235-
.metrics(MetricsExporter::Otel(OtelCollectorOptions { url, headers }));
241+
.metrics(MetricsExporter::Otel(OtelCollectorOptions { url, headers, metric_periodicity }));
236242
} else {
237243
cx.throw_type_error(
238244
"Invalid telemetryOptions.metrics, missing `prometheus` or `otel` option",
@@ -256,7 +262,7 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
256262
};
257263
telemetry_opts.tracing(TraceExportConfig {
258264
filter,
259-
exporter: TraceExporter::Otel(OtelCollectorOptions { url, headers }),
265+
exporter: TraceExporter::Otel(OtelCollectorOptions { url, headers, metric_periodicity: None }),
260266
});
261267
} else {
262268
cx.throw_type_error(

packages/worker/src/runtime.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
TelemetryOptions,
88
CompiledTelemetryOptions,
99
ForwardLogger,
10+
MetricsExporter,
11+
OtelCollectorExporter,
1012
} from '@temporalio/core-bridge';
1113
import { filterNullAndUndefined, normalizeTlsConfig } from '@temporalio/common/lib/internal-non-workflow';
1214
import { IllegalStateError } from '@temporalio/common';
@@ -24,13 +26,18 @@ import { History } from '@temporalio/common/lib/proto-utils';
2426
import * as v8 from 'v8';
2527
import * as fs from 'fs';
2628
import * as os from 'os';
29+
import { msToNumber } from '@temporalio/common/lib/time';
2730

2831
export { History };
2932

3033
function isForwardingLogger(opts: TelemetryOptions['logging']): opts is ForwardLogger {
3134
return Object.hasOwnProperty.call(opts, 'forward');
3235
}
3336

37+
function isOtelCollectorExporter(opts: MetricsExporter): opts is OtelCollectorExporter {
38+
return Object.hasOwnProperty.call(opts, 'otel');
39+
}
40+
3441
/**
3542
* Options used to create a Core runtime
3643
*/
@@ -207,7 +214,7 @@ export class Runtime {
207214
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
208215
protected static compileOptions(options: RuntimeOptions): CompiledRuntimeOptions {
209216
// eslint-disable-next-line deprecation/deprecation
210-
const { logging, tracing, tracingFilter, ...otherTelemetryOpts } = options.telemetryOptions ?? {};
217+
const { logging, tracing, metrics, tracingFilter, ...otherTelemetryOpts } = options.telemetryOptions ?? {};
211218

212219
const defaultFilter = tracingFilter ?? makeTelemetryFilterString({ core: 'INFO', other: 'INFO' });
213220
const loggingFilter = logging?.filter;
@@ -232,9 +239,27 @@ export class Runtime {
232239
tracing: tracing?.otel && {
233240
filter: defaultFilter,
234241
otel: {
235-
...filterNullAndUndefined(tracing.otel),
242+
url: tracing.otel.url,
243+
headers: tracing.otel.headers,
244+
metricsExportInterval: undefined,
236245
},
237246
},
247+
metrics: metrics && {
248+
temporality: metrics.temporality,
249+
...(isOtelCollectorExporter(metrics)
250+
? {
251+
otel: {
252+
url: metrics.otel.url,
253+
headers: metrics.otel.headers,
254+
metricsExportInterval: msToNumber(metrics.otel.metricsExportInterval ?? '1s'),
255+
},
256+
}
257+
: {
258+
prometheus: {
259+
bindAddress: metrics.prometheus.bindAddress,
260+
},
261+
}),
262+
},
238263
...filterNullAndUndefined(otherTelemetryOpts ?? {}),
239264
},
240265
logger: options.logger ?? new DefaultLogger('INFO'),

0 commit comments

Comments
 (0)