@libp2p/prometheus-metrics 0.0.0 → 1.0.1

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/README.md CHANGED
@@ -5,11 +5,18 @@
5
5
  [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-prometheus-metrics.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-prometheus-metrics)
6
6
  [![CI](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-prometheus-metrics/test%20&%20maybe%20release/main?style=flat-square)](https://github.com/libp2p/js-libp2p-prometheus-metrics/actions/workflows/js-test-and-release.yml)
7
7
 
8
- > Collect libp2p metrics for scraping by Prometheus
8
+ > Collect libp2p metrics for scraping by Prometheus or Graphana
9
9
 
10
10
  ## Table of contents <!-- omit in toc -->
11
11
 
12
12
  - [Install](#install)
13
+ - [Usage](#usage)
14
+ - [Queries](#queries)
15
+ - [Data sent/received](#data-sentreceived)
16
+ - [CPU usage](#cpu-usage)
17
+ - [Memory usage](#memory-usage)
18
+ - [DHT query time](#dht-query-time)
19
+ - [TCP transport dialer errors](#tcp-transport-dialer-errors)
13
20
  - [License](#license)
14
21
  - [Contribute](#contribute)
15
22
 
@@ -19,6 +26,60 @@
19
26
  $ npm i @libp2p/prometheus-metrics
20
27
  ```
21
28
 
29
+ ## Usage
30
+
31
+ Configure your libp2p node with Prometheus metrics:
32
+
33
+ ```js
34
+ import { createLibp2p } from 'libp2p'
35
+ import { prometheusMetrics } from '@libp2p/prometheus-metrics'
36
+
37
+ const node = await createLibp2p({
38
+ metrics: prometheusMetrics()
39
+ })
40
+ ```
41
+
42
+ Then use the `prom-client` module to supply metrics to the Prometheus/Graphana client using your http framework:
43
+
44
+ ```js
45
+ import client from 'prom-client'
46
+
47
+ async handler (request, h) {
48
+ return h.response(await client.register.metrics())
49
+ .type(client.register.contentType)
50
+ }
51
+ ```
52
+
53
+ All Prometheus metrics are global so there's no other work required to extract them.
54
+
55
+ ### Queries
56
+
57
+ Some useful queries are:
58
+
59
+ #### Data sent/received
60
+
61
+ rate(libp2p_data_transfer_bytes_total[30s])
62
+
63
+ #### CPU usage
64
+
65
+ rate(process_cpu_user_seconds_total[30s]) * 100
66
+
67
+ #### Memory usage
68
+
69
+ nodejs_memory_usage_bytes
70
+
71
+ #### DHT query time
72
+
73
+ libp2p_kad_dht_wan_query_time_seconds
74
+
75
+ or
76
+
77
+ libp2p_kad_dht_lan_query_time_seconds
78
+
79
+ #### TCP transport dialer errors
80
+
81
+ rate(libp2p_tcp_dialer_errors_total[30s])
82
+
22
83
  ## License
23
84
 
24
85
  Licensed under either of
@@ -0,0 +1,9 @@
1
+ import type { CounterGroup, CalculatedMetricOptions } from '@libp2p/interface-metrics';
2
+ export declare class PrometheusCounterGroup implements CounterGroup {
3
+ private readonly counter;
4
+ private readonly label;
5
+ constructor(name: string, opts: CalculatedMetricOptions<Record<string, number>>);
6
+ increment(values: Record<string, number | unknown>): void;
7
+ reset(): void;
8
+ }
9
+ //# sourceMappingURL=counter-group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter-group.d.ts","sourceRoot":"","sources":["../../src/counter-group.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAmB,MAAM,2BAA2B,CAAA;AAIvG,qBAAa,sBAAuB,YAAW,YAAY;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAEjB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IA2BhF,SAAS,CAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI;IAQ1D,KAAK,IAAK,IAAI;CAGf"}
@@ -0,0 +1,36 @@
1
+ import { Counter as PromCounter } from 'prom-client';
2
+ import { normaliseString } from './utils.js';
3
+ export class PrometheusCounterGroup {
4
+ constructor(name, opts) {
5
+ name = normaliseString(name);
6
+ const help = normaliseString(opts.help ?? name);
7
+ const label = this.label = normaliseString(opts.label ?? name);
8
+ let collect;
9
+ // calculated metric
10
+ if (opts?.calculate != null) {
11
+ const calculate = opts.calculate;
12
+ collect = async function () {
13
+ const values = await calculate();
14
+ Object.entries(values).forEach(([key, value]) => {
15
+ this.inc({ [label]: key }, value);
16
+ });
17
+ };
18
+ }
19
+ this.counter = new PromCounter({
20
+ name,
21
+ help,
22
+ labelNames: [this.label],
23
+ collect
24
+ });
25
+ }
26
+ increment(values) {
27
+ Object.entries(values).forEach(([key, value]) => {
28
+ const inc = typeof value === 'number' ? value : 1;
29
+ this.counter.inc({ [this.label]: key }, inc);
30
+ });
31
+ }
32
+ reset() {
33
+ this.counter.reset();
34
+ }
35
+ }
36
+ //# sourceMappingURL=counter-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter-group.js","sourceRoot":"","sources":["../../src/counter-group.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAmB,MAAM,aAAa,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,OAAO,sBAAsB;IAIjC,YAAa,IAAY,EAAE,IAAqD;QAC9E,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAA;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAA;QAC9D,IAAI,OAAsD,CAAA;QAE1D,oBAAoB;QACpB,IAAI,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE;YAC3B,MAAM,SAAS,GAA4C,IAAI,CAAC,SAAS,CAAA;YAEzE,OAAO,GAAG,KAAK;gBACb,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;gBAEhC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;oBAC9C,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,CAAC,CAAA;gBACnC,CAAC,CAAC,CAAA;YACJ,CAAC,CAAA;SACF;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,WAAW,CAAC;YAC7B,IAAI;YACJ,IAAI;YACJ,UAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;YACxB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,CAAE,MAAwC;QACjD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC9C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAEjD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAA;QAC9C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import type { Counter, CalculatedMetricOptions } from '@libp2p/interface-metrics';
2
+ export declare class PrometheusCounter implements Counter {
3
+ private readonly counter;
4
+ constructor(name: string, opts: CalculatedMetricOptions);
5
+ increment(value?: number): void;
6
+ reset(): void;
7
+ }
8
+ //# sourceMappingURL=counter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter.d.ts","sourceRoot":"","sources":["../../src/counter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AAIjF,qBAAa,iBAAkB,YAAW,OAAO;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;gBAExB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB;IAyBxD,SAAS,CAAE,KAAK,GAAE,MAAU,GAAG,IAAI;IAInC,KAAK,IAAK,IAAI;CAGf"}
@@ -0,0 +1,31 @@
1
+ import { Counter as PromCounter } from 'prom-client';
2
+ import { normaliseString } from './utils.js';
3
+ export class PrometheusCounter {
4
+ constructor(name, opts) {
5
+ name = normaliseString(name);
6
+ const help = normaliseString(opts.help ?? name);
7
+ const labels = opts.label != null ? [normaliseString(opts.label)] : [];
8
+ let collect;
9
+ // calculated metric
10
+ if (opts?.calculate != null) {
11
+ const calculate = opts.calculate;
12
+ collect = async function () {
13
+ const value = await calculate();
14
+ this.inc(value);
15
+ };
16
+ }
17
+ this.counter = new PromCounter({
18
+ name,
19
+ help,
20
+ labelNames: labels,
21
+ collect
22
+ });
23
+ }
24
+ increment(value = 1) {
25
+ this.counter.inc(value);
26
+ }
27
+ reset() {
28
+ this.counter.reset();
29
+ }
30
+ }
31
+ //# sourceMappingURL=counter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter.js","sourceRoot":"","sources":["../../src/counter.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,OAAO,IAAI,WAAW,EAAE,MAAM,aAAa,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,OAAO,iBAAiB;IAG5B,YAAa,IAAY,EAAE,IAA6B;QACtD,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAA;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACtE,IAAI,OAAsD,CAAA;QAE1D,oBAAoB;QACpB,IAAI,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;YAEhC,OAAO,GAAG,KAAK;gBACb,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAA;gBAE/B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACjB,CAAC,CAAA;SACF;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,WAAW,CAAC;YAC7B,IAAI;YACJ,IAAI;YACJ,UAAU,EAAE,MAAM;YAClB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,CAAE,QAAgB,CAAC;QAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;CACF"}
@@ -1,10 +1,21 @@
1
1
  import type { Metrics } from '@libp2p/interface-metrics';
2
2
  import { DefaultMetricsCollectorConfiguration } from 'prom-client';
3
- import { DefaultMetricsInit } from '@libp2p/metrics';
4
- export interface PrometheusMetricsInit extends DefaultMetricsInit {
3
+ export interface PrometheusMetricsInit {
4
+ /**
5
+ * By default we collect default metrics - CPU, memory etc, to not do
6
+ * this, pass true here
7
+ */
8
+ collectDefaultMetrics?: boolean;
9
+ /**
10
+ * prom-client options to pass to the `collectDefaultMetrics` function
11
+ */
5
12
  defaultMetrics?: DefaultMetricsCollectorConfiguration;
13
+ /**
14
+ * All metrics in prometheus are global so to prevent clashes in naming
15
+ * we reset the global metrics registry on creation - to not do this,
16
+ * pass true here
17
+ */
6
18
  preserveExistingMetrics?: boolean;
7
- collectDefaultMetrics?: boolean;
8
19
  }
9
20
  export declare function prometheusMetrics(init?: Partial<PrometheusMetricsInit>): () => Metrics;
10
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgF,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAEtI,OAAO,EAAiD,oCAAoC,EAAY,MAAM,aAAa,CAAA;AAG3H,OAAO,EAAkB,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEpE,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,cAAc,CAAC,EAAE,oCAAoC,CAAA;IACrD,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AAuHD,wBAAgB,iBAAiB,CAAE,IAAI,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,MAAM,OAAO,CAIvF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsF,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAC5I,OAAO,EAAyB,oCAAoC,EAAY,MAAM,aAAa,CAAA;AAYnG,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAE/B;;OAEG;IACH,cAAc,CAAC,EAAE,oCAAoC,CAAA;IAErD;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC;AAwJD,wBAAgB,iBAAiB,CAAE,IAAI,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,MAAM,OAAO,CAIvF"}
package/dist/src/index.js CHANGED
@@ -1,38 +1,37 @@
1
- import { Gauge, collectDefaultMetrics, register } from 'prom-client';
1
+ import { collectDefaultMetrics, register } from 'prom-client';
2
+ import each from 'it-foreach';
2
3
  import { PrometheusMetric } from './metric.js';
3
4
  import { PrometheusMetricGroup } from './metric-group.js';
4
- import { DefaultMetrics } from '@libp2p/metrics';
5
- class PrometheusMetrics extends DefaultMetrics {
5
+ import { PrometheusCounter } from './counter.js';
6
+ import { PrometheusCounterGroup } from './counter-group.js';
7
+ import { logger } from '@libp2p/logger';
8
+ const log = logger('libp2p:prometheus-metrics');
9
+ class PrometheusMetrics {
6
10
  constructor(init) {
7
- super(init);
8
11
  if (init?.preserveExistingMetrics !== true) {
9
- // all metrics in prometheus are global so it's necessary to remove
10
- // existing metrics to make sure we don't error when setting up metrics
12
+ log('Clearing existing metrics');
11
13
  register.clear();
12
14
  }
13
15
  if (init?.preserveExistingMetrics !== false) {
14
- // collect memory/CPU and node-specific default metrics
16
+ log('Collecting default metrics');
15
17
  collectDefaultMetrics(init?.defaultMetrics);
16
18
  }
17
- this.registerMetricGroup('libp2p_data_transfer_bytes', {
19
+ // holds global and per-protocol sent/received stats
20
+ this.transferStats = new Map();
21
+ log('Collecting data transfer metrics');
22
+ this.registerCounterGroup('libp2p_data_transfer_bytes_total', {
18
23
  label: 'protocol',
19
24
  calculate: () => {
20
25
  const output = {};
21
- const global = this.getGlobal().getSnapshot();
22
- output['global sent'] = Number(global.dataSent);
23
- output['global received'] = Number(global.dataReceived);
24
- for (const protocol of this.getProtocols()) {
25
- const stats = this.forProtocol(protocol);
26
- if (stats == null) {
27
- continue;
28
- }
29
- const snapshot = stats.getSnapshot();
30
- output[`${protocol} sent`] = Number(snapshot.dataSent);
31
- output[`${protocol} received`] = Number(snapshot.dataReceived);
26
+ for (const [key, value] of this.transferStats.entries()) {
27
+ output[key] = value;
32
28
  }
29
+ // reset counts for next time
30
+ this.transferStats = new Map();
33
31
  return output;
34
32
  }
35
33
  });
34
+ log('Collecting memory metrics');
36
35
  this.registerMetricGroup('nodejs_memory_usage_bytes', {
37
36
  label: 'memory',
38
37
  calculate: () => {
@@ -42,53 +41,81 @@ class PrometheusMetrics extends DefaultMetrics {
42
41
  }
43
42
  });
44
43
  }
45
- registerMetric(name, opts) {
44
+ /**
45
+ * Increment the transfer stat for the passed key, making sure
46
+ * it exists first
47
+ */
48
+ _incrementValue(key, value) {
49
+ const existing = this.transferStats.get(key) ?? 0;
50
+ this.transferStats.set(key, existing + value);
51
+ }
52
+ /**
53
+ * Override the sink/source of the stream to count the bytes
54
+ * in and out
55
+ */
56
+ _track(stream, name) {
57
+ const self = this;
58
+ const sink = stream.sink;
59
+ stream.sink = async function trackedSink(source) {
60
+ await sink(each(source, buf => {
61
+ self._incrementValue(`${name} sent`, buf.byteLength);
62
+ }));
63
+ };
64
+ const source = stream.source;
65
+ stream.source = each(source, buf => {
66
+ self._incrementValue(`${name} received`, buf.byteLength);
67
+ });
68
+ }
69
+ trackMultiaddrConnection(maConn) {
70
+ this._track(maConn, 'global');
71
+ }
72
+ trackProtocolStream(stream, connection) {
73
+ if (stream.stat.protocol == null) {
74
+ // protocol not negotiated yet, should not happen as the upgrader
75
+ // calls this handler after protocol negotiation
76
+ return;
77
+ }
78
+ this._track(stream, stream.stat.protocol);
79
+ }
80
+ registerMetric(name, opts = {}) {
46
81
  if (name == null ?? name.trim() === '') {
47
82
  throw new Error('Metric name is required');
48
83
  }
49
- if (opts?.calculate != null) {
50
- const calculate = opts.calculate;
51
- // calculated metric
52
- const collect = async function () {
53
- const value = await calculate();
54
- this.set(value);
55
- };
56
- // prom-client metrics are global
57
- new Gauge({
58
- name,
59
- help: opts.help ?? name,
60
- labelNames: [opts.label ?? name],
61
- collect
62
- });
63
- return;
84
+ log('Register metric', name);
85
+ const metric = new PrometheusMetric(name, opts ?? {});
86
+ if (opts.calculate == null) {
87
+ return metric;
64
88
  }
65
- return new PrometheusMetric(name, opts ?? {});
66
89
  }
67
- registerMetricGroup(name, opts) {
90
+ registerMetricGroup(name, opts = {}) {
68
91
  if (name == null ?? name.trim() === '') {
69
92
  throw new Error('Metric name is required');
70
93
  }
71
- if (opts?.calculate != null) {
72
- // calculated metric
73
- const calculate = opts.calculate;
74
- const label = opts.label ?? name;
75
- // calculated metric
76
- const collect = async function () {
77
- const values = await calculate();
78
- Object.entries(values).forEach(([key, value]) => {
79
- this.set({ [label]: key }, value);
80
- });
81
- };
82
- // prom-client metrics are global
83
- new Gauge({
84
- name,
85
- help: opts.help ?? name,
86
- labelNames: [opts.label ?? name],
87
- collect
88
- });
89
- return;
94
+ log('Register metric group', name);
95
+ const group = new PrometheusMetricGroup(name, opts ?? {});
96
+ if (opts.calculate == null) {
97
+ return group;
98
+ }
99
+ }
100
+ registerCounter(name, opts = {}) {
101
+ if (name == null ?? name.trim() === '') {
102
+ throw new Error('Counter name is required');
103
+ }
104
+ log('Register counter', name);
105
+ const counter = new PrometheusCounter(name, opts);
106
+ if (opts.calculate == null) {
107
+ return counter;
108
+ }
109
+ }
110
+ registerCounterGroup(name, opts = {}) {
111
+ if (name == null ?? name.trim() === '') {
112
+ throw new Error('Metric name is required');
113
+ }
114
+ log('Register counter group', name);
115
+ const group = new PrometheusCounterGroup(name, opts);
116
+ if (opts.calculate == null) {
117
+ return group;
90
118
  }
91
- return new PrometheusMetricGroup(name, opts ?? {});
92
119
  }
93
120
  }
94
121
  export function prometheusMetrics(init) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import type { MetricOptions, MetricGroup, StopTimer } from '@libp2p/interface-metrics';
1
+ import type { CalculatedMetricOptions, MetricGroup, StopTimer } from '@libp2p/interface-metrics';
2
2
  export declare class PrometheusMetricGroup implements MetricGroup {
3
3
  private readonly gauge;
4
4
  private readonly label;
5
- constructor(name: string, opts: MetricOptions);
5
+ constructor(name: string, opts: CalculatedMetricOptions<Record<string, number>>);
6
6
  update(values: Record<string, number>): void;
7
7
  increment(values: Record<string, number | unknown>): void;
8
8
  decrement(values: Record<string, number | unknown>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"metric-group.d.ts","sourceRoot":"","sources":["../../src/metric-group.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAItF,qBAAa,qBAAsB,YAAW,WAAW;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAEjB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa;IAY9C,MAAM,CAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAM7C,SAAS,CAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI;IAQ1D,SAAS,CAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI;IAQ1D,KAAK,IAAK,IAAI;IAId,KAAK,CAAE,GAAG,EAAE,MAAM,GAAG,SAAS;CAK/B"}
1
+ {"version":3,"file":"metric-group.d.ts","sourceRoot":"","sources":["../../src/metric-group.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAmB,WAAW,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAIjH,qBAAa,qBAAsB,YAAW,WAAW;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAEjB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IA2BhF,MAAM,CAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAM7C,SAAS,CAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI;IAQ1D,SAAS,CAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI;IAQ1D,KAAK,IAAK,IAAI;IAId,KAAK,CAAE,GAAG,EAAE,MAAM,GAAG,SAAS;CAK/B"}
@@ -4,11 +4,23 @@ export class PrometheusMetricGroup {
4
4
  constructor(name, opts) {
5
5
  name = normaliseString(name);
6
6
  const help = normaliseString(opts.help ?? name);
7
- this.label = normaliseString(opts.label ?? name);
7
+ const label = this.label = normaliseString(opts.label ?? name);
8
+ let collect;
9
+ // calculated metric
10
+ if (opts?.calculate != null) {
11
+ const calculate = opts.calculate;
12
+ collect = async function () {
13
+ const values = await calculate();
14
+ Object.entries(values).forEach(([key, value]) => {
15
+ this.set({ [label]: key }, value);
16
+ });
17
+ };
18
+ }
8
19
  this.gauge = new Gauge({
9
20
  name,
10
21
  help,
11
- labelNames: [this.label]
22
+ labelNames: [this.label],
23
+ collect
12
24
  });
13
25
  }
14
26
  update(values) {
@@ -1 +1 @@
1
- {"version":3,"file":"metric-group.js","sourceRoot":"","sources":["../../src/metric-group.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,OAAO,qBAAqB;IAIhC,YAAa,IAAY,EAAE,IAAmB;QAC5C,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAA;QAC/C,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAA;QAEhD,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC;YACrB,IAAI;YACJ,IAAI;YACJ,UAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAE,MAA8B;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC9C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,CAAC,CAAA;QAC9C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,CAAE,MAAwC;QACjD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC9C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAEjD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAA;QAC5C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,CAAE,MAAwC;QACjD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC9C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAEjD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAA;QAC5C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAED,KAAK,CAAE,GAAW;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;YAC3B,GAAG,EAAE,CAAC;SACP,CAAC,CAAA;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"metric-group.js","sourceRoot":"","sources":["../../src/metric-group.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,OAAO,qBAAqB;IAIhC,YAAa,IAAY,EAAE,IAAqD;QAC9E,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAA;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAA;QAC9D,IAAI,OAAgD,CAAA;QAEpD,oBAAoB;QACpB,IAAI,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE;YAC3B,MAAM,SAAS,GAA4C,IAAI,CAAC,SAAS,CAAA;YAEzE,OAAO,GAAG,KAAK;gBACb,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;gBAEhC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;oBAC9C,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,CAAC,CAAA;gBACnC,CAAC,CAAC,CAAA;YACJ,CAAC,CAAA;SACF;QAED,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC;YACrB,IAAI;YACJ,IAAI;YACJ,UAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;YACxB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAE,MAA8B;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC9C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,CAAC,CAAA;QAC9C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,CAAE,MAAwC;QACjD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC9C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAEjD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAA;QAC5C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,CAAE,MAAwC;QACjD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC9C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAEjD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAA;QAC5C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAED,KAAK,CAAE,GAAW;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;YAC3B,GAAG,EAAE,CAAC;SACP,CAAC,CAAA;IACJ,CAAC;CACF"}
@@ -1,7 +1,7 @@
1
- import type { Metric, MetricOptions, StopTimer } from '@libp2p/interface-metrics';
1
+ import type { Metric, CalculatedMetricOptions, StopTimer } from '@libp2p/interface-metrics';
2
2
  export declare class PrometheusMetric implements Metric {
3
3
  private readonly gauge;
4
- constructor(name: string, opts: MetricOptions);
4
+ constructor(name: string, opts: CalculatedMetricOptions);
5
5
  update(value: number): void;
6
6
  increment(value?: number): void;
7
7
  decrement(value?: number): void;
@@ -1 +1 @@
1
- {"version":3,"file":"metric.d.ts","sourceRoot":"","sources":["../../src/metric.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAIjF,qBAAa,gBAAiB,YAAW,MAAM;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAO;gBAEhB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa;IAY9C,MAAM,CAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI5B,SAAS,CAAE,KAAK,SAAI,GAAG,IAAI;IAI3B,SAAS,CAAE,KAAK,SAAI,GAAG,IAAI;IAI3B,KAAK,IAAK,IAAI;IAId,KAAK,IAAK,SAAS;CAGpB"}
1
+ {"version":3,"file":"metric.d.ts","sourceRoot":"","sources":["../../src/metric.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAI3F,qBAAa,gBAAiB,YAAW,MAAM;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAO;gBAEhB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB;IAyBxD,MAAM,CAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI5B,SAAS,CAAE,KAAK,GAAE,MAAU,GAAG,IAAI;IAInC,SAAS,CAAE,KAAK,GAAE,MAAU,GAAG,IAAI;IAInC,KAAK,IAAK,IAAI;IAId,KAAK,IAAK,SAAS;CAGpB"}
@@ -4,11 +4,21 @@ export class PrometheusMetric {
4
4
  constructor(name, opts) {
5
5
  name = normaliseString(name);
6
6
  const help = normaliseString(opts.help ?? name);
7
- const label = opts.label != null ? normaliseString(opts.label) : undefined;
7
+ const labels = opts.label != null ? [normaliseString(opts.label)] : [];
8
+ let collect;
9
+ // calculated metric
10
+ if (opts?.calculate != null) {
11
+ const calculate = opts.calculate;
12
+ collect = async function () {
13
+ const value = await calculate();
14
+ this.set(value);
15
+ };
16
+ }
8
17
  this.gauge = new Gauge({
9
18
  name,
10
19
  help,
11
- labelNames: label != null ? [label] : []
20
+ labelNames: labels,
21
+ collect
12
22
  });
13
23
  }
14
24
  update(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"metric.js","sourceRoot":"","sources":["../../src/metric.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,OAAO,gBAAgB;IAG3B,YAAa,IAAY,EAAE,IAAmB;QAC5C,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAA;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAE1E,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC;YACrB,IAAI;YACJ,IAAI;YACJ,UAAU,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;SACzC,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAE,KAAa;QACnB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAED,SAAS,CAAE,KAAK,GAAG,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAED,SAAS,CAAE,KAAK,GAAG,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAA;IAChC,CAAC;CACF"}
1
+ {"version":3,"file":"metric.js","sourceRoot":"","sources":["../../src/metric.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,OAAO,gBAAgB;IAG3B,YAAa,IAAY,EAAE,IAA6B;QACtD,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAA;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACtE,IAAI,OAAgD,CAAA;QAEpD,oBAAoB;QACpB,IAAI,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;YAEhC,OAAO,GAAG,KAAK;gBACb,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAA;gBAE/B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACjB,CAAC,CAAA;SACF;QAED,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC;YACrB,IAAI;YACJ,IAAI;YACJ,UAAU,EAAE,MAAM;YAClB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAE,KAAa;QACnB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAED,SAAS,CAAE,QAAgB,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAED,SAAS,CAAE,QAAgB,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAA;IAChC,CAAC;CACF"}
@@ -1,4 +1,8 @@
1
1
  export declare const ONE_SECOND = 1000;
2
2
  export declare const ONE_MINUTE: number;
3
+ /**
4
+ * See https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
5
+ * for rules on valid naming
6
+ */
3
7
  export declare function normaliseString(str: string): string;
4
8
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,UAAU,OAAO,CAAA;AAC9B,eAAO,MAAM,UAAU,QAAkB,CAAA;AAEzC,wBAAgB,eAAe,CAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,UAAU,OAAO,CAAA;AAC9B,eAAO,MAAM,UAAU,QAAkB,CAAA;AAEzC;;;GAGG;AACH,wBAAgB,eAAe,CAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIpD"}
package/dist/src/utils.js CHANGED
@@ -1,6 +1,12 @@
1
1
  export const ONE_SECOND = 1000;
2
2
  export const ONE_MINUTE = 60 * ONE_SECOND;
3
+ /**
4
+ * See https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
5
+ * for rules on valid naming
6
+ */
3
7
  export function normaliseString(str) {
4
- return str.replace(/-/g, '_');
8
+ return str
9
+ .replace(/[^a-zA-Z0-9_]/g, '_')
10
+ .replace(/_+/g, '_');
5
11
  }
6
12
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAA;AAC9B,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,GAAG,UAAU,CAAA;AAEzC,MAAM,UAAU,eAAe,CAAE,GAAW;IAC1C,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AAC/B,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAA;AAC9B,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,GAAG,UAAU,CAAA;AAEzC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAE,GAAW;IAC1C,OAAO,GAAG;SACP,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC;SAC9B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;AACxB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@libp2p/prometheus-metrics",
3
- "version": "0.0.0",
4
- "description": "Collect libp2p metrics for scraping by Prometheus",
3
+ "version": "1.0.1",
4
+ "description": "Collect libp2p metrics for scraping by Prometheus or Graphana",
5
5
  "author": "",
6
6
  "license": "Apache-2.0 OR MIT",
7
7
  "homepage": "https://github.com/libp2p/js-libp2p-prometheus-metrics#readme",
@@ -132,24 +132,25 @@
132
132
  "release": "aegir release"
133
133
  },
134
134
  "dependencies": {
135
- "@libp2p/interface-metrics": "^3.0.0",
136
- "@libp2p/metrics": "^0.0.0",
137
- "it-drain": "^2.0.0",
138
- "it-pipe": "^2.0.4"
139
- },
140
- "peerDependencies": {
141
- "prom-client": "^14.1.0"
135
+ "@libp2p/interface-connection": "^3.0.2",
136
+ "@libp2p/interface-metrics": "^4.0.2",
137
+ "@libp2p/logger": "^2.0.2",
138
+ "it-foreach": "^1.0.0",
139
+ "it-stream-types": "^1.0.4"
142
140
  },
143
141
  "devDependencies": {
144
- "@libp2p/interface-connection": "^3.0.2",
145
142
  "@libp2p/interface-mocks": "^7.0.3",
146
143
  "@libp2p/peer-id-factory": "^1.0.19",
147
144
  "@multiformats/multiaddr": "^11.0.7",
148
145
  "aegir": "^37.5.6",
146
+ "it-drain": "^2.0.0",
149
147
  "it-pair": "^2.0.3",
150
- "it-stream-types": "^1.0.4",
148
+ "it-pipe": "^2.0.4",
151
149
  "p-defer": "^4.0.0",
152
150
  "prom-client": "^14.1.0",
153
151
  "uint8arraylist": "^2.3.3"
152
+ },
153
+ "peerDependencies": {
154
+ "prom-client": "^14.1.0"
154
155
  }
155
156
  }
@@ -0,0 +1,47 @@
1
+ import type { CounterGroup, CalculatedMetricOptions, CalculateMetric } from '@libp2p/interface-metrics'
2
+ import { Counter as PromCounter, CollectFunction } from 'prom-client'
3
+ import { normaliseString } from './utils.js'
4
+
5
+ export class PrometheusCounterGroup implements CounterGroup {
6
+ private readonly counter: PromCounter
7
+ private readonly label: string
8
+
9
+ constructor (name: string, opts: CalculatedMetricOptions<Record<string, number>>) {
10
+ name = normaliseString(name)
11
+ const help = normaliseString(opts.help ?? name)
12
+ const label = this.label = normaliseString(opts.label ?? name)
13
+ let collect: CollectFunction<PromCounter<any>> | undefined
14
+
15
+ // calculated metric
16
+ if (opts?.calculate != null) {
17
+ const calculate: CalculateMetric<Record<string, number>> = opts.calculate
18
+
19
+ collect = async function () {
20
+ const values = await calculate()
21
+
22
+ Object.entries(values).forEach(([key, value]) => {
23
+ this.inc({ [label]: key }, value)
24
+ })
25
+ }
26
+ }
27
+
28
+ this.counter = new PromCounter({
29
+ name,
30
+ help,
31
+ labelNames: [this.label],
32
+ collect
33
+ })
34
+ }
35
+
36
+ increment (values: Record<string, number | unknown>): void {
37
+ Object.entries(values).forEach(([key, value]) => {
38
+ const inc = typeof value === 'number' ? value : 1
39
+
40
+ this.counter.inc({ [this.label]: key }, inc)
41
+ })
42
+ }
43
+
44
+ reset (): void {
45
+ this.counter.reset()
46
+ }
47
+ }
package/src/counter.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type { Counter, CalculatedMetricOptions } from '@libp2p/interface-metrics'
2
+ import { CollectFunction, Counter as PromCounter } from 'prom-client'
3
+ import { normaliseString } from './utils.js'
4
+
5
+ export class PrometheusCounter implements Counter {
6
+ private readonly counter: PromCounter
7
+
8
+ constructor (name: string, opts: CalculatedMetricOptions) {
9
+ name = normaliseString(name)
10
+ const help = normaliseString(opts.help ?? name)
11
+ const labels = opts.label != null ? [normaliseString(opts.label)] : []
12
+ let collect: CollectFunction<PromCounter<any>> | undefined
13
+
14
+ // calculated metric
15
+ if (opts?.calculate != null) {
16
+ const calculate = opts.calculate
17
+
18
+ collect = async function () {
19
+ const value = await calculate()
20
+
21
+ this.inc(value)
22
+ }
23
+ }
24
+
25
+ this.counter = new PromCounter({
26
+ name,
27
+ help,
28
+ labelNames: labels,
29
+ collect
30
+ })
31
+ }
32
+
33
+ increment (value: number = 1): void {
34
+ this.counter.inc(value)
35
+ }
36
+
37
+ reset (): void {
38
+ this.counter.reset()
39
+ }
40
+ }
package/src/index.ts CHANGED
@@ -1,56 +1,71 @@
1
- import type { CalculatedMetricOptions, CalculateMetric, Metric, MetricGroup, MetricOptions, Metrics } from '@libp2p/interface-metrics'
2
- import type { Startable } from '@libp2p/interfaces/startable'
3
- import { Gauge, CollectFunction, collectDefaultMetrics, DefaultMetricsCollectorConfiguration, register } from 'prom-client'
1
+ import type { CalculatedMetricOptions, Counter, CounterGroup, Metric, MetricGroup, MetricOptions, Metrics } from '@libp2p/interface-metrics'
2
+ import { collectDefaultMetrics, DefaultMetricsCollectorConfiguration, register } from 'prom-client'
3
+ import type { MultiaddrConnection, Stream, Connection } from '@libp2p/interface-connection'
4
+ import type { Duplex } from 'it-stream-types'
5
+ import each from 'it-foreach'
4
6
  import { PrometheusMetric } from './metric.js'
5
7
  import { PrometheusMetricGroup } from './metric-group.js'
6
- import { DefaultMetrics, DefaultMetricsInit } from '@libp2p/metrics'
8
+ import { PrometheusCounter } from './counter.js'
9
+ import { PrometheusCounterGroup } from './counter-group.js'
10
+ import { logger } from '@libp2p/logger'
7
11
 
8
- export interface PrometheusMetricsInit extends DefaultMetricsInit {
12
+ const log = logger('libp2p:prometheus-metrics')
13
+
14
+ export interface PrometheusMetricsInit {
15
+ /**
16
+ * By default we collect default metrics - CPU, memory etc, to not do
17
+ * this, pass true here
18
+ */
19
+ collectDefaultMetrics?: boolean
20
+
21
+ /**
22
+ * prom-client options to pass to the `collectDefaultMetrics` function
23
+ */
9
24
  defaultMetrics?: DefaultMetricsCollectorConfiguration
25
+
26
+ /**
27
+ * All metrics in prometheus are global so to prevent clashes in naming
28
+ * we reset the global metrics registry on creation - to not do this,
29
+ * pass true here
30
+ */
10
31
  preserveExistingMetrics?: boolean
11
- collectDefaultMetrics?: boolean
12
32
  }
13
33
 
14
- class PrometheusMetrics extends DefaultMetrics implements Metrics, Startable {
15
- constructor (init?: Partial<PrometheusMetricsInit>) {
16
- super(init)
34
+ class PrometheusMetrics implements Metrics {
35
+ private transferStats: Map<string, number>
17
36
 
37
+ constructor (init?: Partial<PrometheusMetricsInit>) {
18
38
  if (init?.preserveExistingMetrics !== true) {
19
- // all metrics in prometheus are global so it's necessary to remove
20
- // existing metrics to make sure we don't error when setting up metrics
39
+ log('Clearing existing metrics')
21
40
  register.clear()
22
41
  }
23
42
 
24
43
  if (init?.preserveExistingMetrics !== false) {
25
- // collect memory/CPU and node-specific default metrics
44
+ log('Collecting default metrics')
26
45
  collectDefaultMetrics(init?.defaultMetrics)
27
46
  }
28
47
 
29
- this.registerMetricGroup('libp2p_data_transfer_bytes', {
48
+ // holds global and per-protocol sent/received stats
49
+ this.transferStats = new Map()
50
+
51
+ log('Collecting data transfer metrics')
52
+ this.registerCounterGroup('libp2p_data_transfer_bytes_total', {
30
53
  label: 'protocol',
31
54
  calculate: () => {
32
55
  const output: Record<string, number> = {}
33
56
 
34
- const global = this.getGlobal().getSnapshot()
35
- output['global sent'] = Number(global.dataSent)
36
- output['global received'] = Number(global.dataReceived)
37
-
38
- for (const protocol of this.getProtocols()) {
39
- const stats = this.forProtocol(protocol)
40
-
41
- if (stats == null) {
42
- continue
43
- }
44
-
45
- const snapshot = stats.getSnapshot()
46
- output[`${protocol} sent`] = Number(snapshot.dataSent)
47
- output[`${protocol} received`] = Number(snapshot.dataReceived)
57
+ for (const [key, value] of this.transferStats.entries()) {
58
+ output[key] = value
48
59
  }
49
60
 
61
+ // reset counts for next time
62
+ this.transferStats = new Map()
63
+
50
64
  return output
51
65
  }
52
66
  })
53
67
 
68
+ log('Collecting memory metrics')
54
69
  this.registerMetricGroup('nodejs_memory_usage_bytes', {
55
70
  label: 'memory',
56
71
  calculate: () => {
@@ -61,70 +76,108 @@ class PrometheusMetrics extends DefaultMetrics implements Metrics, Startable {
61
76
  })
62
77
  }
63
78
 
64
- registerMetric (name: string, opts: CalculatedMetricOptions): void
65
- registerMetric (name: string, opts?: MetricOptions): Metric
66
- registerMetric (name: string, opts: any): any {
67
- if (name == null ?? name.trim() === '') {
68
- throw new Error('Metric name is required')
69
- }
79
+ /**
80
+ * Increment the transfer stat for the passed key, making sure
81
+ * it exists first
82
+ */
83
+ _incrementValue (key: string, value: number) {
84
+ const existing = this.transferStats.get(key) ?? 0
70
85
 
71
- if (opts?.calculate != null) {
72
- const calculate = opts.calculate
86
+ this.transferStats.set(key, existing + value)
87
+ }
73
88
 
74
- // calculated metric
75
- const collect: CollectFunction<Gauge<any>> = async function () {
76
- const value = await calculate()
89
+ /**
90
+ * Override the sink/source of the stream to count the bytes
91
+ * in and out
92
+ */
93
+ _track (stream: Duplex<any>, name: string) {
94
+ const self = this
95
+
96
+ const sink = stream.sink
97
+ stream.sink = async function trackedSink (source) {
98
+ await sink(each(source, buf => {
99
+ self._incrementValue(`${name} sent`, buf.byteLength)
100
+ }))
101
+ }
77
102
 
78
- this.set(value)
79
- }
103
+ const source = stream.source
104
+ stream.source = each(source, buf => {
105
+ self._incrementValue(`${name} received`, buf.byteLength)
106
+ })
107
+ }
80
108
 
81
- // prom-client metrics are global
82
- new Gauge({ // eslint-disable-line no-new
83
- name,
84
- help: opts.help ?? name,
85
- labelNames: [opts.label ?? name],
86
- collect
87
- })
109
+ trackMultiaddrConnection (maConn: MultiaddrConnection): void {
110
+ this._track(maConn, 'global')
111
+ }
88
112
 
113
+ trackProtocolStream (stream: Stream, connection: Connection): void {
114
+ if (stream.stat.protocol == null) {
115
+ // protocol not negotiated yet, should not happen as the upgrader
116
+ // calls this handler after protocol negotiation
89
117
  return
90
118
  }
91
119
 
92
- return new PrometheusMetric(name, opts ?? {})
120
+ this._track(stream, stream.stat.protocol)
121
+ }
122
+
123
+ registerMetric (name: string, opts: CalculatedMetricOptions): void
124
+ registerMetric (name: string, opts?: MetricOptions): Metric
125
+ registerMetric (name: string, opts: any = {}): any {
126
+ if (name == null ?? name.trim() === '') {
127
+ throw new Error('Metric name is required')
128
+ }
129
+
130
+ log('Register metric', name)
131
+ const metric = new PrometheusMetric(name, opts ?? {})
132
+
133
+ if (opts.calculate == null) {
134
+ return metric
135
+ }
93
136
  }
94
137
 
95
138
  registerMetricGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
96
139
  registerMetricGroup (name: string, opts?: MetricOptions): MetricGroup
97
- registerMetricGroup (name: string, opts: any): any {
140
+ registerMetricGroup (name: string, opts: any = {}): any {
98
141
  if (name == null ?? name.trim() === '') {
99
142
  throw new Error('Metric name is required')
100
143
  }
101
144
 
102
- if (opts?.calculate != null) {
103
- // calculated metric
104
- const calculate: CalculateMetric<Record<string, number>> = opts.calculate
105
- const label = opts.label ?? name
145
+ log('Register metric group', name)
146
+ const group = new PrometheusMetricGroup(name, opts ?? {})
106
147
 
107
- // calculated metric
108
- const collect: CollectFunction<Gauge<any>> = async function () {
109
- const values = await calculate()
148
+ if (opts.calculate == null) {
149
+ return group
150
+ }
151
+ }
110
152
 
111
- Object.entries(values).forEach(([key, value]) => {
112
- this.set({ [label]: key }, value)
113
- })
114
- }
153
+ registerCounter (name: string, opts: CalculatedMetricOptions): void
154
+ registerCounter (name: string, opts?: MetricOptions): Counter
155
+ registerCounter (name: string, opts: any = {}): any {
156
+ if (name == null ?? name.trim() === '') {
157
+ throw new Error('Counter name is required')
158
+ }
115
159
 
116
- // prom-client metrics are global
117
- new Gauge({ // eslint-disable-line no-new
118
- name,
119
- help: opts.help ?? name,
120
- labelNames: [opts.label ?? name],
121
- collect
122
- })
160
+ log('Register counter', name)
161
+ const counter = new PrometheusCounter(name, opts)
123
162
 
124
- return
163
+ if (opts.calculate == null) {
164
+ return counter
125
165
  }
166
+ }
126
167
 
127
- return new PrometheusMetricGroup(name, opts ?? {})
168
+ registerCounterGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
169
+ registerCounterGroup (name: string, opts?: MetricOptions): CounterGroup
170
+ registerCounterGroup (name: string, opts: any = {}): any {
171
+ if (name == null ?? name.trim() === '') {
172
+ throw new Error('Metric name is required')
173
+ }
174
+
175
+ log('Register counter group', name)
176
+ const group = new PrometheusCounterGroup(name, opts)
177
+
178
+ if (opts.calculate == null) {
179
+ return group
180
+ }
128
181
  }
129
182
  }
130
183
 
@@ -1,20 +1,35 @@
1
- import type { MetricOptions, MetricGroup, StopTimer } from '@libp2p/interface-metrics'
2
- import { Gauge } from 'prom-client'
1
+ import type { CalculatedMetricOptions, CalculateMetric, MetricGroup, StopTimer } from '@libp2p/interface-metrics'
2
+ import { CollectFunction, Gauge } from 'prom-client'
3
3
  import { normaliseString } from './utils.js'
4
4
 
5
5
  export class PrometheusMetricGroup implements MetricGroup {
6
6
  private readonly gauge: Gauge
7
7
  private readonly label: string
8
8
 
9
- constructor (name: string, opts: MetricOptions) {
9
+ constructor (name: string, opts: CalculatedMetricOptions<Record<string, number>>) {
10
10
  name = normaliseString(name)
11
11
  const help = normaliseString(opts.help ?? name)
12
- this.label = normaliseString(opts.label ?? name)
12
+ const label = this.label = normaliseString(opts.label ?? name)
13
+ let collect: CollectFunction<Gauge<any>> | undefined
14
+
15
+ // calculated metric
16
+ if (opts?.calculate != null) {
17
+ const calculate: CalculateMetric<Record<string, number>> = opts.calculate
18
+
19
+ collect = async function () {
20
+ const values = await calculate()
21
+
22
+ Object.entries(values).forEach(([key, value]) => {
23
+ this.set({ [label]: key }, value)
24
+ })
25
+ }
26
+ }
13
27
 
14
28
  this.gauge = new Gauge({
15
29
  name,
16
30
  help,
17
- labelNames: [this.label]
31
+ labelNames: [this.label],
32
+ collect
18
33
  })
19
34
  }
20
35
 
package/src/metric.ts CHANGED
@@ -1,19 +1,32 @@
1
- import type { Metric, MetricOptions, StopTimer } from '@libp2p/interface-metrics'
2
- import { Gauge } from 'prom-client'
1
+ import type { Metric, CalculatedMetricOptions, StopTimer } from '@libp2p/interface-metrics'
2
+ import { CollectFunction, Gauge } from 'prom-client'
3
3
  import { normaliseString } from './utils.js'
4
4
 
5
5
  export class PrometheusMetric implements Metric {
6
6
  private readonly gauge: Gauge
7
7
 
8
- constructor (name: string, opts: MetricOptions) {
8
+ constructor (name: string, opts: CalculatedMetricOptions) {
9
9
  name = normaliseString(name)
10
10
  const help = normaliseString(opts.help ?? name)
11
- const label = opts.label != null ? normaliseString(opts.label) : undefined
11
+ const labels = opts.label != null ? [normaliseString(opts.label)] : []
12
+ let collect: CollectFunction<Gauge<any>> | undefined
13
+
14
+ // calculated metric
15
+ if (opts?.calculate != null) {
16
+ const calculate = opts.calculate
17
+
18
+ collect = async function () {
19
+ const value = await calculate()
20
+
21
+ this.set(value)
22
+ }
23
+ }
12
24
 
13
25
  this.gauge = new Gauge({
14
26
  name,
15
27
  help,
16
- labelNames: label != null ? [label] : []
28
+ labelNames: labels,
29
+ collect
17
30
  })
18
31
  }
19
32
 
@@ -21,11 +34,11 @@ export class PrometheusMetric implements Metric {
21
34
  this.gauge.set(value)
22
35
  }
23
36
 
24
- increment (value = 1): void {
37
+ increment (value: number = 1): void {
25
38
  this.gauge.inc(value)
26
39
  }
27
40
 
28
- decrement (value = 1): void {
41
+ decrement (value: number = 1): void {
29
42
  this.gauge.dec(value)
30
43
  }
31
44
 
package/src/utils.ts CHANGED
@@ -2,6 +2,12 @@
2
2
  export const ONE_SECOND = 1000
3
3
  export const ONE_MINUTE = 60 * ONE_SECOND
4
4
 
5
+ /**
6
+ * See https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
7
+ * for rules on valid naming
8
+ */
5
9
  export function normaliseString (str: string): string {
6
- return str.replace(/-/g, '_')
10
+ return str
11
+ .replace(/[^a-zA-Z0-9_]/g, '_')
12
+ .replace(/_+/g, '_')
7
13
  }
@@ -1,20 +0,0 @@
1
- export interface MovingAverage {
2
- variance: number;
3
- movingAverage: number;
4
- deviation: number;
5
- forecast: number;
6
- push: (time: number, value: number) => void;
7
- }
8
- export declare class DefaultMovingAverage {
9
- movingAverage: number;
10
- variance: number;
11
- deviation: number;
12
- forecast: number;
13
- private readonly timespan;
14
- private previousTime?;
15
- constructor(timespan: number);
16
- alpha(t: number, pt: number): number;
17
- push(time: number, value: number): void;
18
- }
19
- export declare function createMovingAverage(timespan: number): MovingAverage;
20
- //# sourceMappingURL=moving-average.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"moving-average.d.ts","sourceRoot":"","sources":["../../src/moving-average.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAEhB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5C;AAED,qBAAa,oBAAoB;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,YAAY,CAAC,CAAQ;gBAEhB,QAAQ,EAAE,MAAM;IAgB7B,KAAK,CAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;IAI5B,IAAI,CAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAkBlC;AAED,wBAAgB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAEpE"}
@@ -1,40 +0,0 @@
1
- export class DefaultMovingAverage {
2
- constructor(timespan) {
3
- if (typeof timespan !== 'number') {
4
- throw new Error('must provide a timespan to the moving average constructor');
5
- }
6
- if (timespan <= 0) {
7
- throw new Error('must provide a timespan > 0 to the moving average constructor');
8
- }
9
- this.timespan = timespan;
10
- this.movingAverage = 0;
11
- this.variance = 0;
12
- this.deviation = 0;
13
- this.forecast = 0;
14
- }
15
- alpha(t, pt) {
16
- return 1 - (Math.exp(-(t - pt) / this.timespan));
17
- }
18
- push(time, value) {
19
- if (this.previousTime != null) {
20
- // calculate moving average
21
- const a = this.alpha(time, this.previousTime);
22
- const diff = value - this.movingAverage;
23
- const incr = a * diff;
24
- this.movingAverage = a * value + (1 - a) * this.movingAverage;
25
- // calculate variance & deviation
26
- this.variance = (1 - a) * (this.variance + diff * incr);
27
- this.deviation = Math.sqrt(this.variance);
28
- // calculate forecast
29
- this.forecast = this.movingAverage + a * diff;
30
- }
31
- else {
32
- this.movingAverage = value;
33
- }
34
- this.previousTime = time;
35
- }
36
- }
37
- export function createMovingAverage(timespan) {
38
- return new DefaultMovingAverage(timespan);
39
- }
40
- //# sourceMappingURL=moving-average.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"moving-average.js","sourceRoot":"","sources":["../../src/moving-average.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,oBAAoB;IAQ/B,YAAa,QAAgB;QAC3B,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE;YAChC,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAA;SAC7E;QAED,IAAI,QAAQ,IAAI,CAAC,EAAE;YACjB,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAA;SACjF;QAED,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;QACjB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAA;QAClB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;IACnB,CAAC;IAED,KAAK,CAAE,CAAS,EAAE,EAAU;QAC1B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAClD,CAAC;IAED,IAAI,CAAE,IAAY,EAAE,KAAa;QAC/B,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE;YAC7B,2BAA2B;YAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;YAC7C,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,aAAa,CAAA;YACvC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAA;YACrB,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,aAAa,CAAA;YAC7D,iCAAiC;YACjC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC,CAAA;YACvD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACzC,qBAAqB;YACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,IAAI,CAAA;SAC9C;aAAM;YACL,IAAI,CAAC,aAAa,GAAG,KAAK,CAAA;SAC3B;QAED,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;IAC1B,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CAAE,QAAgB;IACnD,OAAO,IAAI,oBAAoB,CAAC,QAAQ,CAAC,CAAA;AAC3C,CAAC"}