@aztec/telemetry-client 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dest/attributes.js +66 -0
  2. package/dest/aztec_resource_detector.js +18 -0
  3. package/dest/bench.js +98 -0
  4. package/dest/config.js +66 -0
  5. package/dest/event_loop_monitor.js +93 -0
  6. package/dest/histogram_utils.js +47 -0
  7. package/dest/index.js +10 -0
  8. package/dest/lmdb_metrics.js +42 -0
  9. package/dest/metrics.js +123 -0
  10. package/dest/noop.js +71 -0
  11. package/dest/otel.js +237 -0
  12. package/dest/otel_filter_metric_exporter.js +33 -0
  13. package/dest/otel_logger_provider.js +25 -0
  14. package/dest/otel_propagation.js +44 -0
  15. package/dest/otel_resource.js +16 -0
  16. package/dest/prom_otel_adapter.js +183 -0
  17. package/dest/start.js +24 -0
  18. package/dest/telemetry.js +116 -0
  19. package/dest/vendor/attributes.js +5 -0
  20. package/dest/vendor/otel-pino-stream.js +229 -0
  21. package/dest/with_tracer.js +32 -0
  22. package/dest/wrappers/fetch.js +39 -0
  23. package/dest/wrappers/index.js +3 -0
  24. package/dest/wrappers/json_rpc_server.js +11 -0
  25. package/dest/wrappers/l2_block_stream.js +26 -0
  26. package/package.json +92 -0
  27. package/src/attributes.ts +113 -0
  28. package/src/aztec_resource_detector.ts +28 -0
  29. package/src/bench.ts +153 -0
  30. package/src/config.ts +89 -0
  31. package/src/event_loop_monitor.ts +119 -0
  32. package/src/histogram_utils.ts +50 -0
  33. package/src/index.ts +10 -0
  34. package/src/lmdb_metrics.ts +45 -0
  35. package/src/metrics.ts +151 -0
  36. package/src/noop.ts +91 -0
  37. package/src/otel.ts +299 -0
  38. package/src/otel_filter_metric_exporter.ts +38 -0
  39. package/src/otel_logger_provider.ts +31 -0
  40. package/src/otel_propagation.ts +50 -0
  41. package/src/otel_resource.ts +26 -0
  42. package/src/prom_otel_adapter.ts +326 -0
  43. package/src/start.ts +33 -0
  44. package/src/telemetry.ts +267 -0
  45. package/src/vendor/attributes.ts +5 -0
  46. package/src/vendor/otel-pino-stream.ts +282 -0
  47. package/src/with_tracer.ts +35 -0
  48. package/src/wrappers/fetch.ts +52 -0
  49. package/src/wrappers/index.ts +3 -0
  50. package/src/wrappers/json_rpc_server.ts +15 -0
  51. package/src/wrappers/l2_block_stream.ts +37 -0
package/src/noop.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { type Meter, type Span, type SpanContext, type Tracer, createNoopMeter } from '@opentelemetry/api';
2
+
3
+ import { type TelemetryClient } from './telemetry.js';
4
+
5
+ export class NoopTelemetryClient implements TelemetryClient {
6
+ getMeter(): Meter {
7
+ return createNoopMeter();
8
+ }
9
+
10
+ getTracer(): Tracer {
11
+ return new NoopTracer();
12
+ }
13
+
14
+ stop(): Promise<void> {
15
+ return Promise.resolve();
16
+ }
17
+
18
+ flush(): Promise<void> {
19
+ return Promise.resolve();
20
+ }
21
+
22
+ isEnabled() {
23
+ return false;
24
+ }
25
+ }
26
+
27
+ // @opentelemetry/api internally uses NoopTracer and NoopSpan but they're not exported
28
+ // make our own versions
29
+ // https://github.com/open-telemetry/opentelemetry-js/issues/4518#issuecomment-2179405444
30
+ export class NoopTracer implements Tracer {
31
+ startSpan(): Span {
32
+ return new NoopSpan();
33
+ }
34
+
35
+ startActiveSpan<F extends (...args: any[]) => any>(_name: string, ...args: (unknown | F)[]): ReturnType<F> {
36
+ // there are three different signatures for startActiveSpan, grab the function, we don't care about the rest
37
+ const fn = args.find(arg => typeof arg === 'function') as F;
38
+ return fn(new NoopSpan());
39
+ }
40
+ }
41
+
42
+ class NoopSpan implements Span {
43
+ private recording: boolean = true;
44
+ addEvent(): this {
45
+ return this;
46
+ }
47
+
48
+ addLink(): this {
49
+ return this;
50
+ }
51
+
52
+ addLinks(): this {
53
+ return this;
54
+ }
55
+
56
+ end(): void {
57
+ this.recording = false;
58
+ }
59
+
60
+ isRecording(): boolean {
61
+ return this.recording;
62
+ }
63
+
64
+ recordException(): void {
65
+ return;
66
+ }
67
+
68
+ setAttribute(): this {
69
+ return this;
70
+ }
71
+
72
+ setAttributes(): this {
73
+ return this;
74
+ }
75
+
76
+ setStatus(): this {
77
+ return this;
78
+ }
79
+
80
+ spanContext(): SpanContext {
81
+ return {
82
+ spanId: '',
83
+ traceId: '',
84
+ traceFlags: 0,
85
+ };
86
+ }
87
+
88
+ updateName(): this {
89
+ return this;
90
+ }
91
+ }
package/src/otel.ts ADDED
@@ -0,0 +1,299 @@
1
+ import { type LogData, type Logger, addLogDataHandler } from '@aztec/foundation/log';
2
+
3
+ import { MetricExporter as GoogleCloudMetricExporter } from '@google-cloud/opentelemetry-cloud-monitoring-exporter';
4
+ import { TraceExporter as GoogleCloudTraceExporter } from '@google-cloud/opentelemetry-cloud-trace-exporter';
5
+ import {
6
+ DiagConsoleLogger,
7
+ DiagLogLevel,
8
+ type Meter,
9
+ type Tracer,
10
+ type TracerProvider,
11
+ context,
12
+ diag,
13
+ isSpanContextValid,
14
+ trace,
15
+ } from '@opentelemetry/api';
16
+ import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
17
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
18
+ import { HostMetrics } from '@opentelemetry/host-metrics';
19
+ import { type IResource } from '@opentelemetry/resources';
20
+ import { type LoggerProvider } from '@opentelemetry/sdk-logs';
21
+ import {
22
+ ExplicitBucketHistogramAggregation,
23
+ InstrumentType,
24
+ MeterProvider,
25
+ PeriodicExportingMetricReader,
26
+ type PeriodicExportingMetricReaderOptions,
27
+ View,
28
+ } from '@opentelemetry/sdk-metrics';
29
+ import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
30
+ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
31
+
32
+ import { type TelemetryClientConfig } from './config.js';
33
+ import { EventLoopMonitor } from './event_loop_monitor.js';
34
+ import { linearBuckets } from './histogram_utils.js';
35
+ import { OtelFilterMetricExporter } from './otel_filter_metric_exporter.js';
36
+ import { registerOtelLoggerProvider } from './otel_logger_provider.js';
37
+ import { getOtelResource } from './otel_resource.js';
38
+ import { type Gauge, type TelemetryClient } from './telemetry.js';
39
+
40
+ export type OpenTelemetryClientFactory = (resource: IResource, log: Logger) => OpenTelemetryClient;
41
+
42
+ export class OpenTelemetryClient implements TelemetryClient {
43
+ hostMetrics: HostMetrics | undefined;
44
+ eventLoopMonitor: EventLoopMonitor | undefined;
45
+ targetInfo: Gauge | undefined;
46
+ private meters: Map<string, Meter> = new Map<string, Meter>();
47
+ private tracers: Map<string, Tracer> = new Map<string, Tracer>();
48
+
49
+ protected constructor(
50
+ private resource: IResource,
51
+ private meterProvider: MeterProvider,
52
+ private traceProvider: TracerProvider,
53
+ private loggerProvider: LoggerProvider | undefined,
54
+ private log: Logger,
55
+ ) {}
56
+
57
+ getMeter(name: string): Meter {
58
+ let meter = this.meters.get(name);
59
+ if (!meter) {
60
+ meter = this.meterProvider.getMeter(name, this.resource.attributes[ATTR_SERVICE_VERSION] as string);
61
+ this.meters.set(name, meter);
62
+ }
63
+ return meter;
64
+ }
65
+
66
+ getTracer(name: string): Tracer {
67
+ let tracer = this.tracers.get(name);
68
+ if (!tracer) {
69
+ tracer = this.traceProvider.getTracer(name, this.resource.attributes[ATTR_SERVICE_VERSION] as string);
70
+ this.tracers.set(name, tracer);
71
+ }
72
+ return tracer;
73
+ }
74
+
75
+ public start() {
76
+ this.log.info('Starting OpenTelemetry client');
77
+ diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
78
+
79
+ // Add a callback to the logger to set context data from current trace
80
+ // Adapted from open-telemetry/opentelemetry-js-contrib PinoInstrumentation._getMixinFunction
81
+ addLogDataHandler((data: LogData) => {
82
+ const spanContext = trace.getSpan(context.active())?.spanContext();
83
+ return spanContext && isSpanContextValid(spanContext)
84
+ ? {
85
+ ...data,
86
+ ['trace_id']: spanContext.traceId,
87
+ ['span_id']: spanContext.spanId,
88
+ ['trace_flags']: `0${spanContext.traceFlags.toString(16)}`,
89
+ }
90
+ : data;
91
+ });
92
+
93
+ this.hostMetrics = new HostMetrics({
94
+ name: this.resource.attributes[ATTR_SERVICE_NAME] as string,
95
+ meterProvider: this.meterProvider,
96
+ });
97
+
98
+ this.eventLoopMonitor = new EventLoopMonitor(
99
+ this.meterProvider.getMeter(this.resource.attributes[ATTR_SERVICE_NAME] as string),
100
+ );
101
+
102
+ // See these two links for more information on providing target information:
103
+ // https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#resource-attributes
104
+ // https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#supporting-target-metadata-in-both-push-based-and-pull-based-systems
105
+ this.targetInfo = this.meterProvider.getMeter('target').createGauge('target_info', {
106
+ description: 'Target metadata',
107
+ });
108
+
109
+ this.targetInfo.record(1, this.resource.attributes);
110
+ this.hostMetrics.start();
111
+ this.eventLoopMonitor.start();
112
+ }
113
+
114
+ public isEnabled() {
115
+ return true;
116
+ }
117
+
118
+ public async flush() {
119
+ await Promise.all([
120
+ this.meterProvider.forceFlush(),
121
+ this.loggerProvider?.forceFlush(),
122
+ this.traceProvider instanceof NodeTracerProvider ? this.traceProvider.forceFlush() : Promise.resolve(),
123
+ ]);
124
+ }
125
+
126
+ public async stop() {
127
+ this.eventLoopMonitor?.stop();
128
+
129
+ const flushAndShutdown = async (provider?: { forceFlush: () => Promise<void>; shutdown: () => Promise<void> }) => {
130
+ if (!provider) {
131
+ return;
132
+ }
133
+ await provider.forceFlush();
134
+ await provider.shutdown();
135
+ };
136
+
137
+ await Promise.all([
138
+ flushAndShutdown(this.meterProvider),
139
+ flushAndShutdown(this.loggerProvider),
140
+ this.traceProvider instanceof NodeTracerProvider ? flushAndShutdown(this.traceProvider) : Promise.resolve(),
141
+ ]);
142
+ }
143
+
144
+ public static createMeterProvider(
145
+ resource: IResource,
146
+ options: Partial<PeriodicExportingMetricReaderOptions>,
147
+ ): MeterProvider {
148
+ return new MeterProvider({
149
+ resource,
150
+ readers: options.exporter
151
+ ? [new PeriodicExportingMetricReader(options as PeriodicExportingMetricReaderOptions)]
152
+ : [],
153
+
154
+ views: [
155
+ // Every histogram matching the selector (type + unit) gets these custom buckets assigned
156
+ new View({
157
+ instrumentType: InstrumentType.HISTOGRAM,
158
+ instrumentUnit: 'ms',
159
+ aggregation: new ExplicitBucketHistogramAggregation(
160
+ // 181 buckets between 5ms and 1hr
161
+ [
162
+ ...linearBuckets(5, 100, 20), // 20 buckets between 5 and 100ms
163
+ ...linearBuckets(100, 1_000, 20).slice(1), // another 20 buckets between 100ms and 1s. slice(1) to remove duplicate 100
164
+ ...linearBuckets(1_000, 10_000, 20).slice(1),
165
+ ...linearBuckets(10_000, 60_000, 20).slice(1),
166
+ ...linearBuckets(60_000, 300_000, 20).slice(1),
167
+ ...linearBuckets(300_000, 600_000, 20).slice(1),
168
+ ...linearBuckets(600_000, 1_200_000, 20).slice(1),
169
+ ...linearBuckets(1_200_000, 1_800_000, 20).slice(1),
170
+ ...linearBuckets(1_800_000, 3_600_000, 20).slice(1), // 1hr
171
+ ],
172
+ true,
173
+ ),
174
+ }),
175
+ new View({
176
+ instrumentType: InstrumentType.HISTOGRAM,
177
+ instrumentUnit: 'us',
178
+ aggregation: new ExplicitBucketHistogramAggregation(
179
+ [
180
+ ...linearBuckets(5, 100, 20), // 20 buckets between 5 and 100us
181
+ ...linearBuckets(100, 1_000, 20).slice(1), // another 20 buckets between 100us and 1ms. slice(1) to remove duplicate 100
182
+ ...linearBuckets(1_000, 10_000, 90).slice(1), // 90 buckets between 1ms and 10ms
183
+ ...linearBuckets(10_000, 100_000, 20).slice(1), // 20 buckets between 10ms and 100ms
184
+ ...linearBuckets(100_000, 1_000_000, 20).slice(1), // 20 buckets between 100ms and 1s
185
+ ...linearBuckets(1_000_000, 60_000_000, 20).slice(1), // 20 buckets between 1s and 1m
186
+ ],
187
+ true,
188
+ ),
189
+ }),
190
+ new View({
191
+ instrumentType: InstrumentType.HISTOGRAM,
192
+ instrumentUnit: 'By',
193
+ aggregation: new ExplicitBucketHistogramAggregation(
194
+ // 143 buckets between 32 bytes and 2MB
195
+ [
196
+ ...linearBuckets(2 ** 5, 2 ** 10, 31), // 32 bytes to 1KB at a resolution of 32 bytes
197
+ ...linearBuckets(2 ** 10, 2 ** 15, 31).slice(1), // 1KB to 32KB at a resolution of 1KB
198
+ ...linearBuckets(2 ** 15, 2 ** 18, 32).slice(1), // 32KB to 256KB at a resolution of 7KB
199
+ ...linearBuckets(2 ** 18, 2 ** 20, 32).slice(1), // 256KB to 1MB at a resolution of 24KB
200
+ ...linearBuckets(2 ** 20, 2 ** 21, 16).slice(1), // 1MB to 2MB at a resolution of 64KB
201
+ ],
202
+ true,
203
+ ),
204
+ }),
205
+ new View({
206
+ instrumentType: InstrumentType.HISTOGRAM,
207
+ instrumentUnit: 'gas/s',
208
+ aggregation: new ExplicitBucketHistogramAggregation(
209
+ [...linearBuckets(100_000, 10_000_000, 100), ...linearBuckets(10_000_000, 100_000_000, 100).slice(1)],
210
+ true,
211
+ ),
212
+ }),
213
+ new View({
214
+ instrumentType: InstrumentType.HISTOGRAM,
215
+ instrumentUnit: 'mana/s',
216
+ aggregation: new ExplicitBucketHistogramAggregation(
217
+ [...linearBuckets(100_000, 10_000_000, 100), ...linearBuckets(10_000_000, 100_000_000, 100).slice(1)],
218
+ true,
219
+ ),
220
+ }),
221
+ new View({
222
+ instrumentType: InstrumentType.HISTOGRAM,
223
+ instrumentUnit: 'gas/block',
224
+ aggregation: new ExplicitBucketHistogramAggregation(
225
+ [...linearBuckets(100_000, 10_000_000, 100), ...linearBuckets(10_000_000, 50_000_000, 50).slice(1)],
226
+ true,
227
+ ),
228
+ }),
229
+ new View({
230
+ instrumentType: InstrumentType.HISTOGRAM,
231
+ instrumentUnit: 'gas/tx',
232
+ aggregation: new ExplicitBucketHistogramAggregation(
233
+ [...linearBuckets(50_000, 1_000_000, 20), ...linearBuckets(1_000_000, 10_000_000, 100).slice(1)],
234
+ true,
235
+ ),
236
+ }),
237
+ ],
238
+ });
239
+ }
240
+
241
+ private static getGcloudClientFactory(config: TelemetryClientConfig): OpenTelemetryClientFactory {
242
+ return (resource: IResource, log: Logger) => {
243
+ const tracerProvider = new NodeTracerProvider({
244
+ resource,
245
+ spanProcessors: [new BatchSpanProcessor(new GoogleCloudTraceExporter({ resourceFilter: /.*/ }))],
246
+ });
247
+
248
+ tracerProvider.register();
249
+
250
+ const meterProvider = OpenTelemetryClient.createMeterProvider(resource, {
251
+ exporter: new OtelFilterMetricExporter(new GoogleCloudMetricExporter(), config.otelExcludeMetrics ?? []),
252
+ exportTimeoutMillis: config.otelExportTimeoutMs,
253
+ exportIntervalMillis: config.otelCollectIntervalMs,
254
+ });
255
+
256
+ return new OpenTelemetryClient(resource, meterProvider, tracerProvider, undefined, log);
257
+ };
258
+ }
259
+
260
+ private static getCustomClientFactory(config: TelemetryClientConfig): OpenTelemetryClientFactory {
261
+ return (resource: IResource, log: Logger) => {
262
+ const tracerProvider = new NodeTracerProvider({
263
+ resource,
264
+ spanProcessors: config.tracesCollectorUrl
265
+ ? [new BatchSpanProcessor(new OTLPTraceExporter({ url: config.tracesCollectorUrl.href }))]
266
+ : [],
267
+ });
268
+
269
+ tracerProvider.register();
270
+
271
+ const meterProvider = OpenTelemetryClient.createMeterProvider(resource, {
272
+ exporter: config.metricsCollectorUrl
273
+ ? new OtelFilterMetricExporter(
274
+ new OTLPMetricExporter({ url: config.metricsCollectorUrl.href }),
275
+ config.otelExcludeMetrics ?? [],
276
+ )
277
+ : undefined,
278
+ exportTimeoutMillis: config.otelExportTimeoutMs,
279
+ exportIntervalMillis: config.otelCollectIntervalMs,
280
+ });
281
+
282
+ const loggerProvider = registerOtelLoggerProvider(resource, config.logsCollectorUrl);
283
+
284
+ return new OpenTelemetryClient(resource, meterProvider, tracerProvider, loggerProvider, log);
285
+ };
286
+ }
287
+
288
+ public static createAndStart(config: TelemetryClientConfig, log: Logger): OpenTelemetryClient {
289
+ const resource = getOtelResource();
290
+ const factory = config.useGcloudObservability
291
+ ? OpenTelemetryClient.getGcloudClientFactory(config)
292
+ : OpenTelemetryClient.getCustomClientFactory(config);
293
+
294
+ const service = factory(resource, log);
295
+ service.start();
296
+
297
+ return service;
298
+ }
299
+ }
@@ -0,0 +1,38 @@
1
+ import { type ExportResult } from '@opentelemetry/core';
2
+ import { type MetricData, type PushMetricExporter, type ResourceMetrics } from '@opentelemetry/sdk-metrics';
3
+
4
+ export class OtelFilterMetricExporter implements PushMetricExporter {
5
+ constructor(private readonly exporter: PushMetricExporter, private readonly excludeMetricPrefixes: string[]) {
6
+ if (exporter.selectAggregation) {
7
+ (this as PushMetricExporter).selectAggregation = exporter.selectAggregation.bind(exporter);
8
+ }
9
+ if (exporter.selectAggregationTemporality) {
10
+ (this as PushMetricExporter).selectAggregationTemporality = exporter.selectAggregationTemporality.bind(exporter);
11
+ }
12
+ }
13
+
14
+ public export(metrics: ResourceMetrics, resultCallback: (result: ExportResult) => void): void {
15
+ const filteredMetrics: ResourceMetrics = {
16
+ resource: metrics.resource,
17
+ scopeMetrics: metrics.scopeMetrics
18
+ .map(({ scope, metrics }) => ({ scope, metrics: this.filterMetrics(metrics) }))
19
+ .filter(({ metrics }) => metrics.length > 0),
20
+ };
21
+
22
+ this.exporter.export(filteredMetrics, resultCallback);
23
+ }
24
+
25
+ private filterMetrics(metrics: MetricData[]): MetricData[] {
26
+ return metrics.filter(
27
+ metric => !this.excludeMetricPrefixes.some(prefix => metric.descriptor.name.startsWith(prefix)),
28
+ );
29
+ }
30
+
31
+ public forceFlush(): Promise<void> {
32
+ return this.exporter.forceFlush();
33
+ }
34
+
35
+ public shutdown(): Promise<void> {
36
+ return this.exporter.shutdown();
37
+ }
38
+ }
@@ -0,0 +1,31 @@
1
+ import { logs as otelLogs } from '@opentelemetry/api-logs';
2
+ import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
3
+ import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';
4
+ import { type IResource } from '@opentelemetry/resources';
5
+ import { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs';
6
+
7
+ export function registerOtelLoggerProvider(resource: IResource, otelLogsUrl?: URL) {
8
+ const loggerProvider = new LoggerProvider({ resource });
9
+ if (!otelLogsUrl) {
10
+ // If no URL provided, return it disconnected.
11
+ return loggerProvider;
12
+ }
13
+ const logExporter = new OTLPLogExporter({
14
+ compression: CompressionAlgorithm.GZIP,
15
+ url: otelLogsUrl.href,
16
+ });
17
+ // Add a processor to the logger provider
18
+ loggerProvider.addLogRecordProcessor(
19
+ new BatchLogRecordProcessor(logExporter, {
20
+ /** The maximum batch size of every export. It must be smaller or equal to
21
+ * maxQueueSize. */
22
+ maxExportBatchSize: 1024,
23
+ /** The maximum queue size. After the size is reached log records are dropped. */
24
+ maxQueueSize: 4096,
25
+ }),
26
+ );
27
+
28
+ otelLogs.setGlobalLoggerProvider(loggerProvider);
29
+
30
+ return loggerProvider;
31
+ }
@@ -0,0 +1,50 @@
1
+ import { ROOT_CONTEXT, type Span, SpanKind, SpanStatusCode, propagation } from '@opentelemetry/api';
2
+ import type Koa from 'koa';
3
+
4
+ import { getTelemetryClient } from './start.js';
5
+ import {
6
+ ATTR_JSONRPC_ERROR_CODE,
7
+ ATTR_JSONRPC_ERROR_MSG,
8
+ ATTR_JSONRPC_METHOD,
9
+ ATTR_JSONRPC_REQUEST_ID,
10
+ } from './vendor/attributes.js';
11
+
12
+ export function getOtelJsonRpcPropagationMiddleware(
13
+ scope = 'JsonRpcServer',
14
+ ): (ctx: Koa.Context, next: () => Promise<void>) => Promise<void> {
15
+ return function otelJsonRpcPropagation(ctx: Koa.Context, next: () => Promise<void>) {
16
+ const tracer = getTelemetryClient().getTracer(scope);
17
+ const context = propagation.extract(ROOT_CONTEXT, ctx.request.headers);
18
+ const method = (ctx.request.body as any)?.method;
19
+ return tracer.startActiveSpan(
20
+ `JsonRpcServer.${method ?? 'unknown'}`,
21
+ { kind: SpanKind.SERVER },
22
+ context,
23
+ async (span: Span): Promise<void> => {
24
+ if (ctx.id) {
25
+ span.setAttribute(ATTR_JSONRPC_REQUEST_ID, ctx.id);
26
+ }
27
+ if (method) {
28
+ span.setAttribute(ATTR_JSONRPC_METHOD, method);
29
+ }
30
+
31
+ try {
32
+ await next();
33
+ const err = (ctx.body as any).error?.message;
34
+ const code = (ctx.body as any).error?.code;
35
+ if (err) {
36
+ span.setStatus({ code: SpanStatusCode.ERROR, message: err });
37
+ span.setAttribute(ATTR_JSONRPC_ERROR_CODE, code);
38
+ span.setAttribute(ATTR_JSONRPC_ERROR_MSG, err);
39
+ } else {
40
+ span.setStatus({ code: SpanStatusCode.OK });
41
+ }
42
+ } catch (err) {
43
+ span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
44
+ } finally {
45
+ span.end();
46
+ }
47
+ },
48
+ );
49
+ };
50
+ }
@@ -0,0 +1,26 @@
1
+ import { GcpDetectorSync } from '@google-cloud/opentelemetry-resource-util';
2
+ import {
3
+ type IResource,
4
+ detectResourcesSync,
5
+ envDetectorSync,
6
+ osDetectorSync,
7
+ processDetectorSync,
8
+ serviceInstanceIdDetectorSync,
9
+ } from '@opentelemetry/resources';
10
+
11
+ import { aztecDetector } from './aztec_resource_detector.js';
12
+
13
+ export function getOtelResource(): IResource {
14
+ const resource = detectResourcesSync({
15
+ detectors: [
16
+ osDetectorSync,
17
+ envDetectorSync,
18
+ processDetectorSync,
19
+ serviceInstanceIdDetectorSync,
20
+ aztecDetector,
21
+ new GcpDetectorSync(),
22
+ ],
23
+ });
24
+
25
+ return resource;
26
+ }