@libp2p/prometheus-metrics 0.0.0

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/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ This project is dual licensed under MIT and Apache-2.0.
2
+
3
+ MIT: https://www.opensource.org/licenses/mit
4
+ Apache-2.0: https://www.apache.org/licenses/license-2.0
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @libp2p/prometheus-metrics <!-- omit in toc -->
2
+
3
+ [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
4
+ [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io)
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
+ [![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
+
8
+ > Collect libp2p metrics for scraping by Prometheus
9
+
10
+ ## Table of contents <!-- omit in toc -->
11
+
12
+ - [Install](#install)
13
+ - [License](#license)
14
+ - [Contribute](#contribute)
15
+
16
+ ## Install
17
+
18
+ ```console
19
+ $ npm i @libp2p/prometheus-metrics
20
+ ```
21
+
22
+ ## License
23
+
24
+ Licensed under either of
25
+
26
+ - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
27
+ - MIT ([LICENSE-MIT](LICENSE-MIT) / <http://opensource.org/licenses/MIT>)
28
+
29
+ ## Contribute
30
+
31
+ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
@@ -0,0 +1,10 @@
1
+ import type { Metrics } from '@libp2p/interface-metrics';
2
+ import { DefaultMetricsCollectorConfiguration } from 'prom-client';
3
+ import { DefaultMetricsInit } from '@libp2p/metrics';
4
+ export interface PrometheusMetricsInit extends DefaultMetricsInit {
5
+ defaultMetrics?: DefaultMetricsCollectorConfiguration;
6
+ preserveExistingMetrics?: boolean;
7
+ collectDefaultMetrics?: boolean;
8
+ }
9
+ export declare function prometheusMetrics(init?: Partial<PrometheusMetricsInit>): () => Metrics;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,99 @@
1
+ import { Gauge, collectDefaultMetrics, register } from 'prom-client';
2
+ import { PrometheusMetric } from './metric.js';
3
+ import { PrometheusMetricGroup } from './metric-group.js';
4
+ import { DefaultMetrics } from '@libp2p/metrics';
5
+ class PrometheusMetrics extends DefaultMetrics {
6
+ constructor(init) {
7
+ super(init);
8
+ 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
11
+ register.clear();
12
+ }
13
+ if (init?.preserveExistingMetrics !== false) {
14
+ // collect memory/CPU and node-specific default metrics
15
+ collectDefaultMetrics(init?.defaultMetrics);
16
+ }
17
+ this.registerMetricGroup('libp2p_data_transfer_bytes', {
18
+ label: 'protocol',
19
+ calculate: () => {
20
+ 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);
32
+ }
33
+ return output;
34
+ }
35
+ });
36
+ this.registerMetricGroup('nodejs_memory_usage_bytes', {
37
+ label: 'memory',
38
+ calculate: () => {
39
+ return {
40
+ ...process.memoryUsage()
41
+ };
42
+ }
43
+ });
44
+ }
45
+ registerMetric(name, opts) {
46
+ if (name == null ?? name.trim() === '') {
47
+ throw new Error('Metric name is required');
48
+ }
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;
64
+ }
65
+ return new PrometheusMetric(name, opts ?? {});
66
+ }
67
+ registerMetricGroup(name, opts) {
68
+ if (name == null ?? name.trim() === '') {
69
+ throw new Error('Metric name is required');
70
+ }
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;
90
+ }
91
+ return new PrometheusMetricGroup(name, opts ?? {});
92
+ }
93
+ }
94
+ export function prometheusMetrics(init) {
95
+ return () => {
96
+ return new PrometheusMetrics(init);
97
+ };
98
+ }
99
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { MetricOptions, MetricGroup, StopTimer } from '@libp2p/interface-metrics';
2
+ export declare class PrometheusMetricGroup implements MetricGroup {
3
+ private readonly gauge;
4
+ private readonly label;
5
+ constructor(name: string, opts: MetricOptions);
6
+ update(values: Record<string, number>): void;
7
+ increment(values: Record<string, number | unknown>): void;
8
+ decrement(values: Record<string, number | unknown>): void;
9
+ reset(): void;
10
+ timer(key: string): StopTimer;
11
+ }
12
+ //# sourceMappingURL=metric-group.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,40 @@
1
+ import { Gauge } from 'prom-client';
2
+ import { normaliseString } from './utils.js';
3
+ export class PrometheusMetricGroup {
4
+ constructor(name, opts) {
5
+ name = normaliseString(name);
6
+ const help = normaliseString(opts.help ?? name);
7
+ this.label = normaliseString(opts.label ?? name);
8
+ this.gauge = new Gauge({
9
+ name,
10
+ help,
11
+ labelNames: [this.label]
12
+ });
13
+ }
14
+ update(values) {
15
+ Object.entries(values).forEach(([key, value]) => {
16
+ this.gauge.set({ [this.label]: key }, value);
17
+ });
18
+ }
19
+ increment(values) {
20
+ Object.entries(values).forEach(([key, value]) => {
21
+ const inc = typeof value === 'number' ? value : 1;
22
+ this.gauge.inc({ [this.label]: key }, inc);
23
+ });
24
+ }
25
+ decrement(values) {
26
+ Object.entries(values).forEach(([key, value]) => {
27
+ const dec = typeof value === 'number' ? value : 1;
28
+ this.gauge.dec({ [this.label]: key }, dec);
29
+ });
30
+ }
31
+ reset() {
32
+ this.gauge.reset();
33
+ }
34
+ timer(key) {
35
+ return this.gauge.startTimer({
36
+ key: 0
37
+ });
38
+ }
39
+ }
40
+ //# sourceMappingURL=metric-group.js.map
@@ -0,0 +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"}
@@ -0,0 +1,11 @@
1
+ import type { Metric, MetricOptions, StopTimer } from '@libp2p/interface-metrics';
2
+ export declare class PrometheusMetric implements Metric {
3
+ private readonly gauge;
4
+ constructor(name: string, opts: MetricOptions);
5
+ update(value: number): void;
6
+ increment(value?: number): void;
7
+ decrement(value?: number): void;
8
+ reset(): void;
9
+ timer(): StopTimer;
10
+ }
11
+ //# sourceMappingURL=metric.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,30 @@
1
+ import { Gauge } from 'prom-client';
2
+ import { normaliseString } from './utils.js';
3
+ export class PrometheusMetric {
4
+ constructor(name, opts) {
5
+ name = normaliseString(name);
6
+ const help = normaliseString(opts.help ?? name);
7
+ const label = opts.label != null ? normaliseString(opts.label) : undefined;
8
+ this.gauge = new Gauge({
9
+ name,
10
+ help,
11
+ labelNames: label != null ? [label] : []
12
+ });
13
+ }
14
+ update(value) {
15
+ this.gauge.set(value);
16
+ }
17
+ increment(value = 1) {
18
+ this.gauge.inc(value);
19
+ }
20
+ decrement(value = 1) {
21
+ this.gauge.dec(value);
22
+ }
23
+ reset() {
24
+ this.gauge.reset();
25
+ }
26
+ timer() {
27
+ return this.gauge.startTimer();
28
+ }
29
+ }
30
+ //# sourceMappingURL=metric.js.map
@@ -0,0 +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"}
@@ -0,0 +1,20 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,40 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,4 @@
1
+ export declare const ONE_SECOND = 1000;
2
+ export declare const ONE_MINUTE: number;
3
+ export declare function normaliseString(str: string): string;
4
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,6 @@
1
+ export const ONE_SECOND = 1000;
2
+ export const ONE_MINUTE = 60 * ONE_SECOND;
3
+ export function normaliseString(str) {
4
+ return str.replace(/-/g, '_');
5
+ }
6
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +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"}
package/package.json ADDED
@@ -0,0 +1,155 @@
1
+ {
2
+ "name": "@libp2p/prometheus-metrics",
3
+ "version": "0.0.0",
4
+ "description": "Collect libp2p metrics for scraping by Prometheus",
5
+ "author": "",
6
+ "license": "Apache-2.0 OR MIT",
7
+ "homepage": "https://github.com/libp2p/js-libp2p-prometheus-metrics#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/libp2p/js-libp2p-prometheus-metrics.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/libp2p/js-libp2p-prometheus-metrics/issues"
14
+ },
15
+ "engines": {
16
+ "node": ">=16.0.0",
17
+ "npm": ">=7.0.0"
18
+ },
19
+ "type": "module",
20
+ "types": "./dist/src/index.d.ts",
21
+ "files": [
22
+ "src",
23
+ "dist/src",
24
+ "!dist/test",
25
+ "!**/*.tsbuildinfo"
26
+ ],
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/src/index.d.ts",
30
+ "import": "./dist/src/index.js"
31
+ }
32
+ },
33
+ "eslintConfig": {
34
+ "extends": "ipfs",
35
+ "parserOptions": {
36
+ "sourceType": "module"
37
+ }
38
+ },
39
+ "release": {
40
+ "branches": [
41
+ "main"
42
+ ],
43
+ "plugins": [
44
+ [
45
+ "@semantic-release/commit-analyzer",
46
+ {
47
+ "preset": "conventionalcommits",
48
+ "releaseRules": [
49
+ {
50
+ "breaking": true,
51
+ "release": "major"
52
+ },
53
+ {
54
+ "revert": true,
55
+ "release": "patch"
56
+ },
57
+ {
58
+ "type": "feat",
59
+ "release": "minor"
60
+ },
61
+ {
62
+ "type": "fix",
63
+ "release": "patch"
64
+ },
65
+ {
66
+ "type": "docs",
67
+ "release": "patch"
68
+ },
69
+ {
70
+ "type": "test",
71
+ "release": "patch"
72
+ },
73
+ {
74
+ "type": "deps",
75
+ "release": "patch"
76
+ },
77
+ {
78
+ "scope": "no-release",
79
+ "release": false
80
+ }
81
+ ]
82
+ }
83
+ ],
84
+ [
85
+ "@semantic-release/release-notes-generator",
86
+ {
87
+ "preset": "conventionalcommits",
88
+ "presetConfig": {
89
+ "types": [
90
+ {
91
+ "type": "feat",
92
+ "section": "Features"
93
+ },
94
+ {
95
+ "type": "fix",
96
+ "section": "Bug Fixes"
97
+ },
98
+ {
99
+ "type": "chore",
100
+ "section": "Trivial Changes"
101
+ },
102
+ {
103
+ "type": "docs",
104
+ "section": "Documentation"
105
+ },
106
+ {
107
+ "type": "deps",
108
+ "section": "Dependencies"
109
+ },
110
+ {
111
+ "type": "test",
112
+ "section": "Tests"
113
+ }
114
+ ]
115
+ }
116
+ }
117
+ ],
118
+ "@semantic-release/changelog",
119
+ "@semantic-release/npm",
120
+ "@semantic-release/github",
121
+ "@semantic-release/git"
122
+ ]
123
+ },
124
+ "scripts": {
125
+ "clean": "aegir clean",
126
+ "lint": "aegir lint",
127
+ "dep-check": "aegir dep-check",
128
+ "build": "aegir build",
129
+ "test": "aegir test -t node",
130
+ "test:node": "aegir test -t node --cov",
131
+ "test:electron-main": "aegir test -t electron-main --cov",
132
+ "release": "aegir release"
133
+ },
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"
142
+ },
143
+ "devDependencies": {
144
+ "@libp2p/interface-connection": "^3.0.2",
145
+ "@libp2p/interface-mocks": "^7.0.3",
146
+ "@libp2p/peer-id-factory": "^1.0.19",
147
+ "@multiformats/multiaddr": "^11.0.7",
148
+ "aegir": "^37.5.6",
149
+ "it-pair": "^2.0.3",
150
+ "it-stream-types": "^1.0.4",
151
+ "p-defer": "^4.0.0",
152
+ "prom-client": "^14.1.0",
153
+ "uint8arraylist": "^2.3.3"
154
+ }
155
+ }
package/src/index.ts ADDED
@@ -0,0 +1,135 @@
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'
4
+ import { PrometheusMetric } from './metric.js'
5
+ import { PrometheusMetricGroup } from './metric-group.js'
6
+ import { DefaultMetrics, DefaultMetricsInit } from '@libp2p/metrics'
7
+
8
+ export interface PrometheusMetricsInit extends DefaultMetricsInit {
9
+ defaultMetrics?: DefaultMetricsCollectorConfiguration
10
+ preserveExistingMetrics?: boolean
11
+ collectDefaultMetrics?: boolean
12
+ }
13
+
14
+ class PrometheusMetrics extends DefaultMetrics implements Metrics, Startable {
15
+ constructor (init?: Partial<PrometheusMetricsInit>) {
16
+ super(init)
17
+
18
+ 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
21
+ register.clear()
22
+ }
23
+
24
+ if (init?.preserveExistingMetrics !== false) {
25
+ // collect memory/CPU and node-specific default metrics
26
+ collectDefaultMetrics(init?.defaultMetrics)
27
+ }
28
+
29
+ this.registerMetricGroup('libp2p_data_transfer_bytes', {
30
+ label: 'protocol',
31
+ calculate: () => {
32
+ const output: Record<string, number> = {}
33
+
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)
48
+ }
49
+
50
+ return output
51
+ }
52
+ })
53
+
54
+ this.registerMetricGroup('nodejs_memory_usage_bytes', {
55
+ label: 'memory',
56
+ calculate: () => {
57
+ return {
58
+ ...process.memoryUsage()
59
+ }
60
+ }
61
+ })
62
+ }
63
+
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
+ }
70
+
71
+ if (opts?.calculate != null) {
72
+ const calculate = opts.calculate
73
+
74
+ // calculated metric
75
+ const collect: CollectFunction<Gauge<any>> = async function () {
76
+ const value = await calculate()
77
+
78
+ this.set(value)
79
+ }
80
+
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
+ })
88
+
89
+ return
90
+ }
91
+
92
+ return new PrometheusMetric(name, opts ?? {})
93
+ }
94
+
95
+ registerMetricGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
96
+ registerMetricGroup (name: string, opts?: MetricOptions): MetricGroup
97
+ registerMetricGroup (name: string, opts: any): any {
98
+ if (name == null ?? name.trim() === '') {
99
+ throw new Error('Metric name is required')
100
+ }
101
+
102
+ if (opts?.calculate != null) {
103
+ // calculated metric
104
+ const calculate: CalculateMetric<Record<string, number>> = opts.calculate
105
+ const label = opts.label ?? name
106
+
107
+ // calculated metric
108
+ const collect: CollectFunction<Gauge<any>> = async function () {
109
+ const values = await calculate()
110
+
111
+ Object.entries(values).forEach(([key, value]) => {
112
+ this.set({ [label]: key }, value)
113
+ })
114
+ }
115
+
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
+ })
123
+
124
+ return
125
+ }
126
+
127
+ return new PrometheusMetricGroup(name, opts ?? {})
128
+ }
129
+ }
130
+
131
+ export function prometheusMetrics (init?: Partial<PrometheusMetricsInit>): () => Metrics {
132
+ return () => {
133
+ return new PrometheusMetrics(init)
134
+ }
135
+ }
@@ -0,0 +1,52 @@
1
+ import type { MetricOptions, MetricGroup, StopTimer } from '@libp2p/interface-metrics'
2
+ import { Gauge } from 'prom-client'
3
+ import { normaliseString } from './utils.js'
4
+
5
+ export class PrometheusMetricGroup implements MetricGroup {
6
+ private readonly gauge: Gauge
7
+ private readonly label: string
8
+
9
+ constructor (name: string, opts: MetricOptions) {
10
+ name = normaliseString(name)
11
+ const help = normaliseString(opts.help ?? name)
12
+ this.label = normaliseString(opts.label ?? name)
13
+
14
+ this.gauge = new Gauge({
15
+ name,
16
+ help,
17
+ labelNames: [this.label]
18
+ })
19
+ }
20
+
21
+ update (values: Record<string, number>): void {
22
+ Object.entries(values).forEach(([key, value]) => {
23
+ this.gauge.set({ [this.label]: key }, value)
24
+ })
25
+ }
26
+
27
+ increment (values: Record<string, number | unknown>): void {
28
+ Object.entries(values).forEach(([key, value]) => {
29
+ const inc = typeof value === 'number' ? value : 1
30
+
31
+ this.gauge.inc({ [this.label]: key }, inc)
32
+ })
33
+ }
34
+
35
+ decrement (values: Record<string, number | unknown>): void {
36
+ Object.entries(values).forEach(([key, value]) => {
37
+ const dec = typeof value === 'number' ? value : 1
38
+
39
+ this.gauge.dec({ [this.label]: key }, dec)
40
+ })
41
+ }
42
+
43
+ reset (): void {
44
+ this.gauge.reset()
45
+ }
46
+
47
+ timer (key: string): StopTimer {
48
+ return this.gauge.startTimer({
49
+ key: 0
50
+ })
51
+ }
52
+ }
package/src/metric.ts ADDED
@@ -0,0 +1,39 @@
1
+ import type { Metric, MetricOptions, StopTimer } from '@libp2p/interface-metrics'
2
+ import { Gauge } from 'prom-client'
3
+ import { normaliseString } from './utils.js'
4
+
5
+ export class PrometheusMetric implements Metric {
6
+ private readonly gauge: Gauge
7
+
8
+ constructor (name: string, opts: MetricOptions) {
9
+ name = normaliseString(name)
10
+ const help = normaliseString(opts.help ?? name)
11
+ const label = opts.label != null ? normaliseString(opts.label) : undefined
12
+
13
+ this.gauge = new Gauge({
14
+ name,
15
+ help,
16
+ labelNames: label != null ? [label] : []
17
+ })
18
+ }
19
+
20
+ update (value: number): void {
21
+ this.gauge.set(value)
22
+ }
23
+
24
+ increment (value = 1): void {
25
+ this.gauge.inc(value)
26
+ }
27
+
28
+ decrement (value = 1): void {
29
+ this.gauge.dec(value)
30
+ }
31
+
32
+ reset (): void {
33
+ this.gauge.reset()
34
+ }
35
+
36
+ timer (): StopTimer {
37
+ return this.gauge.startTimer()
38
+ }
39
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,7 @@
1
+
2
+ export const ONE_SECOND = 1000
3
+ export const ONE_MINUTE = 60 * ONE_SECOND
4
+
5
+ export function normaliseString (str: string): string {
6
+ return str.replace(/-/g, '_')
7
+ }