@libp2p/simple-metrics 1.3.11 → 1.3.12-3528df829

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/src/index.ts CHANGED
@@ -27,13 +27,35 @@ import { serviceCapabilities } from '@libp2p/interface'
27
27
  import { logger } from '@libp2p/logger'
28
28
  import each from 'it-foreach'
29
29
  import { TDigest } from 'tdigest'
30
- import type { Startable, MultiaddrConnection, Stream, Connection, Metric, MetricGroup, StopTimer, Metrics, CalculatedMetricOptions, MetricOptions, Counter, CounterGroup, CalculateMetric, Histogram, HistogramOptions, HistogramGroup, Summary, SummaryOptions, SummaryGroup, CalculatedHistogramOptions, CalculatedSummaryOptions } from '@libp2p/interface'
30
+ import type { Startable, MultiaddrConnection, Stream, Connection, Metric, MetricGroup, StopTimer, Metrics, CalculatedMetricOptions, MetricOptions, Counter, CounterGroup, CalculateMetric, Histogram, HistogramOptions, HistogramGroup, Summary, SummaryOptions, SummaryGroup, CalculatedHistogramOptions, CalculatedSummaryOptions, ComponentLogger, Logger } from '@libp2p/interface'
31
31
  import type { Duplex } from 'it-stream-types'
32
32
 
33
33
  const log = logger('libp2p:simple-metrics')
34
34
 
35
35
  class DefaultMetric implements Metric {
36
- public value: number = 0
36
+ private value: number = 0
37
+ private readonly calculators: CalculateMetric[]
38
+
39
+ constructor (opts: CalculatedMetricOptions) {
40
+ this.calculators = []
41
+
42
+ if (opts.calculate != null) {
43
+ this.calculators.push(opts.calculate)
44
+ }
45
+ }
46
+
47
+ async collect (): Promise<number> {
48
+ if (this.calculators.length) {
49
+ const values = await Promise.all(this.calculators.map(async calculate => calculate()))
50
+ return values.reduce((acc, curr) => acc + curr, 0)
51
+ }
52
+
53
+ return this.value
54
+ }
55
+
56
+ addCalculator (calculator: CalculateMetric): void {
57
+ this.calculators.push(calculator)
58
+ }
37
59
 
38
60
  update (value: number): void {
39
61
  this.value = value
@@ -61,7 +83,35 @@ class DefaultMetric implements Metric {
61
83
  }
62
84
 
63
85
  class DefaultGroupMetric implements MetricGroup {
64
- public values: Record<string, number> = {}
86
+ private values: Record<string, number> = {}
87
+ private readonly calculators: CalculateMetric[]
88
+
89
+ constructor () {
90
+ this.calculators = []
91
+ }
92
+
93
+ async collect (): Promise<Record<string, number>> {
94
+ if (this.calculators.length) {
95
+ const output: Record<string, number> = {}
96
+
97
+ await Promise.all(this.calculators.map(async calculate => {
98
+ const values = await calculate()
99
+
100
+ Object.entries(values).forEach(([key, value]) => {
101
+ output[key] ??= 0
102
+ output[key] += value
103
+ })
104
+ }))
105
+
106
+ return output
107
+ }
108
+
109
+ return this.values
110
+ }
111
+
112
+ addCalculator (calculator: CalculateMetric): void {
113
+ this.calculators.push(calculator)
114
+ }
65
115
 
66
116
  update (values: Record<string, number>): void {
67
117
  Object.entries(values).forEach(([key, value]) => {
@@ -101,9 +151,10 @@ class DefaultGroupMetric implements MetricGroup {
101
151
  }
102
152
 
103
153
  class DefaultHistogram implements Histogram {
104
- public bucketValues = new Map<number, number>()
105
- public countValue: number = 0
106
- public sumValue: number = 0
154
+ private bucketValues = new Map<number, number>()
155
+ private countValue: number = 0
156
+ private sumValue: number = 0
157
+ private readonly calculators: CalculateMetric[]
107
158
 
108
159
  constructor (opts: HistogramOptions) {
109
160
  const buckets = [
@@ -113,6 +164,27 @@ class DefaultHistogram implements Histogram {
113
164
  for (const bucket of buckets) {
114
165
  this.bucketValues.set(bucket, 0)
115
166
  }
167
+ this.calculators = []
168
+ }
169
+
170
+ public async collect (): Promise<{ count: number, sum: number, buckets: Record<number, number> }> {
171
+ if (this.calculators.length > 0) {
172
+ const values = await Promise.all(this.calculators.map(async calculate => calculate()))
173
+
174
+ for (const value of values) {
175
+ this.observe(value)
176
+ }
177
+ }
178
+
179
+ return {
180
+ count: this.countValue,
181
+ sum: this.sumValue,
182
+ buckets: { ...this.bucketValues }
183
+ }
184
+ }
185
+
186
+ addCalculator (calculator: CalculateMetric): void {
187
+ this.calculators.push(calculator)
116
188
  }
117
189
 
118
190
  observe (value: number): void {
@@ -145,9 +217,25 @@ class DefaultHistogram implements Histogram {
145
217
 
146
218
  class DefaultHistogramGroup implements HistogramGroup {
147
219
  public histograms: Record<string, DefaultHistogram> = {}
220
+ private readonly calculators: CalculateMetric[]
148
221
 
149
222
  constructor (opts: HistogramOptions) {
150
223
  this.histograms = {}
224
+ this.calculators = []
225
+ }
226
+
227
+ public async collect (): Promise<Record<string, { count: number, sum: number, buckets: Record<number, number> }>> {
228
+ const output: Record<string, { count: number, sum: number, buckets: Record<number, number> }> = {}
229
+
230
+ for (const [key, histogram] of Object.entries(this.histograms)) {
231
+ output[key] = await histogram.collect()
232
+ }
233
+
234
+ return output
235
+ }
236
+
237
+ addCalculator (calculator: CalculateMetric): void {
238
+ this.calculators.push(calculator)
151
239
  }
152
240
 
153
241
  observe (values: Partial<Record<string, number>>): void {
@@ -181,10 +269,31 @@ class DefaultSummary implements Summary {
181
269
  public percentiles: number[]
182
270
  public tdigest = new TDigest(0.01)
183
271
  private readonly compressCount: number
272
+ private readonly calculators: CalculateMetric[]
184
273
 
185
274
  constructor (opts: SummaryOptions) {
186
275
  this.percentiles = opts.percentiles ?? [0.01, 0.05, 0.5, 0.9, 0.95, 0.99, 0.999]
187
276
  this.compressCount = opts.compressCount ?? 1000
277
+ this.calculators = []
278
+ }
279
+
280
+ public async collect (): Promise<{ count: number, sum: number, percentiles: Record<string, number> }> {
281
+ if (this.calculators.length > 0) {
282
+ const values = await Promise.all(this.calculators.map(async calculate => calculate()))
283
+ for (const value of values) {
284
+ this.observe(value)
285
+ }
286
+ }
287
+
288
+ return {
289
+ count: this.countValue,
290
+ sum: this.sumValue,
291
+ percentiles: Object.fromEntries(this.percentiles.map(p => [p, this.tdigest.percentile(p)]))
292
+ }
293
+ }
294
+
295
+ addCalculator (calculator: CalculateMetric): void {
296
+ this.calculators.push(calculator)
188
297
  }
189
298
 
190
299
  observe (value: number): void {
@@ -216,10 +325,34 @@ class DefaultSummary implements Summary {
216
325
  class DefaultSummaryGroup implements SummaryGroup {
217
326
  public summaries: Record<string, DefaultSummary> = {}
218
327
  private readonly opts: SummaryOptions
328
+ private readonly calculators: CalculateMetric<Record<string, number>>[]
219
329
 
220
330
  constructor (opts: SummaryOptions) {
221
331
  this.summaries = {}
222
332
  this.opts = opts
333
+ this.calculators = []
334
+ }
335
+
336
+ public async collect (): Promise<Record<string, { count: number, sum: number, percentiles: Record<string, number> }>> {
337
+ if (this.calculators.length > 0) {
338
+ await Promise.all(this.calculators.map(async calculate => {
339
+ this.observe(await calculate())
340
+ }))
341
+ }
342
+
343
+ return {
344
+ ...Object.fromEntries(Object.entries(this.summaries).map(([key, summary]) => {
345
+ return [key, {
346
+ count: summary.countValue,
347
+ sum: summary.sumValue,
348
+ percentiles: Object.fromEntries(summary.percentiles.map(p => [p, summary.tdigest.percentile(p)]))
349
+ }]
350
+ }))
351
+ }
352
+ }
353
+
354
+ addCalculator (calculator: CalculateMetric<Record<string, number>>): void {
355
+ this.calculators.push(calculator)
223
356
  }
224
357
 
225
358
  observe (values: Record<string, number>): void {
@@ -261,15 +394,21 @@ export interface SimpleMetricsInit {
261
394
  onMetrics: OnMetrics
262
395
  }
263
396
 
397
+ export interface SimpleMetricsComponents {
398
+ logger: ComponentLogger
399
+ }
400
+
264
401
  class SimpleMetrics implements Metrics, Startable {
265
- public metrics = new Map<string, DefaultMetric | DefaultGroupMetric | CalculateMetric | DefaultHistogram | DefaultHistogramGroup | DefaultSummary | DefaultSummaryGroup>()
402
+ public metrics = new Map<string, DefaultMetric | DefaultGroupMetric | DefaultHistogram | DefaultHistogramGroup | DefaultSummary | DefaultSummaryGroup>()
266
403
  private readonly transferStats: Map<string, number>
267
404
  private started: boolean
268
405
  private interval?: ReturnType<typeof setInterval>
269
406
  private readonly intervalMs: number
270
407
  private readonly onMetrics: OnMetrics
408
+ private readonly log: Logger
271
409
 
272
- constructor (components: unknown, init: SimpleMetricsInit) {
410
+ constructor (components: SimpleMetricsComponents, init: SimpleMetricsInit) {
411
+ this.log = components.logger.forComponent('libp2p:simple-metrics')
273
412
  this.started = false
274
413
 
275
414
  this._emitMetrics = this._emitMetrics.bind(this)
@@ -301,6 +440,8 @@ class SimpleMetrics implements Metrics, Startable {
301
440
  this.started = false
302
441
 
303
442
  clearInterval(this.interval)
443
+ this.metrics.clear()
444
+ this.transferStats.clear()
304
445
  }
305
446
 
306
447
  private _emitMetrics (): void {
@@ -308,45 +449,7 @@ class SimpleMetrics implements Metrics, Startable {
308
449
  const output: Record<string, any> = {}
309
450
 
310
451
  for (const [name, metric] of this.metrics.entries()) {
311
- if (metric instanceof DefaultMetric) {
312
- output[name] = metric.value
313
- } else if (metric instanceof DefaultGroupMetric) {
314
- output[name] = metric.values
315
- } else if (metric instanceof DefaultHistogram) {
316
- output[name] = {
317
- count: metric.countValue,
318
- sum: metric.sumValue,
319
- buckets: { ...metric.bucketValues }
320
- }
321
- } else if (metric instanceof DefaultHistogramGroup) {
322
- output[name] = {
323
- ...Object.fromEntries(Object.entries(metric.histograms).map(([key, histogram]) => {
324
- return [key, {
325
- count: histogram.countValue,
326
- sum: histogram.sumValue,
327
- buckets: { ...histogram.bucketValues }
328
- }]
329
- }))
330
- }
331
- } else if (metric instanceof DefaultSummary) {
332
- output[name] = {
333
- count: metric.countValue,
334
- sum: metric.sumValue,
335
- percentiles: Object.fromEntries(metric.percentiles.map(p => [p, metric.tdigest.percentile(p)]))
336
- }
337
- } else if (metric instanceof DefaultSummaryGroup) {
338
- output[name] = {
339
- ...Object.fromEntries(Object.entries(metric.summaries).map(([key, summary]) => {
340
- return [key, {
341
- count: summary.countValue,
342
- sum: summary.sumValue,
343
- percentiles: Object.fromEntries(summary.percentiles.map(p => [p, summary.tdigest.percentile(p)]))
344
- }]
345
- }))
346
- }
347
- } else {
348
- output[name] = await metric()
349
- }
452
+ output[name] = await metric.collect()
350
453
  }
351
454
 
352
455
  this.onMetrics(structuredClone(output))
@@ -407,13 +510,19 @@ class SimpleMetrics implements Metrics, Startable {
407
510
  throw new Error('Metric name is required')
408
511
  }
409
512
 
410
- if (opts?.calculate != null) {
411
- // calculated metric
412
- this.metrics.set(name, opts.calculate)
413
- return
513
+ let metric = this.metrics.get(name)
514
+
515
+ if (metric != null) {
516
+ this.log('reuse existing metric', name)
517
+
518
+ if (opts.calculate != null) {
519
+ metric.addCalculator(opts.calculate)
520
+ }
521
+
522
+ return this.metrics.get(name)
414
523
  }
415
524
 
416
- const metric = new DefaultMetric()
525
+ metric = new DefaultMetric(opts)
417
526
  this.metrics.set(name, metric)
418
527
 
419
528
  return metric
@@ -445,13 +554,19 @@ class SimpleMetrics implements Metrics, Startable {
445
554
  throw new Error('Metric name is required')
446
555
  }
447
556
 
448
- if (opts?.calculate != null) {
449
- // calculated metric
450
- this.metrics.set(name, opts.calculate)
451
- return
557
+ let metric = this.metrics.get(name)
558
+
559
+ if (metric != null) {
560
+ this.log('reuse existing metric', name)
561
+
562
+ if (opts.calculate != null) {
563
+ metric.addCalculator(opts.calculate)
564
+ }
565
+
566
+ return this.metrics.get(name)
452
567
  }
453
568
 
454
- const metric = new DefaultMetric()
569
+ metric = new DefaultMetric(opts)
455
570
  this.metrics.set(name, metric)
456
571
 
457
572
  return metric
@@ -464,13 +579,19 @@ class SimpleMetrics implements Metrics, Startable {
464
579
  throw new Error('Metric name is required')
465
580
  }
466
581
 
467
- if (opts?.calculate != null) {
468
- // calculated metric
469
- this.metrics.set(name, opts.calculate)
470
- return
582
+ let metric = this.metrics.get(name)
583
+
584
+ if (metric != null) {
585
+ this.log('reuse existing metric', name)
586
+
587
+ if (opts.calculate != null) {
588
+ metric.addCalculator(opts.calculate)
589
+ }
590
+
591
+ return this.metrics.get(name)
471
592
  }
472
593
 
473
- const metric = new DefaultGroupMetric()
594
+ metric = new DefaultGroupMetric()
474
595
  this.metrics.set(name, metric)
475
596
 
476
597
  return metric
@@ -483,13 +604,19 @@ class SimpleMetrics implements Metrics, Startable {
483
604
  throw new Error('Metric name is required')
484
605
  }
485
606
 
486
- if (opts?.calculate != null) {
487
- // calculated metric
488
- this.metrics.set(name, opts.calculate)
489
- return
607
+ let metric = this.metrics.get(name)
608
+
609
+ if (metric != null) {
610
+ this.log('reuse existing metric', name)
611
+
612
+ if (opts.calculate != null) {
613
+ metric.addCalculator(opts.calculate)
614
+ }
615
+
616
+ return this.metrics.get(name)
490
617
  }
491
618
 
492
- const metric = new DefaultHistogram(opts)
619
+ metric = new DefaultHistogram(opts)
493
620
  this.metrics.set(name, metric)
494
621
 
495
622
  return metric
@@ -502,13 +629,19 @@ class SimpleMetrics implements Metrics, Startable {
502
629
  throw new Error('Metric name is required')
503
630
  }
504
631
 
505
- if (opts?.calculate != null) {
506
- // calculated metric
507
- this.metrics.set(name, opts.calculate)
508
- return
632
+ let metric = this.metrics.get(name)
633
+
634
+ if (metric != null) {
635
+ this.log('reuse existing metric', name)
636
+
637
+ if (opts.calculate != null) {
638
+ metric.addCalculator(opts.calculate)
639
+ }
640
+
641
+ return this.metrics.get(name)
509
642
  }
510
643
 
511
- const metric = new DefaultHistogramGroup(opts)
644
+ metric = new DefaultHistogramGroup(opts)
512
645
  this.metrics.set(name, metric)
513
646
 
514
647
  return metric
@@ -521,13 +654,19 @@ class SimpleMetrics implements Metrics, Startable {
521
654
  throw new Error('Metric name is required')
522
655
  }
523
656
 
524
- if (opts?.calculate != null) {
525
- // calculated metric
526
- this.metrics.set(name, opts.calculate)
527
- return
657
+ let metric = this.metrics.get(name)
658
+
659
+ if (metric != null) {
660
+ this.log('reuse existing metric', name)
661
+
662
+ if (opts.calculate != null) {
663
+ metric.addCalculator(opts.calculate)
664
+ }
665
+
666
+ return this.metrics.get(name)
528
667
  }
529
668
 
530
- const metric = new DefaultSummary(opts)
669
+ metric = new DefaultSummary(opts)
531
670
  this.metrics.set(name, metric)
532
671
 
533
672
  return metric
@@ -540,13 +679,19 @@ class SimpleMetrics implements Metrics, Startable {
540
679
  throw new Error('Metric name is required')
541
680
  }
542
681
 
543
- if (opts?.calculate != null) {
544
- // calculated metric
545
- this.metrics.set(name, opts.calculate)
546
- return
682
+ let metric = this.metrics.get(name)
683
+
684
+ if (metric != null) {
685
+ this.log('reuse existing metric', name)
686
+
687
+ if (opts.calculate != null) {
688
+ metric.addCalculator(opts.calculate)
689
+ }
690
+
691
+ return this.metrics.get(name)
547
692
  }
548
693
 
549
- const metric = new DefaultSummaryGroup(opts)
694
+ metric = new DefaultSummaryGroup(opts)
550
695
  this.metrics.set(name, metric)
551
696
 
552
697
  return metric
@@ -562,6 +707,6 @@ class SimpleMetrics implements Metrics, Startable {
562
707
  }
563
708
  }
564
709
 
565
- export function simpleMetrics (init: SimpleMetricsInit): (components: unknown) => Metrics {
566
- return (components: unknown) => new SimpleMetrics(components, init)
710
+ export function simpleMetrics (init: SimpleMetricsInit): (components: SimpleMetricsComponents) => Metrics {
711
+ return (components) => new SimpleMetrics(components, init)
567
712
  }
@@ -1,8 +0,0 @@
1
- {
2
- "OnMetrics": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_simple-metrics.OnMetrics.html",
3
- ".:OnMetrics": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_simple-metrics.OnMetrics.html",
4
- "SimpleMetricsInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_simple-metrics.SimpleMetricsInit.html",
5
- ".:SimpleMetricsInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_simple-metrics.SimpleMetricsInit.html",
6
- "simpleMetrics": "https://libp2p.github.io/js-libp2p/functions/_libp2p_simple-metrics.simpleMetrics.html",
7
- ".:simpleMetrics": "https://libp2p.github.io/js-libp2p/functions/_libp2p_simple-metrics.simpleMetrics.html"
8
- }