@@ -48,14 +48,15 @@ import { OpenTelemetryMetric } from './metric.js'
48
48
import { OpenTelemetrySummaryGroup } from './summary-group.js'
49
49
import { OpenTelemetrySummary } from './summary.js'
50
50
import { collectSystemMetrics } from './system-metrics.js'
51
- import type { MultiaddrConnection , Stream , Connection , Metric , MetricGroup , Metrics , CalculatedMetricOptions , MetricOptions , Counter , CounterGroup , Histogram , HistogramOptions , HistogramGroup , Summary , SummaryOptions , SummaryGroup , CalculatedHistogramOptions , CalculatedSummaryOptions , NodeInfo , TraceFunctionOptions , TraceGeneratorFunctionOptions , TraceAttributes } from '@libp2p/interface'
52
- import type { Span , Attributes } from '@opentelemetry/api'
51
+ import type { MultiaddrConnection , Stream , Connection , Metric , MetricGroup , Metrics , CalculatedMetricOptions , MetricOptions , Counter , CounterGroup , Histogram , HistogramOptions , HistogramGroup , Summary , SummaryOptions , SummaryGroup , CalculatedHistogramOptions , CalculatedSummaryOptions , NodeInfo , TraceFunctionOptions , TraceGeneratorFunctionOptions , TraceAttributes , ComponentLogger , Logger } from '@libp2p/interface'
52
+ import type { Span , Attributes , Meter , Observable } from '@opentelemetry/api'
53
53
import type { Duplex } from 'it-stream-types'
54
54
55
55
// see https://betterstack.com/community/guides/observability/opentelemetry-metrics-nodejs/#prerequisites
56
56
57
57
export interface OpenTelemetryComponents {
58
58
nodeInfo : NodeInfo
59
+ logger : ComponentLogger
59
60
}
60
61
61
62
export interface OpenTelemetryMetricsInit {
@@ -92,14 +93,20 @@ export interface OpenTelemetryMetricsInit {
92
93
class OpenTelemetryMetrics implements Metrics {
93
94
private transferStats : Map < string , number >
94
95
private readonly tracer : ReturnType < typeof trace . getTracer >
95
- private readonly meterName : string
96
+ private readonly meter : Meter
97
+ private readonly log : Logger
98
+ private metrics : Map < string , OpenTelemetryMetric | OpenTelemetryMetricGroup | OpenTelemetryCounter | OpenTelemetryCounterGroup | OpenTelemetryHistogram | OpenTelemetryHistogramGroup | OpenTelemetrySummary | OpenTelemetrySummaryGroup >
99
+ private observables : Map < string , Observable >
96
100
97
101
constructor ( components : OpenTelemetryComponents , init ?: OpenTelemetryMetricsInit ) {
102
+ this . log = components . logger . forComponent ( 'libp2p:open-telemetry-metrics' )
98
103
this . tracer = trace . getTracer ( init ?. appName ?? components . nodeInfo . name , init ?. appVersion ?? components . nodeInfo . version )
104
+ this . metrics = new Map ( )
105
+ this . observables = new Map ( )
99
106
100
107
// holds global and per-protocol sent/received stats
101
108
this . transferStats = new Map ( )
102
- this . meterName = init ?. meterName ?? components . nodeInfo . name
109
+ this . meter = metrics . getMeterProvider ( ) . getMeter ( init ?. meterName ?? components . nodeInfo . name )
103
110
104
111
this . registerCounterGroup ( 'libp2p_data_transfer_bytes_total' , {
105
112
label : 'protocol' ,
@@ -126,6 +133,16 @@ class OpenTelemetryMetrics implements Metrics {
126
133
'@libp2p/metrics'
127
134
]
128
135
136
+ start ( ) : void {
137
+
138
+ }
139
+
140
+ stop ( ) : void {
141
+ this . transferStats . clear ( )
142
+ this . metrics . clear ( )
143
+ this . observables . clear ( )
144
+ }
145
+
129
146
/**
130
147
* Increment the transfer stat for the passed key, making sure
131
148
* it exists first
@@ -177,23 +194,32 @@ class OpenTelemetryMetrics implements Metrics {
177
194
throw new InvalidParametersError ( 'Metric name is required' )
178
195
}
179
196
180
- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
181
-
182
197
if ( isCalculatedMetricOptions < CalculatedMetricOptions > ( opts ) ) {
183
- const calculate = opts . calculate
184
- const counter = meter . createObservableGauge ( name , {
198
+ const gauge = this . observables . get ( name ) ?? this . meter . createObservableGauge ( name , {
185
199
description : opts ?. help ?? name
186
200
} )
187
- counter . addCallback ( async ( result ) => {
201
+
202
+ const calculate = opts . calculate
203
+ gauge . addCallback ( async ( result ) => {
188
204
result . observe ( await calculate ( ) )
189
205
} )
190
206
207
+ this . observables . set ( name , gauge )
208
+
191
209
return
192
210
}
193
211
194
- return new OpenTelemetryMetric ( meter . createGauge ( name , {
195
- description : opts ?. help ?? name
196
- } ) )
212
+ let metric = this . metrics . get ( name )
213
+
214
+ if ( metric == null ) {
215
+ metric = new OpenTelemetryMetric ( this . meter . createGauge ( name , {
216
+ description : opts ?. help ?? name
217
+ } ) )
218
+
219
+ this . metrics . set ( name , metric )
220
+ }
221
+
222
+ return metric
197
223
}
198
224
199
225
registerMetricGroup ( name : string , opts : CalculatedMetricOptions < Record < string , number > > ) : void
@@ -203,14 +229,14 @@ class OpenTelemetryMetrics implements Metrics {
203
229
throw new InvalidParametersError ( 'Metric name is required' )
204
230
}
205
231
206
- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
207
232
const label = opts ?. label ?? name
208
233
209
234
if ( isCalculatedMetricOptions < CalculatedMetricOptions < Record < string , number > > > ( opts ) ) {
210
- const calculate = opts . calculate
211
- const gauge = meter . createObservableGauge ( name , {
235
+ const gauge = this . observables . get ( name ) ?? this . meter . createObservableGauge ( name , {
212
236
description : opts ?. help ?? name
213
237
} )
238
+
239
+ const calculate = opts . calculate
214
240
gauge . addCallback ( async ( observable ) => {
215
241
const observed = await calculate ( )
216
242
@@ -221,12 +247,22 @@ class OpenTelemetryMetrics implements Metrics {
221
247
}
222
248
} )
223
249
250
+ this . observables . set ( name , gauge )
251
+
224
252
return
225
253
}
226
254
227
- return new OpenTelemetryMetricGroup ( label , meter . createGauge ( name , {
228
- description : opts ?. help ?? name
229
- } ) )
255
+ let metric = this . metrics . get ( name )
256
+
257
+ if ( metric == null ) {
258
+ metric = new OpenTelemetryMetricGroup ( label , this . meter . createGauge ( name , {
259
+ description : opts ?. help ?? name
260
+ } ) )
261
+
262
+ this . metrics . set ( name , metric )
263
+ }
264
+
265
+ return metric
230
266
}
231
267
232
268
registerCounter ( name : string , opts : CalculatedMetricOptions ) : void
@@ -236,23 +272,32 @@ class OpenTelemetryMetrics implements Metrics {
236
272
throw new InvalidParametersError ( 'Metric name is required' )
237
273
}
238
274
239
- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
240
-
241
275
if ( isCalculatedMetricOptions < CalculatedMetricOptions > ( opts ) ) {
242
- const calculate = opts . calculate
243
- const counter = meter . createObservableCounter ( name , {
276
+ const counter = this . observables . get ( name ) ?? this . meter . createObservableCounter ( name , {
244
277
description : opts ?. help ?? name
245
278
} )
279
+
280
+ const calculate = opts . calculate
246
281
counter . addCallback ( async ( result ) => {
247
282
result . observe ( await calculate ( ) )
248
283
} )
249
284
285
+ this . observables . set ( name , counter )
286
+
250
287
return
251
288
}
252
289
253
- return new OpenTelemetryCounter ( meter . createCounter ( name , {
254
- description : opts ?. help ?? name
255
- } ) )
290
+ let metric = this . metrics . get ( name )
291
+
292
+ if ( metric == null ) {
293
+ metric = new OpenTelemetryCounter ( this . meter . createCounter ( name , {
294
+ description : opts ?. help ?? name
295
+ } ) )
296
+
297
+ this . metrics . set ( name , metric )
298
+ }
299
+
300
+ return metric
256
301
}
257
302
258
303
registerCounterGroup ( name : string , opts : CalculatedMetricOptions < Record < string , number > > ) : void
@@ -262,15 +307,15 @@ class OpenTelemetryMetrics implements Metrics {
262
307
throw new InvalidParametersError ( 'Metric name is required' )
263
308
}
264
309
265
- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
266
310
const label = opts ?. label ?? name
267
311
268
312
if ( isCalculatedMetricOptions < CalculatedMetricOptions < Record < string , number > > > ( opts ) ) {
269
- const values : Record < string , number > = { }
270
- const calculate = opts . calculate
271
- const counter = meter . createObservableGauge ( name , {
313
+ const counter = this . observables . get ( name ) ?? this . meter . createObservableCounter ( name , {
272
314
description : opts ?. help ?? name
273
315
} )
316
+
317
+ const values : Record < string , number > = { }
318
+ const calculate = opts . calculate
274
319
counter . addCallback ( async ( observable ) => {
275
320
const observed = await calculate ( )
276
321
@@ -290,9 +335,17 @@ class OpenTelemetryMetrics implements Metrics {
290
335
return
291
336
}
292
337
293
- return new OpenTelemetryCounterGroup ( label , meter . createCounter ( name , {
294
- description : opts ?. help ?? name
295
- } ) )
338
+ let metric = this . metrics . get ( name )
339
+
340
+ if ( metric == null ) {
341
+ metric = new OpenTelemetryCounterGroup ( label , this . meter . createCounter ( name , {
342
+ description : opts ?. help ?? name
343
+ } ) )
344
+
345
+ this . metrics . set ( name , metric )
346
+ }
347
+
348
+ return metric
296
349
}
297
350
298
351
registerHistogram ( name : string , opts : CalculatedHistogramOptions ) : void
@@ -302,18 +355,24 @@ class OpenTelemetryMetrics implements Metrics {
302
355
throw new InvalidParametersError ( 'Metric name is required' )
303
356
}
304
357
305
- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
306
-
307
358
if ( isCalculatedMetricOptions < CalculatedHistogramOptions > ( opts ) ) {
308
359
return
309
360
}
310
361
311
- return new OpenTelemetryHistogram ( meter . createHistogram ( name , {
312
- advice : {
313
- explicitBucketBoundaries : opts . buckets
314
- } ,
315
- description : opts ?. help ?? name
316
- } ) )
362
+ let metric = this . metrics . get ( name )
363
+
364
+ if ( metric == null ) {
365
+ metric = new OpenTelemetryHistogram ( this . meter . createHistogram ( name , {
366
+ advice : {
367
+ explicitBucketBoundaries : opts . buckets
368
+ } ,
369
+ description : opts ?. help ?? name
370
+ } ) )
371
+
372
+ this . metrics . set ( name , metric )
373
+ }
374
+
375
+ return metric
317
376
}
318
377
319
378
registerHistogramGroup ( name : string , opts : CalculatedHistogramOptions < Record < string , number > > ) : void
@@ -323,19 +382,26 @@ class OpenTelemetryMetrics implements Metrics {
323
382
throw new InvalidParametersError ( 'Metric name is required' )
324
383
}
325
384
326
- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
327
385
const label = opts ?. label ?? name
328
386
329
387
if ( isCalculatedMetricOptions < CalculatedHistogramOptions < Record < string , number > > > ( opts ) ) {
330
388
return
331
389
}
332
390
333
- return new OpenTelemetryHistogramGroup ( label , meter . createHistogram ( name , {
334
- advice : {
335
- explicitBucketBoundaries : opts . buckets
336
- } ,
337
- description : opts ?. help ?? name
338
- } ) )
391
+ let metric = this . metrics . get ( name )
392
+
393
+ if ( metric == null ) {
394
+ metric = new OpenTelemetryHistogramGroup ( label , this . meter . createHistogram ( name , {
395
+ advice : {
396
+ explicitBucketBoundaries : opts . buckets
397
+ } ,
398
+ description : opts ?. help ?? name
399
+ } ) )
400
+
401
+ this . metrics . set ( name , metric )
402
+ }
403
+
404
+ return metric
339
405
}
340
406
341
407
registerSummary ( name : string , opts : CalculatedSummaryOptions ) : void
@@ -345,15 +411,21 @@ class OpenTelemetryMetrics implements Metrics {
345
411
throw new InvalidParametersError ( 'Metric name is required' )
346
412
}
347
413
348
- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
349
-
350
414
if ( isCalculatedMetricOptions < CalculatedHistogramOptions > ( opts ) ) {
351
415
return
352
416
}
353
417
354
- return new OpenTelemetrySummary ( meter . createGauge ( name , {
355
- description : opts ?. help ?? name
356
- } ) )
418
+ let metric = this . metrics . get ( name )
419
+
420
+ if ( metric == null ) {
421
+ metric = new OpenTelemetrySummary ( this . meter . createGauge ( name , {
422
+ description : opts ?. help ?? name
423
+ } ) )
424
+
425
+ this . metrics . set ( name , metric )
426
+ }
427
+
428
+ return metric
357
429
}
358
430
359
431
registerSummaryGroup ( name : string , opts : CalculatedSummaryOptions < Record < string , number > > ) : void
@@ -363,16 +435,23 @@ class OpenTelemetryMetrics implements Metrics {
363
435
throw new InvalidParametersError ( 'Metric name is required' )
364
436
}
365
437
366
- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
367
438
const label = opts ?. label ?? name
368
439
369
440
if ( isCalculatedMetricOptions < CalculatedSummaryOptions > ( opts ) ) {
370
441
return
371
442
}
372
443
373
- return new OpenTelemetrySummaryGroup ( label , meter . createGauge ( name , {
374
- description : opts ?. help ?? name
375
- } ) )
444
+ let metric = this . metrics . get ( name )
445
+
446
+ if ( metric == null ) {
447
+ metric = new OpenTelemetrySummaryGroup ( label , this . meter . createGauge ( name , {
448
+ description : opts ?. help ?? name
449
+ } ) )
450
+
451
+ this . metrics . set ( name , metric )
452
+ }
453
+
454
+ return metric
376
455
}
377
456
378
457
createTrace ( ) : any {
0 commit comments