@aztec/telemetry-client 0.0.0-test.1 → 0.0.1-commit.1142ef1

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 (90) hide show
  1. package/dest/attributes.d.ts +33 -9
  2. package/dest/attributes.d.ts.map +1 -1
  3. package/dest/attributes.js +22 -8
  4. package/dest/bench.d.ts +6 -1
  5. package/dest/bench.d.ts.map +1 -1
  6. package/dest/bench.js +31 -14
  7. package/dest/config.d.ts +7 -2
  8. package/dest/config.d.ts.map +1 -1
  9. package/dest/config.js +29 -1
  10. package/dest/index.d.ts +2 -1
  11. package/dest/index.d.ts.map +1 -1
  12. package/dest/index.js +1 -0
  13. package/dest/l1_metrics.d.ts +17 -0
  14. package/dest/l1_metrics.d.ts.map +1 -0
  15. package/dest/l1_metrics.js +54 -0
  16. package/dest/lmdb_metrics.d.ts +5 -3
  17. package/dest/lmdb_metrics.d.ts.map +1 -1
  18. package/dest/lmdb_metrics.js +7 -15
  19. package/dest/metric-utils.d.ts +5 -0
  20. package/dest/metric-utils.d.ts.map +1 -0
  21. package/dest/metric-utils.js +7 -0
  22. package/dest/metrics.d.ts +247 -122
  23. package/dest/metrics.d.ts.map +1 -1
  24. package/dest/metrics.js +1239 -120
  25. package/dest/nodejs_metrics_monitor.d.ts +19 -0
  26. package/dest/nodejs_metrics_monitor.d.ts.map +1 -0
  27. package/dest/nodejs_metrics_monitor.js +109 -0
  28. package/dest/noop.d.ts +9 -4
  29. package/dest/noop.d.ts.map +1 -1
  30. package/dest/noop.js +34 -1
  31. package/dest/otel.d.ts +13 -7
  32. package/dest/otel.d.ts.map +1 -1
  33. package/dest/otel.js +142 -21
  34. package/dest/otel_filter_metric_exporter.d.ts +12 -4
  35. package/dest/otel_filter_metric_exporter.d.ts.map +1 -1
  36. package/dest/otel_filter_metric_exporter.js +38 -4
  37. package/dest/otel_logger_provider.d.ts +1 -1
  38. package/dest/otel_propagation.d.ts +1 -1
  39. package/dest/otel_resource.d.ts +1 -1
  40. package/dest/otel_resource.d.ts.map +1 -1
  41. package/dest/otel_resource.js +31 -2
  42. package/dest/prom_otel_adapter.d.ts +58 -9
  43. package/dest/prom_otel_adapter.d.ts.map +1 -1
  44. package/dest/prom_otel_adapter.js +153 -45
  45. package/dest/start.d.ts +2 -2
  46. package/dest/start.d.ts.map +1 -1
  47. package/dest/start.js +7 -6
  48. package/dest/telemetry.d.ts +62 -34
  49. package/dest/telemetry.d.ts.map +1 -1
  50. package/dest/telemetry.js +47 -24
  51. package/dest/vendor/attributes.d.ts +1 -1
  52. package/dest/vendor/otel-pino-stream.d.ts +1 -2
  53. package/dest/vendor/otel-pino-stream.d.ts.map +1 -1
  54. package/dest/vendor/otel-pino-stream.js +2 -2
  55. package/dest/with_tracer.d.ts +1 -1
  56. package/dest/with_tracer.d.ts.map +1 -1
  57. package/dest/wrappers/fetch.d.ts +2 -2
  58. package/dest/wrappers/fetch.d.ts.map +1 -1
  59. package/dest/wrappers/fetch.js +7 -5
  60. package/dest/wrappers/index.d.ts +1 -1
  61. package/dest/wrappers/json_rpc_server.d.ts +2 -2
  62. package/dest/wrappers/json_rpc_server.d.ts.map +1 -1
  63. package/dest/wrappers/l2_block_stream.d.ts +4 -3
  64. package/dest/wrappers/l2_block_stream.d.ts.map +1 -1
  65. package/dest/wrappers/l2_block_stream.js +385 -11
  66. package/package.json +21 -15
  67. package/src/attributes.ts +42 -11
  68. package/src/bench.ts +37 -15
  69. package/src/config.ts +54 -2
  70. package/src/index.ts +1 -0
  71. package/src/l1_metrics.ts +65 -0
  72. package/src/lmdb_metrics.ts +24 -24
  73. package/src/metric-utils.ts +12 -0
  74. package/src/metrics.ts +1313 -145
  75. package/src/nodejs_metrics_monitor.ts +132 -0
  76. package/src/noop.ts +65 -4
  77. package/src/otel.ts +155 -26
  78. package/src/otel_filter_metric_exporter.ts +47 -5
  79. package/src/otel_resource.ts +40 -2
  80. package/src/prom_otel_adapter.ts +191 -65
  81. package/src/start.ts +7 -6
  82. package/src/telemetry.ts +151 -76
  83. package/src/vendor/otel-pino-stream.ts +1 -4
  84. package/src/wrappers/fetch.ts +24 -31
  85. package/src/wrappers/json_rpc_server.ts +1 -1
  86. package/src/wrappers/l2_block_stream.ts +6 -2
  87. package/dest/event_loop_monitor.d.ts +0 -18
  88. package/dest/event_loop_monitor.d.ts.map +0 -1
  89. package/dest/event_loop_monitor.js +0 -93
  90. package/src/event_loop_monitor.ts +0 -119
@@ -1,8 +1,10 @@
1
1
  import { type Logger, createLogger } from '@aztec/foundation/log';
2
+ import { Timer } from '@aztec/foundation/timer';
2
3
 
3
4
  import { Registry } from 'prom-client';
4
5
 
5
- import type { Meter, MetricsType, ObservableGauge, TelemetryClient } from './telemetry.js';
6
+ import type { MetricDefinition } from './metrics.js';
7
+ import type { Histogram, Meter, ObservableGauge, TelemetryClient } from './telemetry.js';
6
8
 
7
9
  /**
8
10
  * Types matching the gossipsub and libp2p services
@@ -93,9 +95,8 @@ export class OtelGauge<Labels extends LabelsGeneric = NoLabels> implements IGaug
93
95
  help: string,
94
96
  private labelNames: Array<keyof Labels> = [],
95
97
  ) {
96
- this.gauge = meter.createObservableGauge(name as MetricsType, {
97
- description: help,
98
- });
98
+ const metricDef: MetricDefinition = { name, description: help };
99
+ this.gauge = meter.createObservableGauge(metricDef);
99
100
 
100
101
  // Only observe in the callback when collect() is called
101
102
  this.gauge.addCallback(this.handleObservation.bind(this));
@@ -125,7 +126,7 @@ export class OtelGauge<Labels extends LabelsGeneric = NoLabels> implements IGaug
125
126
  }
126
127
 
127
128
  for (const [labelStr, value] of this.labeledValues.entries()) {
128
- const labels = this.parseLabelsSafely(labelStr);
129
+ const labels = parseLabelsSafely(labelStr, this.logger);
129
130
  if (labels) {
130
131
  result.observe(value, labels);
131
132
  }
@@ -146,7 +147,7 @@ export class OtelGauge<Labels extends LabelsGeneric = NoLabels> implements IGaug
146
147
  }
147
148
 
148
149
  if (labelsOrValue) {
149
- this.validateLabels(labelsOrValue);
150
+ validateLabels(labelsOrValue, this.labelNames, 'Gauge');
150
151
  const labelKey = JSON.stringify(labelsOrValue);
151
152
  const currentValue = this.labeledValues.get(labelKey) ?? 0;
152
153
  this.labeledValues.set(labelKey, currentValue + (value ?? 1));
@@ -169,7 +170,7 @@ export class OtelGauge<Labels extends LabelsGeneric = NoLabels> implements IGaug
169
170
  return;
170
171
  }
171
172
 
172
- this.validateLabels(labelsOrValue);
173
+ validateLabels(labelsOrValue, this.labelNames, 'Gauge');
173
174
  const labelKey = JSON.stringify(labelsOrValue);
174
175
  this.labeledValues.set(labelKey, value!);
175
176
  }
@@ -180,7 +181,7 @@ export class OtelGauge<Labels extends LabelsGeneric = NoLabels> implements IGaug
180
181
  */
181
182
  dec(labels?: Labels): void {
182
183
  if (labels) {
183
- this.validateLabels(labels);
184
+ validateLabels(labels, this.labelNames, 'Gauge');
184
185
  const labelKey = JSON.stringify(labels);
185
186
  const currentValue = this.labeledValues.get(labelKey) ?? 0;
186
187
  this.labeledValues.set(labelKey, currentValue - 1);
@@ -197,84 +198,209 @@ export class OtelGauge<Labels extends LabelsGeneric = NoLabels> implements IGaug
197
198
  this.currentValue = 0;
198
199
  this.labeledValues.clear();
199
200
  }
201
+ }
202
+
203
+ /**
204
+ * Implementation of a Histogram collector
205
+ */
206
+ export class OtelHistogram<Labels extends LabelsGeneric = NoLabels> implements IHistogram<Labels> {
207
+ private histogram: Histogram;
208
+
209
+ constructor(
210
+ private logger: Logger,
211
+ meter: Meter,
212
+ name: string,
213
+ help: string,
214
+ buckets: number[] = [],
215
+ private labelNames: Array<keyof Labels> = [],
216
+ ) {
217
+ const metricDef: MetricDefinition = { name, description: help };
218
+ this.histogram = meter.createHistogram(metricDef, {
219
+ advice: buckets.length ? { explicitBucketBoundaries: buckets } : undefined,
220
+ });
221
+ }
200
222
 
201
223
  /**
202
- * Validates that provided labels match the expected schema
203
- * @param labels - Labels object to validate
204
- * @throws Error if invalid labels are provided
224
+ * Starts a timer and returns a function that when called will record the time elapsed
225
+ * @param labels - Optional labels for the observation
205
226
  */
206
- private validateLabels(labels: Labels): void {
207
- if (this.labelNames.length === 0) {
208
- throw new Error('Gauge was initialized without labels support');
227
+ startTimer(labels?: Labels): () => void {
228
+ if (labels) {
229
+ validateLabels(labels, this.labelNames, 'Histogram');
209
230
  }
210
231
 
211
- for (const key of Object.keys(labels)) {
212
- if (!this.labelNames.includes(key as keyof Labels)) {
213
- throw new Error(`Invalid label key: ${key}`);
232
+ const timer = new Timer();
233
+ return () => {
234
+ // Use timer.s() here to get the duration in seconds since this is only currently used by gossipsub_heartbeat_duration_seconds
235
+ const duration = timer.s();
236
+ if (labels) {
237
+ this.observe(labels, duration);
238
+ } else {
239
+ this.observe(duration);
214
240
  }
215
- }
241
+ };
216
242
  }
217
243
 
218
244
  /**
219
- * Safely parses label string back to object
220
- * @param labelStr - Stringified labels object
221
- * @returns Labels object or null if parsing fails
245
+ * Observes a value
246
+ * @param value - Value to observe
247
+ */
248
+ observe(value: number): void;
249
+ /**
250
+ * Observes a value with labels
251
+ * @param labels - Labels object
252
+ * @param value - Value to observe
222
253
  */
223
- private parseLabelsSafely(labelStr: string): Labels | null {
224
- try {
225
- return JSON.parse(labelStr) as Labels;
226
- } catch {
227
- this.logger.error(`Failed to parse label string: ${labelStr}`);
228
- return null;
254
+ observe(labels: Labels, value: number): void;
255
+ observe(labelsOrValue: Labels | number, value?: number): void {
256
+ if (typeof labelsOrValue === 'number') {
257
+ this.histogram.record(labelsOrValue);
258
+ } else {
259
+ validateLabels(labelsOrValue, this.labelNames, 'Histogram');
260
+ this.histogram.record(value!, labelsOrValue);
229
261
  }
230
262
  }
263
+
264
+ reset(): void {
265
+ // OpenTelemetry histograms cannot be reset, but we implement the interface
266
+ this.logger.silent('OpenTelemetry histograms cannot be fully reset');
267
+ }
231
268
  }
232
269
 
233
270
  /**
234
- * Noop implementation of a Historgram collec
271
+ * Implementation of an AvgMinMax collector
235
272
  */
236
- class NoopOtelHistogram<Labels extends LabelsGeneric = NoLabels> implements IHistogram<Labels> {
273
+ export class OtelAvgMinMax<Labels extends LabelsGeneric = NoLabels> implements IAvgMinMax<Labels> {
274
+ private gauges: {
275
+ avg: ObservableGauge;
276
+ min: ObservableGauge;
277
+ max: ObservableGauge;
278
+ };
279
+
280
+ private currentValues: number[] = [];
281
+ private labeledValues: Map<string, number[]> = new Map();
282
+
237
283
  constructor(
238
284
  private logger: Logger,
239
- _meter: Meter,
240
- _name: string, // MetricsType must be registered in the aztec labels registry
241
- _help: string,
242
- _buckets: number[] = [],
243
- _labelNames: Array<keyof Labels> = [],
244
- ) {}
245
-
246
- // Overload signatures
247
- observe(_value: number): void;
248
- observe(_labels: Labels, _value: number): void;
249
- observe(_valueOrLabels: number | Labels, _value?: number): void {}
250
-
251
- startTimer(_labels?: Labels): (_labels?: Labels) => number {
252
- return () => 0;
285
+ meter: Meter,
286
+ name: string,
287
+ help: string,
288
+ private labelNames: Array<keyof Labels> = [],
289
+ ) {
290
+ // Create three separate gauges for avg, min, and max
291
+ this.gauges = {
292
+ avg: meter.createObservableGauge({ name: `${name}_avg`, description: `${help} (average)` }),
293
+ min: meter.createObservableGauge({ name: `${name}_min`, description: `${help} (minimum)` }),
294
+ max: meter.createObservableGauge({ name: `${name}_max`, description: `${help} (maximum)` }),
295
+ };
296
+
297
+ // Register callbacks for each gauge
298
+ this.gauges.avg.addCallback(this.observeAvg.bind(this));
299
+ this.gauges.min.addCallback(this.observeMin.bind(this));
300
+ this.gauges.max.addCallback(this.observeMax.bind(this));
253
301
  }
254
302
 
303
+ /**
304
+ * Sets the values for calculating avg, min, max
305
+ * @param values - Array of values
306
+ */
307
+ set(values: number[]): void;
308
+ /**
309
+ * Sets the values for calculating avg, min, max with labels
310
+ * @param labels - Labels object
311
+ * @param values - Array of values
312
+ */
313
+ set(labels: Labels, values: number[]): void;
314
+ set(labelsOrValues: number[] | Labels, values?: number[]): void {
315
+ if (Array.isArray(labelsOrValues)) {
316
+ this.currentValues = labelsOrValues;
317
+ return;
318
+ } else {
319
+ validateLabels(labelsOrValues, this.labelNames, 'AvgMinMax');
320
+ const labelKey = JSON.stringify(labelsOrValues);
321
+ this.labeledValues.set(labelKey, values || []);
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Resets all stored values
327
+ */
255
328
  reset(): void {
256
- // OpenTelemetry histograms cannot be reset, but we implement the interface
257
- this.logger.warn('OpenTelemetry histograms cannot be reset');
329
+ this.currentValues = [];
330
+ this.labeledValues.clear();
331
+ }
332
+
333
+ /**
334
+ * General function to observe an aggregation
335
+ * @param result - Observer result
336
+ * @param aggregateFn - Function that calculates the aggregation
337
+ */
338
+ private observeAggregation(result: any, aggregateFn: (arr: number[]) => number): void {
339
+ // Observe unlabeled values
340
+ if (this.currentValues.length > 0) {
341
+ result.observe(aggregateFn(this.currentValues));
342
+ }
343
+
344
+ // Observe labeled values
345
+ for (const [labelStr, values] of this.labeledValues.entries()) {
346
+ if (values.length > 0) {
347
+ const labels = parseLabelsSafely(labelStr, this.logger);
348
+ if (labels) {
349
+ result.observe(aggregateFn(values), labels);
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ private observeAvg(result: any): void {
356
+ this.observeAggregation(result, arr => arr.reduce((sum, val) => sum + val, 0) / arr.length);
357
+ }
358
+
359
+ private observeMin(result: any): void {
360
+ this.observeAggregation(result, arr => Math.min.apply(null, arr));
361
+ }
362
+
363
+ private observeMax(result: any): void {
364
+ this.observeAggregation(result, arr => Math.max.apply(null, arr));
258
365
  }
259
366
  }
260
367
 
261
368
  /**
262
- * Noop implementation of an AvgMinMax collector
369
+ * Validates that provided labels match the expected schema
370
+ * @param labels - Labels object to validate
371
+ * @param labelNames - Array of allowed label names
372
+ * @param metricType - Type of metric for error message ('Gauge', 'Histogram', 'AvgMinMax')
373
+ * @throws Error if invalid labels are provided
263
374
  */
264
- class NoopOtelAvgMinMax<Labels extends LabelsGeneric = NoLabels> implements IAvgMinMax<Labels> {
265
- constructor(
266
- private _logger: Logger,
267
- _meter: Meter,
268
- _name: string, // MetricsType must be registered in the aztec labels registry
269
- _help: string,
270
- _labelNames: Array<keyof Labels> = [],
271
- ) {}
272
-
273
- set(_values: number[]): void;
274
- set(_labels: Labels, _values: number[]): void;
275
- set(_valueOrLabels: number[] | Labels, _values?: number[]): void {}
276
-
277
- reset(): void {}
375
+ function validateLabels<Labels extends LabelsGeneric>(
376
+ labels: Labels,
377
+ labelNames: Array<keyof Labels>,
378
+ metricType: string,
379
+ ): void {
380
+ if (labelNames.length === 0) {
381
+ throw new Error(`${metricType} was initialized without labels support`);
382
+ }
383
+
384
+ for (const key of Object.keys(labels)) {
385
+ if (!labelNames.includes(key as keyof Labels)) {
386
+ throw new Error(`Invalid label key: ${key}`);
387
+ }
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Safely parses label string back to object
393
+ * @param labelStr - Stringified labels object
394
+ * @param logger - Logger instance for error reporting
395
+ * @returns Labels object or null if parsing fails
396
+ */
397
+ function parseLabelsSafely<Labels extends LabelsGeneric>(labelStr: string, logger: Logger): Labels | null {
398
+ try {
399
+ return JSON.parse(labelStr) as Labels;
400
+ } catch {
401
+ logger.error(`Failed to parse label string: ${labelStr}`);
402
+ return null;
403
+ }
278
404
  }
279
405
 
280
406
  /**
@@ -297,17 +423,17 @@ export class OtelMetricsAdapter extends Registry implements MetricsRegister {
297
423
  return new OtelGauge<Labels>(
298
424
  this.logger,
299
425
  this.meter,
300
- configuration.name as MetricsType,
426
+ configuration.name,
301
427
  configuration.help,
302
428
  configuration.labelNames,
303
429
  );
304
430
  }
305
431
 
306
432
  histogram<Labels extends LabelsGeneric = NoLabels>(configuration: HistogramConfig<Labels>): IHistogram<Labels> {
307
- return new NoopOtelHistogram<Labels>(
433
+ return new OtelHistogram<Labels>(
308
434
  this.logger,
309
435
  this.meter,
310
- configuration.name as MetricsType,
436
+ configuration.name,
311
437
  configuration.help,
312
438
  configuration.buckets,
313
439
  configuration.labelNames,
@@ -315,10 +441,10 @@ export class OtelMetricsAdapter extends Registry implements MetricsRegister {
315
441
  }
316
442
 
317
443
  avgMinMax<Labels extends LabelsGeneric = NoLabels>(configuration: AvgMinMaxConfig<Labels>): IAvgMinMax<Labels> {
318
- return new NoopOtelAvgMinMax<Labels>(
444
+ return new OtelAvgMinMax<Labels>(
319
445
  this.logger,
320
446
  this.meter,
321
- configuration.name as MetricsType,
447
+ configuration.name,
322
448
  configuration.help,
323
449
  configuration.labelNames,
324
450
  );
package/src/start.ts CHANGED
@@ -2,29 +2,30 @@ import { createLogger } from '@aztec/foundation/log';
2
2
 
3
3
  import type { TelemetryClientConfig } from './config.js';
4
4
  import { NoopTelemetryClient } from './noop.js';
5
- import { OpenTelemetryClient } from './otel.js';
6
5
  import type { TelemetryClient } from './telemetry.js';
7
6
 
8
7
  export * from './config.js';
9
8
 
10
- let initialised = false;
9
+ let initialized = false;
11
10
  let telemetry: TelemetryClient = new NoopTelemetryClient();
12
11
 
13
- export function initTelemetryClient(config: TelemetryClientConfig): TelemetryClient {
12
+ export async function initTelemetryClient(config: TelemetryClientConfig): Promise<TelemetryClient> {
14
13
  const log = createLogger('telemetry:client');
15
- if (initialised) {
14
+ if (initialized) {
16
15
  log.warn('Telemetry client has already been initialized once');
17
16
  return telemetry;
18
17
  }
19
18
 
20
- if (config.metricsCollectorUrl) {
19
+ if (config.metricsCollectorUrl || config.publicMetricsCollectorUrl) {
21
20
  log.info(`Using OpenTelemetry client with custom collector`);
21
+ // Lazy load OpenTelemetry to avoid loading heavy deps at startup
22
+ const { OpenTelemetryClient } = await import('./otel.js');
22
23
  telemetry = OpenTelemetryClient.createAndStart(config, log);
23
24
  } else {
24
25
  log.info('Using NoopTelemetryClient');
25
26
  }
26
27
 
27
- initialised = true;
28
+ initialized = true;
28
29
  return telemetry;
29
30
  }
30
31