@@ -48,14 +48,15 @@ import { OpenTelemetryMetric } from './metric.js'
4848import { OpenTelemetrySummaryGroup } from './summary-group.js'
4949import { OpenTelemetrySummary } from './summary.js'
5050import { 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'
5353import type { Duplex } from 'it-stream-types'
5454
5555// see https://betterstack.com/community/guides/observability/opentelemetry-metrics-nodejs/#prerequisites
5656
5757export interface OpenTelemetryComponents {
5858 nodeInfo : NodeInfo
59+ logger : ComponentLogger
5960}
6061
6162export interface OpenTelemetryMetricsInit {
@@ -92,14 +93,20 @@ export interface OpenTelemetryMetricsInit {
9293class OpenTelemetryMetrics implements Metrics {
9394 private transferStats : Map < string , number >
9495 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 >
96100
97101 constructor ( components : OpenTelemetryComponents , init ?: OpenTelemetryMetricsInit ) {
102+ this . log = components . logger . forComponent ( 'libp2p:open-telemetry-metrics' )
98103 this . tracer = trace . getTracer ( init ?. appName ?? components . nodeInfo . name , init ?. appVersion ?? components . nodeInfo . version )
104+ this . metrics = new Map ( )
105+ this . observables = new Map ( )
99106
100107 // holds global and per-protocol sent/received stats
101108 this . transferStats = new Map ( )
102- this . meterName = init ?. meterName ?? components . nodeInfo . name
109+ this . meter = metrics . getMeterProvider ( ) . getMeter ( init ?. meterName ?? components . nodeInfo . name )
103110
104111 this . registerCounterGroup ( 'libp2p_data_transfer_bytes_total' , {
105112 label : 'protocol' ,
@@ -126,6 +133,16 @@ class OpenTelemetryMetrics implements Metrics {
126133 '@libp2p/metrics'
127134 ]
128135
136+ start ( ) : void {
137+
138+ }
139+
140+ stop ( ) : void {
141+ this . transferStats . clear ( )
142+ this . metrics . clear ( )
143+ this . observables . clear ( )
144+ }
145+
129146 /**
130147 * Increment the transfer stat for the passed key, making sure
131148 * it exists first
@@ -177,23 +194,32 @@ class OpenTelemetryMetrics implements Metrics {
177194 throw new InvalidParametersError ( 'Metric name is required' )
178195 }
179196
180- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
181-
182197 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 , {
185199 description : opts ?. help ?? name
186200 } )
187- counter . addCallback ( async ( result ) => {
201+
202+ const calculate = opts . calculate
203+ gauge . addCallback ( async ( result ) => {
188204 result . observe ( await calculate ( ) )
189205 } )
190206
207+ this . observables . set ( name , gauge )
208+
191209 return
192210 }
193211
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
197223 }
198224
199225 registerMetricGroup ( name : string , opts : CalculatedMetricOptions < Record < string , number > > ) : void
@@ -203,14 +229,14 @@ class OpenTelemetryMetrics implements Metrics {
203229 throw new InvalidParametersError ( 'Metric name is required' )
204230 }
205231
206- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
207232 const label = opts ?. label ?? name
208233
209234 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 , {
212236 description : opts ?. help ?? name
213237 } )
238+
239+ const calculate = opts . calculate
214240 gauge . addCallback ( async ( observable ) => {
215241 const observed = await calculate ( )
216242
@@ -221,12 +247,22 @@ class OpenTelemetryMetrics implements Metrics {
221247 }
222248 } )
223249
250+ this . observables . set ( name , gauge )
251+
224252 return
225253 }
226254
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
230266 }
231267
232268 registerCounter ( name : string , opts : CalculatedMetricOptions ) : void
@@ -236,23 +272,32 @@ class OpenTelemetryMetrics implements Metrics {
236272 throw new InvalidParametersError ( 'Metric name is required' )
237273 }
238274
239- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
240-
241275 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 , {
244277 description : opts ?. help ?? name
245278 } )
279+
280+ const calculate = opts . calculate
246281 counter . addCallback ( async ( result ) => {
247282 result . observe ( await calculate ( ) )
248283 } )
249284
285+ this . observables . set ( name , counter )
286+
250287 return
251288 }
252289
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
256301 }
257302
258303 registerCounterGroup ( name : string , opts : CalculatedMetricOptions < Record < string , number > > ) : void
@@ -262,15 +307,15 @@ class OpenTelemetryMetrics implements Metrics {
262307 throw new InvalidParametersError ( 'Metric name is required' )
263308 }
264309
265- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
266310 const label = opts ?. label ?? name
267311
268312 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 , {
272314 description : opts ?. help ?? name
273315 } )
316+
317+ const values : Record < string , number > = { }
318+ const calculate = opts . calculate
274319 counter . addCallback ( async ( observable ) => {
275320 const observed = await calculate ( )
276321
@@ -290,9 +335,17 @@ class OpenTelemetryMetrics implements Metrics {
290335 return
291336 }
292337
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
296349 }
297350
298351 registerHistogram ( name : string , opts : CalculatedHistogramOptions ) : void
@@ -302,18 +355,24 @@ class OpenTelemetryMetrics implements Metrics {
302355 throw new InvalidParametersError ( 'Metric name is required' )
303356 }
304357
305- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
306-
307358 if ( isCalculatedMetricOptions < CalculatedHistogramOptions > ( opts ) ) {
308359 return
309360 }
310361
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
317376 }
318377
319378 registerHistogramGroup ( name : string , opts : CalculatedHistogramOptions < Record < string , number > > ) : void
@@ -323,19 +382,26 @@ class OpenTelemetryMetrics implements Metrics {
323382 throw new InvalidParametersError ( 'Metric name is required' )
324383 }
325384
326- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
327385 const label = opts ?. label ?? name
328386
329387 if ( isCalculatedMetricOptions < CalculatedHistogramOptions < Record < string , number > > > ( opts ) ) {
330388 return
331389 }
332390
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
339405 }
340406
341407 registerSummary ( name : string , opts : CalculatedSummaryOptions ) : void
@@ -345,15 +411,21 @@ class OpenTelemetryMetrics implements Metrics {
345411 throw new InvalidParametersError ( 'Metric name is required' )
346412 }
347413
348- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
349-
350414 if ( isCalculatedMetricOptions < CalculatedHistogramOptions > ( opts ) ) {
351415 return
352416 }
353417
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
357429 }
358430
359431 registerSummaryGroup ( name : string , opts : CalculatedSummaryOptions < Record < string , number > > ) : void
@@ -363,16 +435,23 @@ class OpenTelemetryMetrics implements Metrics {
363435 throw new InvalidParametersError ( 'Metric name is required' )
364436 }
365437
366- const meter = metrics . getMeterProvider ( ) . getMeter ( this . meterName )
367438 const label = opts ?. label ?? name
368439
369440 if ( isCalculatedMetricOptions < CalculatedSummaryOptions > ( opts ) ) {
370441 return
371442 }
372443
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
376455 }
377456
378457 createTrace ( ) : any {
0 commit comments