@aztec/telemetry-client 0.0.0-test.0
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.
- package/dest/attributes.d.ts +99 -0
- package/dest/attributes.d.ts.map +1 -0
- package/dest/attributes.js +67 -0
- package/dest/bench.d.ts +29 -0
- package/dest/bench.d.ts.map +1 -0
- package/dest/bench.js +98 -0
- package/dest/config.d.ts +12 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/config.js +39 -0
- package/dest/event_loop_monitor.d.ts +18 -0
- package/dest/event_loop_monitor.d.ts.map +1 -0
- package/dest/event_loop_monitor.js +93 -0
- package/dest/index.d.ts +10 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +9 -0
- package/dest/lmdb_metrics.d.ts +16 -0
- package/dest/lmdb_metrics.d.ts.map +1 -0
- package/dest/lmdb_metrics.js +42 -0
- package/dest/metrics.d.ts +129 -0
- package/dest/metrics.d.ts.map +1 -0
- package/dest/metrics.js +126 -0
- package/dest/noop.d.ts +14 -0
- package/dest/noop.d.ts.map +1 -0
- package/dest/noop.js +71 -0
- package/dest/otel.d.ts +32 -0
- package/dest/otel.d.ts.map +1 -0
- package/dest/otel.js +319 -0
- package/dest/otel_filter_metric_exporter.d.ts +12 -0
- package/dest/otel_filter_metric_exporter.d.ts.map +1 -0
- package/dest/otel_filter_metric_exporter.js +33 -0
- package/dest/otel_logger_provider.d.ts +4 -0
- package/dest/otel_logger_provider.d.ts.map +1 -0
- package/dest/otel_logger_provider.js +25 -0
- package/dest/otel_propagation.d.ts +3 -0
- package/dest/otel_propagation.d.ts.map +1 -0
- package/dest/otel_propagation.js +44 -0
- package/dest/otel_resource.d.ts +3 -0
- package/dest/otel_resource.d.ts.map +1 -0
- package/dest/otel_resource.js +12 -0
- package/dest/prom_otel_adapter.d.ts +127 -0
- package/dest/prom_otel_adapter.d.ts.map +1 -0
- package/dest/prom_otel_adapter.js +183 -0
- package/dest/start.d.ts +6 -0
- package/dest/start.d.ts.map +1 -0
- package/dest/start.js +24 -0
- package/dest/telemetry.d.ts +118 -0
- package/dest/telemetry.d.ts.map +1 -0
- package/dest/telemetry.js +116 -0
- package/dest/vendor/attributes.d.ts +5 -0
- package/dest/vendor/attributes.d.ts.map +1 -0
- package/dest/vendor/attributes.js +5 -0
- package/dest/vendor/otel-pino-stream.d.ts +41 -0
- package/dest/vendor/otel-pino-stream.d.ts.map +1 -0
- package/dest/vendor/otel-pino-stream.js +229 -0
- package/dest/with_tracer.d.ts +33 -0
- package/dest/with_tracer.d.ts.map +1 -0
- package/dest/with_tracer.js +32 -0
- package/dest/wrappers/fetch.d.ts +16 -0
- package/dest/wrappers/fetch.d.ts.map +1 -0
- package/dest/wrappers/fetch.js +39 -0
- package/dest/wrappers/index.d.ts +4 -0
- package/dest/wrappers/index.d.ts.map +1 -0
- package/dest/wrappers/index.js +3 -0
- package/dest/wrappers/json_rpc_server.d.ts +4 -0
- package/dest/wrappers/json_rpc_server.d.ts.map +1 -0
- package/dest/wrappers/json_rpc_server.js +11 -0
- package/dest/wrappers/l2_block_stream.d.ts +15 -0
- package/dest/wrappers/l2_block_stream.d.ts.map +1 -0
- package/dest/wrappers/l2_block_stream.js +26 -0
- package/package.json +89 -0
- package/src/attributes.ts +115 -0
- package/src/bench.ts +147 -0
- package/src/config.ts +56 -0
- package/src/event_loop_monitor.ts +119 -0
- package/src/index.ts +9 -0
- package/src/lmdb_metrics.ts +45 -0
- package/src/metrics.ts +153 -0
- package/src/noop.ts +91 -0
- package/src/otel.ts +286 -0
- package/src/otel_filter_metric_exporter.ts +38 -0
- package/src/otel_logger_provider.ts +31 -0
- package/src/otel_propagation.ts +50 -0
- package/src/otel_resource.ts +16 -0
- package/src/prom_otel_adapter.ts +326 -0
- package/src/start.ts +33 -0
- package/src/telemetry.ts +267 -0
- package/src/vendor/attributes.ts +5 -0
- package/src/vendor/otel-pino-stream.ts +282 -0
- package/src/with_tracer.ts +35 -0
- package/src/wrappers/fetch.ts +52 -0
- package/src/wrappers/index.ts +3 -0
- package/src/wrappers/json_rpc_server.ts +15 -0
- package/src/wrappers/l2_block_stream.ts +37 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Adapted from open-telemetry/opentelemetry-js-contrib
|
|
3
|
+
* All changes are prefixed with [aztec] to make them easy to identify
|
|
4
|
+
*
|
|
5
|
+
* Copyright The OpenTelemetry Authors
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { type Logger, SeverityNumber, logs } from '@opentelemetry/api-logs';
|
|
20
|
+
import { millisToHrTime } from '@opentelemetry/core';
|
|
21
|
+
import { Writable } from 'stream';
|
|
22
|
+
|
|
23
|
+
import { registerOtelLoggerProvider } from '../otel_logger_provider.js';
|
|
24
|
+
import { getOtelResource } from '../otel_resource.js';
|
|
25
|
+
|
|
26
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
27
|
+
/* eslint-disable camelcase */
|
|
28
|
+
|
|
29
|
+
// This block is a copy (modulo code style and TypeScript types) of the Pino
|
|
30
|
+
// code that defines log level value and names. This file is part of
|
|
31
|
+
// *instrumenting* Pino, so we want to avoid a dependency on the library.
|
|
32
|
+
const DEFAULT_LEVELS = {
|
|
33
|
+
trace: 10,
|
|
34
|
+
debug: 20,
|
|
35
|
+
info: 30,
|
|
36
|
+
warn: 40,
|
|
37
|
+
error: 50,
|
|
38
|
+
fatal: 60,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const OTEL_SEV_NUM_FROM_PINO_LEVEL: { [level: number]: SeverityNumber } = {
|
|
42
|
+
[DEFAULT_LEVELS.trace]: SeverityNumber.TRACE,
|
|
43
|
+
[DEFAULT_LEVELS.debug]: SeverityNumber.DEBUG,
|
|
44
|
+
[DEFAULT_LEVELS.info]: SeverityNumber.INFO,
|
|
45
|
+
[DEFAULT_LEVELS.warn]: SeverityNumber.WARN,
|
|
46
|
+
[DEFAULT_LEVELS.error]: SeverityNumber.ERROR,
|
|
47
|
+
[DEFAULT_LEVELS.fatal]: SeverityNumber.FATAL,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const EXTRA_SEV_NUMS = [
|
|
51
|
+
SeverityNumber.TRACE2,
|
|
52
|
+
SeverityNumber.TRACE3,
|
|
53
|
+
SeverityNumber.TRACE4,
|
|
54
|
+
SeverityNumber.DEBUG2,
|
|
55
|
+
SeverityNumber.DEBUG3,
|
|
56
|
+
SeverityNumber.DEBUG4,
|
|
57
|
+
SeverityNumber.INFO2,
|
|
58
|
+
SeverityNumber.INFO3,
|
|
59
|
+
SeverityNumber.INFO4,
|
|
60
|
+
SeverityNumber.WARN2,
|
|
61
|
+
SeverityNumber.WARN3,
|
|
62
|
+
SeverityNumber.WARN4,
|
|
63
|
+
SeverityNumber.ERROR2,
|
|
64
|
+
SeverityNumber.ERROR3,
|
|
65
|
+
SeverityNumber.ERROR4,
|
|
66
|
+
SeverityNumber.FATAL2,
|
|
67
|
+
SeverityNumber.FATAL3,
|
|
68
|
+
SeverityNumber.FATAL4,
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
function severityNumberFromPinoLevel(lvl: number) {
|
|
72
|
+
// Fast common case: one of the known levels
|
|
73
|
+
const sev = OTEL_SEV_NUM_FROM_PINO_LEVEL[lvl];
|
|
74
|
+
if (sev !== undefined) {
|
|
75
|
+
return sev;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Otherwise, scale the Pino level range -- 10 (trace) to 70 (fatal+10)
|
|
79
|
+
// -- onto the extra OTel severity numbers (TRACE2, TRACE3, ..., FATAL4).
|
|
80
|
+
// Values below trace (10) map to SeverityNumber.TRACE2, which may be
|
|
81
|
+
// considered a bit weird, but it means the unnumbered levels are always
|
|
82
|
+
// just for exactly matching values.
|
|
83
|
+
const relativeLevelWeight = (lvl - 10) / (70 - 10);
|
|
84
|
+
const otelSevIdx = Math.floor(relativeLevelWeight * EXTRA_SEV_NUMS.length);
|
|
85
|
+
const cappedOTelIdx = Math.min(EXTRA_SEV_NUMS.length - 1, Math.max(0, otelSevIdx));
|
|
86
|
+
const otelSevValue = EXTRA_SEV_NUMS[cappedOTelIdx];
|
|
87
|
+
return otelSevValue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// [aztec] Custom function to map Aztec logging levels to OpenTelemetry severity numbers
|
|
91
|
+
function severityNumberFromAztecPinoLevel(lvl: number) {
|
|
92
|
+
return (
|
|
93
|
+
OTEL_SEV_NUM_FROM_PINO_LEVEL[lvl] ??
|
|
94
|
+
/* verbose */ (lvl === 25 ? SeverityNumber.DEBUG3 : undefined) ??
|
|
95
|
+
severityNumberFromPinoLevel(lvl)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Return a function that knows how to convert the "time" field value on a
|
|
101
|
+
* Pino log record to an OTel LogRecord timestamp value.
|
|
102
|
+
*
|
|
103
|
+
* How to convert the serialized "time" on a Pino log record
|
|
104
|
+
* depends on the Logger's `Symbol(pino.time)` prop, configurable
|
|
105
|
+
* via https://getpino.io/#/docs/api?id=timestamp-boolean-function
|
|
106
|
+
*
|
|
107
|
+
* For example:
|
|
108
|
+
* const logger = pino({timestamp: pino.stdTimeFunctions.isoTime})
|
|
109
|
+
* results in log record entries of the form:
|
|
110
|
+
* ,"time":"2024-05-17T22:03:25.969Z"
|
|
111
|
+
* `otelTimestampFromTime` will be given the value of the "time" field:
|
|
112
|
+
* "2024-05-17T22:03:25.969Z"
|
|
113
|
+
* which should be parsed to a number of milliseconds since the epoch.
|
|
114
|
+
*/
|
|
115
|
+
export function getTimeConverter(pinoLogger: any, pinoMod: any) {
|
|
116
|
+
const stdTimeFns = pinoMod.stdTimeFunctions;
|
|
117
|
+
const loggerTimeFn = pinoLogger[pinoMod.symbols.timeSym];
|
|
118
|
+
if (loggerTimeFn === stdTimeFns.epochTime) {
|
|
119
|
+
return (time: number) => time;
|
|
120
|
+
} else if (loggerTimeFn === stdTimeFns.unixTime) {
|
|
121
|
+
return (time: number) => time * 1e3;
|
|
122
|
+
} else if (loggerTimeFn === stdTimeFns.isoTime) {
|
|
123
|
+
return (time: string) => new Date(time).getTime();
|
|
124
|
+
} else if (loggerTimeFn === stdTimeFns.nullTime) {
|
|
125
|
+
return () => Date.now();
|
|
126
|
+
} else {
|
|
127
|
+
// The logger has a custom time function. Don't guess.
|
|
128
|
+
return () => NaN;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface OTelPinoStreamOptions {
|
|
133
|
+
messageKey?: string;
|
|
134
|
+
levels: any; // Pino.LevelMapping
|
|
135
|
+
otelTimestampFromTime?: (time: any) => number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* A Pino stream for sending records to the OpenTelemetry Logs API.
|
|
140
|
+
*
|
|
141
|
+
* - This stream emits an 'unknown' event on an unprocessable pino record.
|
|
142
|
+
* The event arguments are: `logLine: string`, `err: string | Error`.
|
|
143
|
+
*/
|
|
144
|
+
export class OTelPinoStream extends Writable {
|
|
145
|
+
private _otelLogger: Logger;
|
|
146
|
+
private _messageKey: string;
|
|
147
|
+
private _levels;
|
|
148
|
+
private _otelTimestampFromTime;
|
|
149
|
+
|
|
150
|
+
constructor(options: OTelPinoStreamOptions) {
|
|
151
|
+
super();
|
|
152
|
+
|
|
153
|
+
// Note: A PINO_CONFIG event was added to pino (2024-04-04) to send config
|
|
154
|
+
// to transports. Eventually OTelPinoStream might be able to use this
|
|
155
|
+
// for auto-configuration in newer pino versions. The event currently does
|
|
156
|
+
// not include the `timeSym` value that is needed here, however.
|
|
157
|
+
this._messageKey = options.messageKey ?? 'msg';
|
|
158
|
+
this._levels = options.levels;
|
|
159
|
+
|
|
160
|
+
// [aztec] The following will break if we set up a custom time function in our logger
|
|
161
|
+
this._otelTimestampFromTime = options.otelTimestampFromTime ?? ((time: number) => time);
|
|
162
|
+
|
|
163
|
+
// Cannot use `instrumentation.logger` until have delegating LoggerProvider:
|
|
164
|
+
// https://github.com/open-telemetry/opentelemetry-js/issues/4399
|
|
165
|
+
// [aztec] Use the name of this package
|
|
166
|
+
this._otelLogger = logs.getLogger('@aztec/telemetry-client/otel-pino-stream', '0.1.0');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
override _write(s: string, _encoding: string, callback: Function) {
|
|
170
|
+
try {
|
|
171
|
+
/* istanbul ignore if */
|
|
172
|
+
if (!s) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Parse, and handle edge cases similar to how `pino-abtract-transport` does:
|
|
177
|
+
// https://github.com/pinojs/pino-abstract-transport/blob/v1.2.0/index.js#L28-L45
|
|
178
|
+
// - Emitting an 'unknown' event on parse error mimicks pino-abstract-transport.
|
|
179
|
+
let recObj;
|
|
180
|
+
try {
|
|
181
|
+
recObj = JSON.parse(s);
|
|
182
|
+
} catch (parseErr) {
|
|
183
|
+
// Invalid JSON suggests a bug in Pino, or a logger configuration bug
|
|
184
|
+
// (a bogus `options.timestamp` or serializer).
|
|
185
|
+
this.emit('unknown', s.toString(), parseErr);
|
|
186
|
+
callback();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
/* istanbul ignore if */
|
|
190
|
+
if (recObj === null) {
|
|
191
|
+
this.emit('unknown', s.toString(), 'Null value ignored');
|
|
192
|
+
callback();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
/* istanbul ignore if */
|
|
196
|
+
if (typeof recObj !== 'object') {
|
|
197
|
+
recObj = {
|
|
198
|
+
data: recObj,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const {
|
|
203
|
+
time,
|
|
204
|
+
[this._messageKey]: body,
|
|
205
|
+
level, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
206
|
+
|
|
207
|
+
// The typical Pino `hostname` and `pid` fields are removed because they
|
|
208
|
+
// are redundant with the OpenTelemetry `host.name` and `process.pid`
|
|
209
|
+
// Resource attributes, respectively. This code cannot change the
|
|
210
|
+
// LoggerProvider's `resource`, so getting the OpenTelemetry equivalents
|
|
211
|
+
// depends on the user using the OpenTelemetry HostDetector and
|
|
212
|
+
// ProcessDetector.
|
|
213
|
+
// https://getpino.io/#/docs/api?id=opt-base
|
|
214
|
+
hostname, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
215
|
+
pid, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
216
|
+
|
|
217
|
+
// The `trace_id` et al fields that may have been added by the
|
|
218
|
+
// "log correlation" feature are stripped, because they are redundant.
|
|
219
|
+
// trace_id, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
220
|
+
// span_id, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
221
|
+
// trace_flags, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
222
|
+
|
|
223
|
+
// [aztec] They are not redundant, we depend on them for correlation.
|
|
224
|
+
// The instrumentation package seems to be adding these fields via a custom hook.
|
|
225
|
+
// We push them from the logger module in foundation, so we don't want to clear them here.
|
|
226
|
+
|
|
227
|
+
...attributes
|
|
228
|
+
} = recObj;
|
|
229
|
+
|
|
230
|
+
let timestamp = this._otelTimestampFromTime(time);
|
|
231
|
+
if (isNaN(timestamp)) {
|
|
232
|
+
attributes['time'] = time; // save the unexpected "time" field to attributes
|
|
233
|
+
timestamp = Date.now();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// This avoids a possible subtle bug when a Pino logger uses
|
|
237
|
+
// `time: pino.stdTimeFunctions.unixTime` and logs in the first half-second
|
|
238
|
+
// since process start. The rounding involved results in:
|
|
239
|
+
// timestamp < performance.timeOrigin
|
|
240
|
+
// If that is passed to Logger.emit() it will be misinterpreted by
|
|
241
|
+
// `timeInputToHrTime` as a `performance.now()` value.
|
|
242
|
+
const timestampHrTime = millisToHrTime(timestamp);
|
|
243
|
+
|
|
244
|
+
// Prefer using `stream.lastLevel`, because `recObj.level` can be customized
|
|
245
|
+
// to anything via `formatters.level`
|
|
246
|
+
// (https://getpino.io/#/docs/api?id=formatters-object).
|
|
247
|
+
// const lastLevel = (this as any).lastLevel;
|
|
248
|
+
|
|
249
|
+
// [aztec] We do not prefer stream.lastLevel since it's undefined here, as we are running
|
|
250
|
+
// on a worker thread, so we use recObj.level because we know that we won't customize it.
|
|
251
|
+
const lastLevel = recObj.level;
|
|
252
|
+
|
|
253
|
+
const otelRec = {
|
|
254
|
+
timestamp: timestampHrTime,
|
|
255
|
+
observedTimestamp: timestampHrTime,
|
|
256
|
+
severityNumber: severityNumberFromAztecPinoLevel(lastLevel),
|
|
257
|
+
severityText: this._levels.labels[lastLevel],
|
|
258
|
+
body,
|
|
259
|
+
attributes,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
this._otelLogger.emit(otelRec);
|
|
263
|
+
} catch (err) {
|
|
264
|
+
// [aztec] Log errors to stderr
|
|
265
|
+
// eslint-disable-next-line no-console
|
|
266
|
+
console.error(`Error in OTelPinoStream: ${err}`);
|
|
267
|
+
}
|
|
268
|
+
callback();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// [aztec] Default export that loads the resource information and creates a new otel pino stream.
|
|
273
|
+
// Invoked by pino when creating a transport in a worker thread out of this stream.
|
|
274
|
+
// Note that the original open-telemetry/opentelemetry-js-contrib was set up to run on the main
|
|
275
|
+
// nodejs loop, as opposed to in a worker as pino recommends.
|
|
276
|
+
export default function (options: OTelPinoStreamOptions): OTelPinoStream {
|
|
277
|
+
const url = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;
|
|
278
|
+
const resource = getOtelResource();
|
|
279
|
+
// We re-register here because this runs on a worker thread
|
|
280
|
+
registerOtelLoggerProvider(resource, url ? new URL(url) : undefined);
|
|
281
|
+
return new OTelPinoStream(options);
|
|
282
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TelemetryClient, Tracer } from './telemetry.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A minimal class that enables recording metrics with a telemetry client.
|
|
5
|
+
* This base class enables tracing
|
|
6
|
+
*
|
|
7
|
+
* In other words:
|
|
8
|
+
* Enables the ability to call `@trackSpan` on methods.
|
|
9
|
+
*
|
|
10
|
+
* Example:
|
|
11
|
+
*
|
|
12
|
+
* ```
|
|
13
|
+
* import {Attributes, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
|
|
14
|
+
*
|
|
15
|
+
* class MyClass extends WithTracer {
|
|
16
|
+
* // Constructor is required to be passed the TelemetryClient implementation.
|
|
17
|
+
* constructor(client: TelemetryClient) {
|
|
18
|
+
* super(client, 'MyClass');
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* @trackSpan('MyClass.myMethod', (arg: string) => ({
|
|
22
|
+
* [Attributes.ARG]: arg,
|
|
23
|
+
* }))
|
|
24
|
+
* public myMethod(arg: string) {
|
|
25
|
+
* // ...
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export class WithTracer {
|
|
31
|
+
public readonly tracer: Tracer;
|
|
32
|
+
constructor(client: TelemetryClient, name: string) {
|
|
33
|
+
this.tracer = client.getTracer(name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { defaultFetch } from '@aztec/foundation/json-rpc/client';
|
|
2
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
3
|
+
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
4
|
+
|
|
5
|
+
import { SpanKind, SpanStatusCode, context, propagation } from '@opentelemetry/api';
|
|
6
|
+
|
|
7
|
+
import { getTelemetryClient } from '../start.js';
|
|
8
|
+
import { ATTR_JSONRPC_METHOD, ATTR_JSONRPC_REQUEST_ID } from '../vendor/attributes.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Makes a fetch function that retries based on the given attempts and propagates trace information.
|
|
12
|
+
* @param retries - Sequence of intervals (in seconds) to retry.
|
|
13
|
+
* @param noRetry - Whether to stop retries on server errors.
|
|
14
|
+
* @param log - Optional logger for logging attempts.
|
|
15
|
+
* @returns A fetch function.
|
|
16
|
+
*/
|
|
17
|
+
export function makeTracedFetch(retries: number[], defaultNoRetry: boolean, fetch = defaultFetch, log?: Logger) {
|
|
18
|
+
return (
|
|
19
|
+
host: string,
|
|
20
|
+
rpcMethod: string,
|
|
21
|
+
body: any,
|
|
22
|
+
useApiEndpoints: boolean,
|
|
23
|
+
extraHeaders: Record<string, string> = {},
|
|
24
|
+
noRetry?: boolean,
|
|
25
|
+
) => {
|
|
26
|
+
const telemetry = getTelemetryClient();
|
|
27
|
+
return telemetry
|
|
28
|
+
.getTracer('fetch')
|
|
29
|
+
.startActiveSpan(`JsonRpcClient.${rpcMethod}`, { kind: SpanKind.CLIENT }, async span => {
|
|
30
|
+
try {
|
|
31
|
+
if (body && typeof body.id === 'number') {
|
|
32
|
+
span.setAttribute(ATTR_JSONRPC_REQUEST_ID, body.id);
|
|
33
|
+
}
|
|
34
|
+
span.setAttribute(ATTR_JSONRPC_METHOD, rpcMethod);
|
|
35
|
+
const headers = { ...extraHeaders };
|
|
36
|
+
propagation.inject(context.active(), headers);
|
|
37
|
+
return await retry(
|
|
38
|
+
() => fetch(host, rpcMethod, body, useApiEndpoints, headers, noRetry ?? defaultNoRetry),
|
|
39
|
+
`JsonRpcClient request ${rpcMethod} to ${host}`,
|
|
40
|
+
makeBackoff(retries),
|
|
41
|
+
log,
|
|
42
|
+
false,
|
|
43
|
+
);
|
|
44
|
+
} catch (err: any) {
|
|
45
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: err?.message ?? String(err) });
|
|
46
|
+
throw err;
|
|
47
|
+
} finally {
|
|
48
|
+
span.end();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type SafeJsonRpcServerOptions, createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
|
|
2
|
+
import type { ApiSchemaFor } from '@aztec/stdlib/schemas';
|
|
3
|
+
|
|
4
|
+
import { getOtelJsonRpcPropagationMiddleware } from '../otel_propagation.js';
|
|
5
|
+
|
|
6
|
+
export function createTracedJsonRpcServer<T extends object = any>(
|
|
7
|
+
handler: T,
|
|
8
|
+
schema: ApiSchemaFor<T>,
|
|
9
|
+
options: Partial<SafeJsonRpcServerOptions> = {},
|
|
10
|
+
) {
|
|
11
|
+
return createSafeJsonRpcServer(handler, schema, {
|
|
12
|
+
...options,
|
|
13
|
+
middlewares: [...(options.middlewares ?? []), getOtelJsonRpcPropagationMiddleware()],
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import {
|
|
3
|
+
type L2BlockSource,
|
|
4
|
+
L2BlockStream,
|
|
5
|
+
type L2BlockStreamEventHandler,
|
|
6
|
+
type L2BlockStreamLocalDataProvider,
|
|
7
|
+
} from '@aztec/stdlib/block';
|
|
8
|
+
import { type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
9
|
+
|
|
10
|
+
/** Extends an L2BlockStream with a tracer to create a new trace per iteration. */
|
|
11
|
+
export class TraceableL2BlockStream extends L2BlockStream implements Traceable {
|
|
12
|
+
constructor(
|
|
13
|
+
l2BlockSource: Pick<L2BlockSource, 'getBlocks' | 'getBlockHeader' | 'getL2Tips'>,
|
|
14
|
+
localData: L2BlockStreamLocalDataProvider,
|
|
15
|
+
handler: L2BlockStreamEventHandler,
|
|
16
|
+
public readonly tracer: Tracer,
|
|
17
|
+
private readonly name: string = 'L2BlockStream',
|
|
18
|
+
log = createLogger('types:block_stream'),
|
|
19
|
+
opts: {
|
|
20
|
+
proven?: boolean;
|
|
21
|
+
pollIntervalMS?: number;
|
|
22
|
+
batchSize?: number;
|
|
23
|
+
startingBlock?: number;
|
|
24
|
+
} = {},
|
|
25
|
+
) {
|
|
26
|
+
super(l2BlockSource, localData, handler, log, opts);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// We need to use a non-arrow function to be able to access `this`
|
|
30
|
+
// See https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function
|
|
31
|
+
@trackSpan(function () {
|
|
32
|
+
return `${this!.name}.work`;
|
|
33
|
+
})
|
|
34
|
+
override work() {
|
|
35
|
+
return super.work();
|
|
36
|
+
}
|
|
37
|
+
}
|