@libp2p/simple-metrics 1.3.12 → 1.3.13

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,26 @@ 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
- class DefaultMetric implements Metric {
36
- public value: number = 0
35
+ class SimpleMetric implements Metric {
36
+ private value: number = 0
37
+ private readonly calculate?: CalculateMetric
38
+
39
+ constructor (opts?: CalculatedMetricOptions) {
40
+ this.calculate = opts?.calculate
41
+ }
42
+
43
+ async collect (): Promise<number> {
44
+ if (this.calculate != null) {
45
+ return this.calculate()
46
+ }
47
+
48
+ return this.value
49
+ }
37
50
 
38
51
  update (value: number): void {
39
52
  this.value = value
@@ -60,8 +73,21 @@ class DefaultMetric implements Metric {
60
73
  }
61
74
  }
62
75
 
63
- class DefaultGroupMetric implements MetricGroup {
64
- public values: Record<string, number> = {}
76
+ class SimpleGroupMetric implements MetricGroup {
77
+ private values: Record<string, number> = {}
78
+ private readonly calculate?: CalculateMetric<Record<string, number>>
79
+
80
+ constructor (opts?: CalculatedMetricOptions<Record<string, number>>) {
81
+ this.calculate = opts?.calculate
82
+ }
83
+
84
+ async collect (): Promise<Record<string, number>> {
85
+ if (this.calculate != null) {
86
+ return this.calculate()
87
+ }
88
+
89
+ return this.values
90
+ }
65
91
 
66
92
  update (values: Record<string, number>): void {
67
93
  Object.entries(values).forEach(([key, value]) => {
@@ -100,19 +126,33 @@ class DefaultGroupMetric implements MetricGroup {
100
126
  }
101
127
  }
102
128
 
103
- class DefaultHistogram implements Histogram {
104
- public bucketValues = new Map<number, number>()
105
- public countValue: number = 0
106
- public sumValue: number = 0
129
+ class SimpleHistogram implements Histogram {
130
+ private bucketValues = new Map<number, number>()
131
+ private countValue: number = 0
132
+ private sumValue: number = 0
133
+ private readonly calculate?: CalculateMetric
107
134
 
108
- constructor (opts: HistogramOptions) {
135
+ constructor (opts?: CalculatedHistogramOptions) {
109
136
  const buckets = [
110
- ...(opts.buckets ?? [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]),
137
+ ...(opts?.buckets ?? [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]),
111
138
  Infinity
112
139
  ]
113
140
  for (const bucket of buckets) {
114
141
  this.bucketValues.set(bucket, 0)
115
142
  }
143
+ this.calculate = opts?.calculate
144
+ }
145
+
146
+ public async collect (): Promise<{ count: number, sum: number, buckets: Record<number, number> }> {
147
+ if (this.calculate != null) {
148
+ this.observe(await this.calculate())
149
+ }
150
+
151
+ return {
152
+ count: this.countValue,
153
+ sum: this.sumValue,
154
+ buckets: { ...this.bucketValues }
155
+ }
116
156
  }
117
157
 
118
158
  observe (value: number): void {
@@ -143,17 +183,29 @@ class DefaultHistogram implements Histogram {
143
183
  }
144
184
  }
145
185
 
146
- class DefaultHistogramGroup implements HistogramGroup {
147
- public histograms: Record<string, DefaultHistogram> = {}
186
+ class SimpleHistogramGroup implements HistogramGroup {
187
+ public histograms: Record<string, SimpleHistogram> = {}
188
+ private readonly calculate?: CalculateMetric
148
189
 
149
- constructor (opts: HistogramOptions) {
190
+ constructor (opts?: CalculatedHistogramOptions) {
150
191
  this.histograms = {}
192
+ this.calculate = opts?.calculate
193
+ }
194
+
195
+ public async collect (): Promise<Record<string, { count: number, sum: number, buckets: Record<number, number> }>> {
196
+ const output: Record<string, { count: number, sum: number, buckets: Record<number, number> }> = {}
197
+
198
+ for (const [key, histogram] of Object.entries(this.histograms)) {
199
+ output[key] = await histogram.collect()
200
+ }
201
+
202
+ return output
151
203
  }
152
204
 
153
205
  observe (values: Partial<Record<string, number>>): void {
154
206
  for (const [key, value] of Object.entries(values) as Array<[string, number]>) {
155
207
  if (this.histograms[key] === undefined) {
156
- this.histograms[key] = new DefaultHistogram({})
208
+ this.histograms[key] = new SimpleHistogram()
157
209
  }
158
210
 
159
211
  this.histograms[key].observe(value)
@@ -175,16 +227,30 @@ class DefaultHistogramGroup implements HistogramGroup {
175
227
  }
176
228
  }
177
229
 
178
- class DefaultSummary implements Summary {
230
+ class SimpleSummary implements Summary {
179
231
  public sumValue: number = 0
180
232
  public countValue: number = 0
181
233
  public percentiles: number[]
182
234
  public tdigest = new TDigest(0.01)
183
235
  private readonly compressCount: number
236
+ private readonly calculate?: CalculateMetric
184
237
 
185
- constructor (opts: SummaryOptions) {
186
- this.percentiles = opts.percentiles ?? [0.01, 0.05, 0.5, 0.9, 0.95, 0.99, 0.999]
187
- this.compressCount = opts.compressCount ?? 1000
238
+ constructor (opts?: CalculatedSummaryOptions) {
239
+ this.percentiles = opts?.percentiles ?? [0.01, 0.05, 0.5, 0.9, 0.95, 0.99, 0.999]
240
+ this.compressCount = opts?.compressCount ?? 1000
241
+ this.calculate = opts?.calculate
242
+ }
243
+
244
+ public async collect (): Promise<{ count: number, sum: number, percentiles: Record<string, number> }> {
245
+ if (this.calculate != null) {
246
+ this.observe(await this.calculate())
247
+ }
248
+
249
+ return {
250
+ count: this.countValue,
251
+ sum: this.sumValue,
252
+ percentiles: Object.fromEntries(this.percentiles.map(p => [p, this.tdigest.percentile(p)]))
253
+ }
188
254
  }
189
255
 
190
256
  observe (value: number): void {
@@ -213,19 +279,31 @@ class DefaultSummary implements Summary {
213
279
  }
214
280
  }
215
281
 
216
- class DefaultSummaryGroup implements SummaryGroup {
217
- public summaries: Record<string, DefaultSummary> = {}
218
- private readonly opts: SummaryOptions
282
+ class SimpleSummaryGroup implements SummaryGroup {
283
+ public summaries: Record<string, SimpleSummary> = {}
284
+ private readonly opts?: CalculatedSummaryOptions
219
285
 
220
- constructor (opts: SummaryOptions) {
286
+ constructor (opts?: CalculatedSummaryOptions) {
221
287
  this.summaries = {}
222
288
  this.opts = opts
223
289
  }
224
290
 
291
+ public async collect (): Promise<Record<string, { count: number, sum: number, percentiles: Record<string, number> }>> {
292
+ return {
293
+ ...Object.fromEntries(Object.entries(this.summaries).map(([key, summary]) => {
294
+ return [key, {
295
+ count: summary.countValue,
296
+ sum: summary.sumValue,
297
+ percentiles: Object.fromEntries(summary.percentiles.map(p => [p, summary.tdigest.percentile(p)]))
298
+ }]
299
+ }))
300
+ }
301
+ }
302
+
225
303
  observe (values: Record<string, number>): void {
226
304
  for (const [key, value] of Object.entries(values)) {
227
305
  if (this.summaries[key] === undefined) {
228
- this.summaries[key] = new DefaultSummary(this.opts)
306
+ this.summaries[key] = new SimpleSummary(this.opts)
229
307
  }
230
308
 
231
309
  this.summaries[key].observe(value)
@@ -261,15 +339,21 @@ export interface SimpleMetricsInit {
261
339
  onMetrics: OnMetrics
262
340
  }
263
341
 
342
+ export interface SimpleMetricsComponents {
343
+ logger: ComponentLogger
344
+ }
345
+
264
346
  class SimpleMetrics implements Metrics, Startable {
265
- public metrics = new Map<string, DefaultMetric | DefaultGroupMetric | CalculateMetric | DefaultHistogram | DefaultHistogramGroup | DefaultSummary | DefaultSummaryGroup>()
347
+ public metrics = new Map<string, SimpleMetric | SimpleGroupMetric | SimpleHistogram | SimpleHistogramGroup | SimpleSummary | SimpleSummaryGroup>()
266
348
  private readonly transferStats: Map<string, number>
267
349
  private started: boolean
268
350
  private interval?: ReturnType<typeof setInterval>
269
351
  private readonly intervalMs: number
270
352
  private readonly onMetrics: OnMetrics
353
+ private readonly log: Logger
271
354
 
272
- constructor (components: unknown, init: SimpleMetricsInit) {
355
+ constructor (components: SimpleMetricsComponents, init: SimpleMetricsInit) {
356
+ this.log = components.logger.forComponent('libp2p:simple-metrics')
273
357
  this.started = false
274
358
 
275
359
  this._emitMetrics = this._emitMetrics.bind(this)
@@ -301,6 +385,7 @@ class SimpleMetrics implements Metrics, Startable {
301
385
  this.started = false
302
386
 
303
387
  clearInterval(this.interval)
388
+ this.transferStats.clear()
304
389
  }
305
390
 
306
391
  private _emitMetrics (): void {
@@ -308,45 +393,7 @@ class SimpleMetrics implements Metrics, Startable {
308
393
  const output: Record<string, any> = {}
309
394
 
310
395
  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
- }
396
+ output[name] = await metric.collect()
350
397
  }
351
398
 
352
399
  this.onMetrics(structuredClone(output))
@@ -407,13 +454,14 @@ class SimpleMetrics implements Metrics, Startable {
407
454
  throw new Error('Metric name is required')
408
455
  }
409
456
 
410
- if (opts?.calculate != null) {
411
- // calculated metric
412
- this.metrics.set(name, opts.calculate)
413
- return
457
+ let metric = this.metrics.get(name)
458
+
459
+ if (metric != null) {
460
+ this.log('reuse existing metric', name)
461
+ return metric
414
462
  }
415
463
 
416
- const metric = new DefaultMetric()
464
+ metric = new SimpleMetric(opts)
417
465
  this.metrics.set(name, metric)
418
466
 
419
467
  return metric
@@ -426,13 +474,14 @@ class SimpleMetrics implements Metrics, Startable {
426
474
  throw new Error('Metric name is required')
427
475
  }
428
476
 
429
- if (opts?.calculate != null) {
430
- // calculated metric
431
- this.metrics.set(name, opts.calculate)
432
- return
477
+ let metric = this.metrics.get(name)
478
+
479
+ if (metric != null) {
480
+ this.log('reuse existing metric', name)
481
+ return metric
433
482
  }
434
483
 
435
- const metric = new DefaultGroupMetric()
484
+ metric = new SimpleGroupMetric(opts)
436
485
  this.metrics.set(name, metric)
437
486
 
438
487
  return metric
@@ -445,13 +494,14 @@ class SimpleMetrics implements Metrics, Startable {
445
494
  throw new Error('Metric name is required')
446
495
  }
447
496
 
448
- if (opts?.calculate != null) {
449
- // calculated metric
450
- this.metrics.set(name, opts.calculate)
451
- return
497
+ let metric = this.metrics.get(name)
498
+
499
+ if (metric != null) {
500
+ this.log('reuse existing metric', name)
501
+ return metric
452
502
  }
453
503
 
454
- const metric = new DefaultMetric()
504
+ metric = new SimpleMetric(opts)
455
505
  this.metrics.set(name, metric)
456
506
 
457
507
  return metric
@@ -464,13 +514,14 @@ class SimpleMetrics implements Metrics, Startable {
464
514
  throw new Error('Metric name is required')
465
515
  }
466
516
 
467
- if (opts?.calculate != null) {
468
- // calculated metric
469
- this.metrics.set(name, opts.calculate)
470
- return
517
+ let metric = this.metrics.get(name)
518
+
519
+ if (metric != null) {
520
+ this.log('reuse existing metric', name)
521
+ return metric
471
522
  }
472
523
 
473
- const metric = new DefaultGroupMetric()
524
+ metric = new SimpleGroupMetric(opts)
474
525
  this.metrics.set(name, metric)
475
526
 
476
527
  return metric
@@ -483,13 +534,14 @@ class SimpleMetrics implements Metrics, Startable {
483
534
  throw new Error('Metric name is required')
484
535
  }
485
536
 
486
- if (opts?.calculate != null) {
487
- // calculated metric
488
- this.metrics.set(name, opts.calculate)
489
- return
537
+ let metric = this.metrics.get(name)
538
+
539
+ if (metric != null) {
540
+ this.log('reuse existing metric', name)
541
+ return metric
490
542
  }
491
543
 
492
- const metric = new DefaultHistogram(opts)
544
+ metric = new SimpleHistogram(opts)
493
545
  this.metrics.set(name, metric)
494
546
 
495
547
  return metric
@@ -502,13 +554,14 @@ class SimpleMetrics implements Metrics, Startable {
502
554
  throw new Error('Metric name is required')
503
555
  }
504
556
 
505
- if (opts?.calculate != null) {
506
- // calculated metric
507
- this.metrics.set(name, opts.calculate)
508
- return
557
+ let metric = this.metrics.get(name)
558
+
559
+ if (metric != null) {
560
+ this.log('reuse existing metric', name)
561
+ return metric
509
562
  }
510
563
 
511
- const metric = new DefaultHistogramGroup(opts)
564
+ metric = new SimpleHistogramGroup(opts)
512
565
  this.metrics.set(name, metric)
513
566
 
514
567
  return metric
@@ -521,13 +574,14 @@ class SimpleMetrics implements Metrics, Startable {
521
574
  throw new Error('Metric name is required')
522
575
  }
523
576
 
524
- if (opts?.calculate != null) {
525
- // calculated metric
526
- this.metrics.set(name, opts.calculate)
527
- return
577
+ let metric = this.metrics.get(name)
578
+
579
+ if (metric != null) {
580
+ this.log('reuse existing metric', name)
581
+ return metric
528
582
  }
529
583
 
530
- const metric = new DefaultSummary(opts)
584
+ metric = new SimpleSummary(opts)
531
585
  this.metrics.set(name, metric)
532
586
 
533
587
  return metric
@@ -540,13 +594,14 @@ class SimpleMetrics implements Metrics, Startable {
540
594
  throw new Error('Metric name is required')
541
595
  }
542
596
 
543
- if (opts?.calculate != null) {
544
- // calculated metric
545
- this.metrics.set(name, opts.calculate)
546
- return
597
+ let metric = this.metrics.get(name)
598
+
599
+ if (metric != null) {
600
+ this.log('reuse existing metric', name)
601
+ return metric
547
602
  }
548
603
 
549
- const metric = new DefaultSummaryGroup(opts)
604
+ metric = new SimpleSummaryGroup(opts)
550
605
  this.metrics.set(name, metric)
551
606
 
552
607
  return metric
@@ -562,6 +617,6 @@ class SimpleMetrics implements Metrics, Startable {
562
617
  }
563
618
  }
564
619
 
565
- export function simpleMetrics (init: SimpleMetricsInit): (components: unknown) => Metrics {
566
- return (components: unknown) => new SimpleMetrics(components, init)
620
+ export function simpleMetrics (init: SimpleMetricsInit): (components: SimpleMetricsComponents) => Metrics {
621
+ return (components) => new SimpleMetrics(components, init)
567
622
  }