@libp2p/simple-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,40 @@
1
+ # @libp2p/simple-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-simple-metrics.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-simple-metrics)
6
+ [![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p-simple-metrics/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/libp2p/js-libp2p-simple-metrics/actions/workflows/js-test-and-release.yml?query=branch%3Amain)
7
+
8
+ > Simple in-memory metrics gathering for libp2p
9
+
10
+ ## Table of contents <!-- omit in toc -->
11
+
12
+ - [Install](#install)
13
+ - [Browser `<script>` tag](#browser-script-tag)
14
+ - [License](#license)
15
+ - [Contribution](#contribution)
16
+
17
+ ## Install
18
+
19
+ ```console
20
+ $ npm i @libp2p/simple-metrics
21
+ ```
22
+
23
+ ### Browser `<script>` tag
24
+
25
+ Loading this module through a script tag will make it's exports available as `Libp2pSimpleMetrics` in the global namespace.
26
+
27
+ ```html
28
+ <script src="https://unpkg.com/@libp2p/simple-metrics/dist/index.min.js"></script>
29
+ ```
30
+
31
+ ## License
32
+
33
+ Licensed under either of
34
+
35
+ - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
36
+ - MIT ([LICENSE-MIT](LICENSE-MIT) / <http://opensource.org/licenses/MIT>)
37
+
38
+ ## Contribution
39
+
40
+ 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,3 @@
1
+ (function (root, factory) {(typeof module === 'object' && module.exports) ? module.exports = factory() : root.Libp2PSimpleMetrics = factory()}(typeof self !== 'undefined' ? self : this, function () {
2
+ "use strict";var Libp2PSimpleMetrics=(()=>{var ie=Object.create;var j=Object.defineProperty;var se=Object.getOwnPropertyDescriptor;var ae=Object.getOwnPropertyNames;var ce=Object.getPrototypeOf,ue=Object.prototype.hasOwnProperty;var D=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),le=(t,e)=>{for(var r in e)j(t,r,{get:e[r],enumerable:!0})},V=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ae(e))!ue.call(t,i)&&i!==r&&j(t,i,{get:()=>e[i],enumerable:!(n=se(e,i))||n.enumerable});return t};var de=(t,e,r)=>(r=t!=null?ie(ce(t)):{},V(e||!t||!t.__esModule?j(r,"default",{value:t,enumerable:!0}):r,t)),fe=t=>V(j({},"__esModule",{value:!0}),t);var X=D((je,_)=>{var U=1e3,k=U*60,R=k*60,E=R*24,pe=E*7,he=E*365.25;_.exports=function(t,e){e=e||{};var r=typeof t;if(r==="string"&&t.length>0)return me(t);if(r==="number"&&isFinite(t))return e.long?Ce(t):be(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))};function me(t){if(t=String(t),!(t.length>100)){var e=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),n=(e[2]||"ms").toLowerCase();switch(n){case"years":case"year":case"yrs":case"yr":case"y":return r*he;case"weeks":case"week":case"w":return r*pe;case"days":case"day":case"d":return r*E;case"hours":case"hour":case"hrs":case"hr":case"h":return r*R;case"minutes":case"minute":case"mins":case"min":case"m":return r*k;case"seconds":case"second":case"secs":case"sec":case"s":return r*U;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}}}function be(t){var e=Math.abs(t);return e>=E?Math.round(t/E)+"d":e>=R?Math.round(t/R)+"h":e>=k?Math.round(t/k)+"m":e>=U?Math.round(t/U)+"s":t+"ms"}function Ce(t){var e=Math.abs(t);return e>=E?G(t,e,E,"day"):e>=R?G(t,e,R,"hour"):e>=k?G(t,e,k,"minute"):e>=U?G(t,e,U,"second"):t+" ms"}function G(t,e,r,n){var i=e>=r*1.5;return Math.round(t/r)+" "+n+(i?"s":"")}});var K=D((Ge,Z)=>{function ge(t){r.debug=r,r.default=r,r.coerce=g,r.disable=p,r.enable=i,r.enabled=C,r.humanize=X(),r.destroy=I,Object.keys(t).forEach(o=>{r[o]=t[o]}),r.names=[],r.skips=[],r.formatters={};function e(o){let s=0;for(let u=0;u<o.length;u++)s=(s<<5)-s+o.charCodeAt(u),s|=0;return r.colors[Math.abs(s)%r.colors.length]}r.selectColor=e;function r(o){let s,u=null,S,a;function c(...d){if(!c.enabled)return;let f=c,m=Number(new Date),x=m-(s||m);f.diff=x,f.prev=s,f.curr=m,s=m,d[0]=r.coerce(d[0]),typeof d[0]!="string"&&d.unshift("%O");let h=0;d[0]=d[0].replace(/%([a-zA-Z%])/g,(F,w)=>{if(F==="%%")return"%";h++;let M=r.formatters[w];if(typeof M=="function"){let O=d[h];F=M.call(f,O),d.splice(h,1),h--}return F}),r.formatArgs.call(f,d),(f.log||r.log).apply(f,d)}return c.namespace=o,c.useColors=r.useColors(),c.color=r.selectColor(o),c.extend=n,c.destroy=r.destroy,Object.defineProperty(c,"enabled",{enumerable:!0,configurable:!1,get:()=>u!==null?u:(S!==r.namespaces&&(S=r.namespaces,a=r.enabled(o)),a),set:d=>{u=d}}),typeof r.init=="function"&&r.init(c),c}function n(o,s){let u=r(this.namespace+(typeof s>"u"?":":s)+o);return u.log=this.log,u}function i(o){r.save(o),r.namespaces=o,r.names=[],r.skips=[];let s,u=(typeof o=="string"?o:"").split(/[\s,]+/),S=u.length;for(s=0;s<S;s++)u[s]&&(o=u[s].replace(/\*/g,".*?"),o[0]==="-"?r.skips.push(new RegExp("^"+o.slice(1)+"$")):r.names.push(new RegExp("^"+o+"$")))}function p(){let o=[...r.names.map(l),...r.skips.map(l).map(s=>"-"+s)].join(",");return r.enable(""),o}function C(o){if(o[o.length-1]==="*")return!0;let s,u;for(s=0,u=r.skips.length;s<u;s++)if(r.skips[s].test(o))return!1;for(s=0,u=r.names.length;s<u;s++)if(r.names[s].test(o))return!0;return!1}function l(o){return o.toString().substring(2,o.toString().length-2).replace(/\.\*\?$/,"*")}function g(o){return o instanceof Error?o.stack||o.message:o}function I(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}return r.enable(r.load()),r}Z.exports=ge});var Q=D((v,P)=>{v.formatArgs=ve;v.save=ye;v.load=xe;v.useColors=we;v.storage=Fe();v.destroy=(()=>{let t=!1;return()=>{t||(t=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();v.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function we(){return typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs)?!0:typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)?!1:typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function ve(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+P.exports.humanize(this.diff),!this.useColors)return;let e="color: "+this.color;t.splice(1,0,e,"color: inherit");let r=0,n=0;t[0].replace(/%[a-zA-Z%]/g,i=>{i!=="%%"&&(r++,i==="%c"&&(n=r))}),t.splice(n,0,e)}v.log=console.debug||console.log||(()=>{});function ye(t){try{t?v.storage.setItem("debug",t):v.storage.removeItem("debug")}catch{}}function xe(){let t;try{t=v.storage.getItem("debug")}catch{}return!t&&typeof process<"u"&&"env"in process&&(t=process.env.DEBUG),t}function Fe(){try{return localStorage}catch{}}P.exports=K()(v);var{formatters:Me}=P.exports;Me.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}});var ze={};le(ze,{simpleMetrics:()=>Ie});var y=de(Q(),1);function Ae(t,e){if(t.length>=255)throw new TypeError("Alphabet too long");for(var r=new Uint8Array(256),n=0;n<r.length;n++)r[n]=255;for(var i=0;i<t.length;i++){var p=t.charAt(i),C=p.charCodeAt(0);if(r[C]!==255)throw new TypeError(p+" is ambiguous");r[C]=i}var l=t.length,g=t.charAt(0),I=Math.log(l)/Math.log(256),o=Math.log(256)/Math.log(l);function s(a){if(a instanceof Uint8Array||(ArrayBuffer.isView(a)?a=new Uint8Array(a.buffer,a.byteOffset,a.byteLength):Array.isArray(a)&&(a=Uint8Array.from(a))),!(a instanceof Uint8Array))throw new TypeError("Expected Uint8Array");if(a.length===0)return"";for(var c=0,d=0,f=0,m=a.length;f!==m&&a[f]===0;)f++,c++;for(var x=(m-f)*o+1>>>0,h=new Uint8Array(x);f!==m;){for(var A=a[f],F=0,w=x-1;(A!==0||F<d)&&w!==-1;w--,F++)A+=256*h[w]>>>0,h[w]=A%l>>>0,A=A/l>>>0;if(A!==0)throw new Error("Non-zero carry");d=F,f++}for(var M=x-d;M!==x&&h[M]===0;)M++;for(var O=g.repeat(c);M<x;++M)O+=t.charAt(h[M]);return O}function u(a){if(typeof a!="string")throw new TypeError("Expected String");if(a.length===0)return new Uint8Array;var c=0;if(a[c]!==" "){for(var d=0,f=0;a[c]===g;)d++,c++;for(var m=(a.length-c)*I+1>>>0,x=new Uint8Array(m);a[c];){var h=r[a.charCodeAt(c)];if(h===255)return;for(var A=0,F=m-1;(h!==0||A<f)&&F!==-1;F--,A++)h+=l*x[F]>>>0,x[F]=h%256>>>0,h=h/256>>>0;if(h!==0)throw new Error("Non-zero carry");f=A,c++}if(a[c]!==" "){for(var w=m-f;w!==m&&x[w]===0;)w++;for(var M=new Uint8Array(d+(m-w)),O=d;w!==m;)M[O++]=x[w++];return M}}}function S(a){var c=u(a);if(c)return c;throw new Error(`Non-${e} character`)}return{encode:s,decodeUnsafe:u,decode:S}}var Ee=Ae,Se=Ee,W=Se;var De=new Uint8Array(0);var Y=t=>{if(t instanceof Uint8Array&&t.constructor.name==="Uint8Array")return t;if(t instanceof ArrayBuffer)return new Uint8Array(t);if(ArrayBuffer.isView(t))return new Uint8Array(t.buffer,t.byteOffset,t.byteLength);throw new Error("Unknown type, must be binary type")};var T=class{constructor(e,r,n){this.name=e,this.prefix=r,this.baseEncode=n}encode(e){if(e instanceof Uint8Array)return`${this.prefix}${this.baseEncode(e)}`;throw Error("Unknown type, must be binary type")}},q=class{constructor(e,r,n){if(this.name=e,this.prefix=r,r.codePointAt(0)===void 0)throw new Error("Invalid prefix character");this.prefixCodePoint=r.codePointAt(0),this.baseDecode=n}decode(e){if(typeof e=="string"){if(e.codePointAt(0)!==this.prefixCodePoint)throw Error(`Unable to decode multibase string ${JSON.stringify(e)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`);return this.baseDecode(e.slice(this.prefix.length))}else throw Error("Can only multibase decode strings")}or(e){return H(this,e)}},$=class{constructor(e){this.decoders=e}or(e){return H(this,e)}decode(e){let r=e[0],n=this.decoders[r];if(n)return n.decode(e);throw RangeError(`Unable to decode multibase string ${JSON.stringify(e)}, only inputs prefixed with ${Object.keys(this.decoders)} are supported`)}},H=(t,e)=>new $({...t.decoders||{[t.prefix]:t},...e.decoders||{[e.prefix]:e}}),L=class{constructor(e,r,n,i){this.name=e,this.prefix=r,this.baseEncode=n,this.baseDecode=i,this.encoder=new T(e,r,n),this.decoder=new q(e,r,i)}encode(e){return this.encoder.encode(e)}decode(e){return this.decoder.decode(e)}},ee=({name:t,prefix:e,encode:r,decode:n})=>new L(t,e,r,n),B=({prefix:t,name:e,alphabet:r})=>{let{encode:n,decode:i}=W(r,e);return ee({prefix:t,name:e,encode:n,decode:p=>Y(i(p))})},Oe=(t,e,r,n)=>{let i={};for(let o=0;o<e.length;++o)i[e[o]]=o;let p=t.length;for(;t[p-1]==="=";)--p;let C=new Uint8Array(p*r/8|0),l=0,g=0,I=0;for(let o=0;o<p;++o){let s=i[t[o]];if(s===void 0)throw new SyntaxError(`Non-${n} character`);g=g<<r|s,l+=r,l>=8&&(l-=8,C[I++]=255&g>>l)}if(l>=r||255&g<<8-l)throw new SyntaxError("Unexpected end of data");return C},Ue=(t,e,r)=>{let n=e[e.length-1]==="=",i=(1<<r)-1,p="",C=0,l=0;for(let g=0;g<t.length;++g)for(l=l<<8|t[g],C+=8;C>r;)C-=r,p+=e[i&l>>C];if(C&&(p+=e[i&l<<r-C]),n)for(;p.length*r&7;)p+="=";return p},b=({name:t,prefix:e,bitsPerChar:r,alphabet:n})=>ee({prefix:e,name:t,encode(i){return Ue(i,n,r)},decode(i){return Oe(i,n,r,t)}});var re=b({prefix:"b",name:"base32",alphabet:"abcdefghijklmnopqrstuvwxyz234567",bitsPerChar:5}),Je=b({prefix:"B",name:"base32upper",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",bitsPerChar:5}),Ve=b({prefix:"c",name:"base32pad",alphabet:"abcdefghijklmnopqrstuvwxyz234567=",bitsPerChar:5}),_e=b({prefix:"C",name:"base32padupper",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",bitsPerChar:5}),Xe=b({prefix:"v",name:"base32hex",alphabet:"0123456789abcdefghijklmnopqrstuv",bitsPerChar:5}),Ze=b({prefix:"V",name:"base32hexupper",alphabet:"0123456789ABCDEFGHIJKLMNOPQRSTUV",bitsPerChar:5}),Ke=b({prefix:"t",name:"base32hexpad",alphabet:"0123456789abcdefghijklmnopqrstuv=",bitsPerChar:5}),Qe=b({prefix:"T",name:"base32hexpadupper",alphabet:"0123456789ABCDEFGHIJKLMNOPQRSTUV=",bitsPerChar:5}),We=b({prefix:"h",name:"base32z",alphabet:"ybndrfg8ejkmcpqxot1uwisza345h769",bitsPerChar:5});var te=B({name:"base58btc",prefix:"z",alphabet:"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}),er=B({name:"base58flickr",prefix:"Z",alphabet:"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"});var ne=b({prefix:"m",name:"base64",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",bitsPerChar:6}),nr=b({prefix:"M",name:"base64pad",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",bitsPerChar:6}),or=b({prefix:"u",name:"base64url",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",bitsPerChar:6}),ir=b({prefix:"U",name:"base64urlpad",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",bitsPerChar:6});y.default.formatters.b=t=>t==null?"undefined":te.baseEncode(t);y.default.formatters.t=t=>t==null?"undefined":re.baseEncode(t);y.default.formatters.m=t=>t==null?"undefined":ne.baseEncode(t);y.default.formatters.p=t=>t==null?"undefined":t.toString();y.default.formatters.c=t=>t==null?"undefined":t.toString();y.default.formatters.k=t=>t==null?"undefined":t.toString();y.default.formatters.a=t=>t==null?"undefined":t.toString();function ke(t){let e=()=>{};return e.enabled=!1,e.color="",e.diff=0,e.log=()=>{},e.namespace=t,e.destroy=()=>!0,e.extend=()=>e,e}function oe(t){let e=ke(`${t}:trace`);return y.default.enabled(`${t}:trace`)&&y.default.names.map(r=>r.toString()).find(r=>r.includes(":trace"))!=null&&(e=(0,y.default)(`${t}:trace`)),Object.assign((0,y.default)(t),{error:(0,y.default)(`${t}:error`),trace:e})}var Re=oe("libp2p:simple-metrics"),z=class{value=0;update(e){this.value=e}increment(e=1){this.value+=e}decrement(e=1){this.value-=e}reset(){this.value=0}timer(){let e=Date.now();return()=>{this.value=Date.now()-e}}},N=class{values={};update(e){Object.entries(e).forEach(([r,n])=>{this.values[r]=n})}increment(e){Object.entries(e).forEach(([r,n])=>{this.values[r]=this.values[r]??0;let i=typeof n=="number"?n:1;this.values[r]+=Number(i)})}decrement(e){Object.entries(e).forEach(([r,n])=>{this.values[r]=this.values[r]??0;let i=typeof n=="number"?n:1;this.values[r]-=Number(i)})}reset(){this.values={}}timer(e){let r=Date.now();return()=>{this.values[e]=Date.now()-r}}},J=class{metrics=new Map;started;interval;intervalMs;onMetrics;constructor(e,r){this.started=!1,this._emitMetrics=this._emitMetrics.bind(this),this.intervalMs=r.intervalMs??1e3,this.onMetrics=r.onMetrics}isStarted(){return this.started}start(){this.started=!0,this.interval=setInterval(this._emitMetrics,this.intervalMs)}stop(){this.started=!1,clearInterval(this.interval)}_emitMetrics(){Promise.resolve().then(async()=>{let e={};for(let[r,n]of this.metrics.entries())n instanceof z?e[r]=n.value:n instanceof N?e[r]=n.values:e[r]=await n();this.onMetrics(e)}).catch(e=>{Re.error("could not invoke onMetrics callback",e)})}trackMultiaddrConnection(e){}trackProtocolStream(e,r){}registerMetric(e,r={}){if(e==null)throw new Error("Metric name is required");if(r?.calculate!=null){this.metrics.set(e,r.calculate);return}let n=new z;return this.metrics.set(e,n),n}registerMetricGroup(e,r={}){if(e==null)throw new Error("Metric name is required");if(r?.calculate!=null){this.metrics.set(e,r.calculate);return}let n=new z;return this.metrics.set(e,n),n}registerCounter(e,r={}){if(e==null)throw new Error("Metric name is required");if(r?.calculate!=null){this.metrics.set(e,r.calculate);return}let n=new N;return this.metrics.set(e,n),n}registerCounterGroup(e,r={}){if(e==null)throw new Error("Metric name is required");if(r?.calculate!=null){this.metrics.set(e,r.calculate);return}let n=new N;return this.metrics.set(e,n),n}};function Ie(t){return e=>new J(e,t)}return fe(ze);})();
3
+ return Libp2PSimpleMetrics}));
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * Stores metrics in memory and periodically invokes a configured callback
5
+ * to receive them.
6
+ *
7
+ * @example
8
+ *
9
+ * ```ts
10
+ * import { createLibp2p } from 'libp2p'
11
+ * import { simpleMetrics } from '@libp2p/simple-metrics'
12
+ *
13
+ * const node = await createLibp2p({
14
+ * // ... other options
15
+ * metrics: simpleMetrics({
16
+ * onMetrics: (metrics) => {
17
+ * // do something with metrics
18
+ * }
19
+ * }),
20
+ * intervalMs: 1000 // default 1s
21
+ * })
22
+ *
23
+ * ```
24
+ */
25
+ import type { Metrics } from '@libp2p/interface/metrics';
26
+ export interface OnMetrics {
27
+ (metrics: Record<string, any>): void;
28
+ }
29
+ export interface SimpleMetricsInit {
30
+ /**
31
+ * How often to invoke the onMetrics callback
32
+ */
33
+ intervalMs?: number;
34
+ /**
35
+ * A callback periodically invoked with collected metrics
36
+ */
37
+ onMetrics: OnMetrics;
38
+ }
39
+ export declare function simpleMetrics(init: SimpleMetricsInit): (components: unknown) => Metrics;
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAkC,OAAO,EAAkF,MAAM,2BAA2B,CAAA;AAwExK,MAAM,WAAW,SAAS;IAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAA;CAAE;AAEnE,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,SAAS,EAAE,SAAS,CAAA;CACrB;AA4ID,wBAAgB,aAAa,CAAE,IAAI,EAAE,iBAAiB,GAAG,CAAC,UAAU,EAAE,OAAO,KAAK,OAAO,CAExF"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * Stores metrics in memory and periodically invokes a configured callback
5
+ * to receive them.
6
+ *
7
+ * @example
8
+ *
9
+ * ```ts
10
+ * import { createLibp2p } from 'libp2p'
11
+ * import { simpleMetrics } from '@libp2p/simple-metrics'
12
+ *
13
+ * const node = await createLibp2p({
14
+ * // ... other options
15
+ * metrics: simpleMetrics({
16
+ * onMetrics: (metrics) => {
17
+ * // do something with metrics
18
+ * }
19
+ * }),
20
+ * intervalMs: 1000 // default 1s
21
+ * })
22
+ *
23
+ * ```
24
+ */
25
+ import { logger } from '@libp2p/logger';
26
+ const log = logger('libp2p:simple-metrics');
27
+ class DefaultMetric {
28
+ value = 0;
29
+ update(value) {
30
+ this.value = value;
31
+ }
32
+ increment(value = 1) {
33
+ this.value += value;
34
+ }
35
+ decrement(value = 1) {
36
+ this.value -= value;
37
+ }
38
+ reset() {
39
+ this.value = 0;
40
+ }
41
+ timer() {
42
+ const start = Date.now();
43
+ return () => {
44
+ this.value = Date.now() - start;
45
+ };
46
+ }
47
+ }
48
+ class DefaultGroupMetric {
49
+ values = {};
50
+ update(values) {
51
+ Object.entries(values).forEach(([key, value]) => {
52
+ this.values[key] = value;
53
+ });
54
+ }
55
+ increment(values) {
56
+ Object.entries(values).forEach(([key, value]) => {
57
+ this.values[key] = this.values[key] ?? 0;
58
+ const inc = typeof value === 'number' ? value : 1;
59
+ this.values[key] += Number(inc);
60
+ });
61
+ }
62
+ decrement(values) {
63
+ Object.entries(values).forEach(([key, value]) => {
64
+ this.values[key] = this.values[key] ?? 0;
65
+ const dec = typeof value === 'number' ? value : 1;
66
+ this.values[key] -= Number(dec);
67
+ });
68
+ }
69
+ reset() {
70
+ this.values = {};
71
+ }
72
+ timer(key) {
73
+ const start = Date.now();
74
+ return () => {
75
+ this.values[key] = Date.now() - start;
76
+ };
77
+ }
78
+ }
79
+ class SimpleMetrics {
80
+ metrics = new Map();
81
+ started;
82
+ interval;
83
+ intervalMs;
84
+ onMetrics;
85
+ constructor(components, init) {
86
+ this.started = false;
87
+ this._emitMetrics = this._emitMetrics.bind(this);
88
+ this.intervalMs = init.intervalMs ?? 1000;
89
+ this.onMetrics = init.onMetrics;
90
+ }
91
+ isStarted() {
92
+ return this.started;
93
+ }
94
+ start() {
95
+ this.started = true;
96
+ this.interval = setInterval(this._emitMetrics, this.intervalMs);
97
+ }
98
+ stop() {
99
+ this.started = false;
100
+ clearInterval(this.interval);
101
+ }
102
+ _emitMetrics() {
103
+ void Promise.resolve().then(async () => {
104
+ const output = {};
105
+ for (const [name, metric] of this.metrics.entries()) {
106
+ if (metric instanceof DefaultMetric) {
107
+ output[name] = metric.value;
108
+ }
109
+ else if (metric instanceof DefaultGroupMetric) {
110
+ output[name] = metric.values;
111
+ }
112
+ else {
113
+ output[name] = await metric();
114
+ }
115
+ }
116
+ this.onMetrics(output);
117
+ })
118
+ .catch(err => {
119
+ log.error('could not invoke onMetrics callback', err);
120
+ });
121
+ }
122
+ trackMultiaddrConnection(maConn) {
123
+ }
124
+ trackProtocolStream(stream, connection) {
125
+ }
126
+ registerMetric(name, opts = {}) {
127
+ if (name == null ?? name.trim() === '') {
128
+ throw new Error('Metric name is required');
129
+ }
130
+ if (opts?.calculate != null) {
131
+ // calculated metric
132
+ this.metrics.set(name, opts.calculate);
133
+ return;
134
+ }
135
+ const metric = new DefaultMetric();
136
+ this.metrics.set(name, metric);
137
+ return metric;
138
+ }
139
+ registerMetricGroup(name, opts = {}) {
140
+ if (name == null ?? name.trim() === '') {
141
+ throw new Error('Metric name is required');
142
+ }
143
+ if (opts?.calculate != null) {
144
+ // calculated metric
145
+ this.metrics.set(name, opts.calculate);
146
+ return;
147
+ }
148
+ const metric = new DefaultMetric();
149
+ this.metrics.set(name, metric);
150
+ return metric;
151
+ }
152
+ registerCounter(name, opts = {}) {
153
+ if (name == null ?? name.trim() === '') {
154
+ throw new Error('Metric name is required');
155
+ }
156
+ if (opts?.calculate != null) {
157
+ // calculated metric
158
+ this.metrics.set(name, opts.calculate);
159
+ return;
160
+ }
161
+ const metric = new DefaultGroupMetric();
162
+ this.metrics.set(name, metric);
163
+ return metric;
164
+ }
165
+ registerCounterGroup(name, opts = {}) {
166
+ if (name == null ?? name.trim() === '') {
167
+ throw new Error('Metric name is required');
168
+ }
169
+ if (opts?.calculate != null) {
170
+ // calculated metric
171
+ this.metrics.set(name, opts.calculate);
172
+ return;
173
+ }
174
+ const metric = new DefaultGroupMetric();
175
+ this.metrics.set(name, metric);
176
+ return metric;
177
+ }
178
+ }
179
+ export function simpleMetrics(init) {
180
+ return (components) => new SimpleMetrics(components, init);
181
+ }
182
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@libp2p/simple-metrics",
3
+ "version": "0.0.0",
4
+ "description": "Simple in-memory metrics gathering for libp2p",
5
+ "license": "Apache-2.0 OR MIT",
6
+ "homepage": "https://github.com/libp2p/js-libp2p-simple-metrics#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/libp2p/js-libp2p-simple-metrics.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/libp2p/js-libp2p-simple-metrics/issues"
13
+ },
14
+ "type": "module",
15
+ "types": "./dist/src/index.d.ts",
16
+ "files": [
17
+ "src",
18
+ "dist",
19
+ "!dist/test",
20
+ "!**/*.tsbuildinfo"
21
+ ],
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/src/index.d.ts",
25
+ "import": "./dist/src/index.js"
26
+ }
27
+ },
28
+ "eslintConfig": {
29
+ "extends": "ipfs",
30
+ "parserOptions": {
31
+ "sourceType": "module"
32
+ }
33
+ },
34
+ "scripts": {
35
+ "clean": "aegir clean",
36
+ "lint": "aegir lint",
37
+ "build": "aegir build",
38
+ "test": "aegir test",
39
+ "test:node": "aegir test -t node --cov",
40
+ "test:chrome": "aegir test -t browser --cov",
41
+ "test:chrome-webworker": "aegir test -t webworker",
42
+ "test:firefox": "aegir test -t browser -- --browser firefox",
43
+ "test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
44
+ "dep-check": "aegir dep-check -i events"
45
+ },
46
+ "dependencies": {
47
+ "@libp2p/interface": "^0.1.2",
48
+ "@libp2p/logger": "^3.0.2"
49
+ },
50
+ "devDependencies": {
51
+ "aegir": "^40.0.13",
52
+ "p-defer": "^4.0.0"
53
+ }
54
+ }
package/src/index.ts ADDED
@@ -0,0 +1,255 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * Stores metrics in memory and periodically invokes a configured callback
5
+ * to receive them.
6
+ *
7
+ * @example
8
+ *
9
+ * ```ts
10
+ * import { createLibp2p } from 'libp2p'
11
+ * import { simpleMetrics } from '@libp2p/simple-metrics'
12
+ *
13
+ * const node = await createLibp2p({
14
+ * // ... other options
15
+ * metrics: simpleMetrics({
16
+ * onMetrics: (metrics) => {
17
+ * // do something with metrics
18
+ * }
19
+ * }),
20
+ * intervalMs: 1000 // default 1s
21
+ * })
22
+ *
23
+ * ```
24
+ */
25
+
26
+ import { logger } from '@libp2p/logger'
27
+ import type { MultiaddrConnection, Stream, Connection } from '@libp2p/interface/connection'
28
+ import type { Startable } from '@libp2p/interface/dist/src/startable'
29
+ import type { Metric, MetricGroup, StopTimer, Metrics, CalculatedMetricOptions, MetricOptions, Counter, CounterGroup, CalculateMetric } from '@libp2p/interface/metrics'
30
+
31
+ const log = logger('libp2p:simple-metrics')
32
+
33
+ class DefaultMetric implements Metric {
34
+ public value: number = 0
35
+
36
+ update (value: number): void {
37
+ this.value = value
38
+ }
39
+
40
+ increment (value: number = 1): void {
41
+ this.value += value
42
+ }
43
+
44
+ decrement (value: number = 1): void {
45
+ this.value -= value
46
+ }
47
+
48
+ reset (): void {
49
+ this.value = 0
50
+ }
51
+
52
+ timer (): StopTimer {
53
+ const start = Date.now()
54
+
55
+ return () => {
56
+ this.value = Date.now() - start
57
+ }
58
+ }
59
+ }
60
+
61
+ class DefaultGroupMetric implements MetricGroup {
62
+ public values: Record<string, number> = {}
63
+
64
+ update (values: Record<string, number>): void {
65
+ Object.entries(values).forEach(([key, value]) => {
66
+ this.values[key] = value
67
+ })
68
+ }
69
+
70
+ increment (values: Record<string, number | unknown>): void {
71
+ Object.entries(values).forEach(([key, value]) => {
72
+ this.values[key] = this.values[key] ?? 0
73
+ const inc = typeof value === 'number' ? value : 1
74
+
75
+ this.values[key] += Number(inc)
76
+ })
77
+ }
78
+
79
+ decrement (values: Record<string, number | unknown>): void {
80
+ Object.entries(values).forEach(([key, value]) => {
81
+ this.values[key] = this.values[key] ?? 0
82
+ const dec = typeof value === 'number' ? value : 1
83
+
84
+ this.values[key] -= Number(dec)
85
+ })
86
+ }
87
+
88
+ reset (): void {
89
+ this.values = {}
90
+ }
91
+
92
+ timer (key: string): StopTimer {
93
+ const start = Date.now()
94
+
95
+ return () => {
96
+ this.values[key] = Date.now() - start
97
+ }
98
+ }
99
+ }
100
+
101
+ export interface OnMetrics { (metrics: Record<string, any>): void }
102
+
103
+ export interface SimpleMetricsInit {
104
+ /**
105
+ * How often to invoke the onMetrics callback
106
+ */
107
+ intervalMs?: number
108
+
109
+ /**
110
+ * A callback periodically invoked with collected metrics
111
+ */
112
+ onMetrics: OnMetrics
113
+ }
114
+
115
+ class SimpleMetrics implements Metrics, Startable {
116
+ public metrics = new Map<string, DefaultMetric | DefaultGroupMetric | CalculateMetric>()
117
+ private started: boolean
118
+ private interval?: ReturnType<typeof setInterval>
119
+ private readonly intervalMs: number
120
+ private readonly onMetrics: OnMetrics
121
+
122
+ constructor (components: unknown, init: SimpleMetricsInit) {
123
+ this.started = false
124
+
125
+ this._emitMetrics = this._emitMetrics.bind(this)
126
+
127
+ this.intervalMs = init.intervalMs ?? 1000
128
+ this.onMetrics = init.onMetrics
129
+ }
130
+
131
+ isStarted (): boolean {
132
+ return this.started
133
+ }
134
+
135
+ start (): void {
136
+ this.started = true
137
+
138
+ this.interval = setInterval(this._emitMetrics, this.intervalMs)
139
+ }
140
+
141
+ stop (): void {
142
+ this.started = false
143
+
144
+ clearInterval(this.interval)
145
+ }
146
+
147
+ private _emitMetrics (): void {
148
+ void Promise.resolve().then(async () => {
149
+ const output: Record<string, any> = {}
150
+
151
+ for (const [name, metric] of this.metrics.entries()) {
152
+ if (metric instanceof DefaultMetric) {
153
+ output[name] = metric.value
154
+ } else if (metric instanceof DefaultGroupMetric) {
155
+ output[name] = metric.values
156
+ } else {
157
+ output[name] = await metric()
158
+ }
159
+ }
160
+
161
+ this.onMetrics(output)
162
+ })
163
+ .catch(err => {
164
+ log.error('could not invoke onMetrics callback', err)
165
+ })
166
+ }
167
+
168
+ trackMultiaddrConnection (maConn: MultiaddrConnection): void {
169
+
170
+ }
171
+
172
+ trackProtocolStream (stream: Stream, connection: Connection): void {
173
+
174
+ }
175
+
176
+ registerMetric (name: string, opts: CalculatedMetricOptions): void
177
+ registerMetric (name: string, opts?: MetricOptions): Metric
178
+ registerMetric (name: string, opts: any = {}): any {
179
+ if (name == null ?? name.trim() === '') {
180
+ throw new Error('Metric name is required')
181
+ }
182
+
183
+ if (opts?.calculate != null) {
184
+ // calculated metric
185
+ this.metrics.set(name, opts.calculate)
186
+ return
187
+ }
188
+
189
+ const metric = new DefaultMetric()
190
+ this.metrics.set(name, metric)
191
+
192
+ return metric
193
+ }
194
+
195
+ registerMetricGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
196
+ registerMetricGroup (name: string, opts?: MetricOptions): MetricGroup
197
+ registerMetricGroup (name: string, opts: any = {}): any {
198
+ if (name == null ?? name.trim() === '') {
199
+ throw new Error('Metric name is required')
200
+ }
201
+
202
+ if (opts?.calculate != null) {
203
+ // calculated metric
204
+ this.metrics.set(name, opts.calculate)
205
+ return
206
+ }
207
+
208
+ const metric = new DefaultMetric()
209
+ this.metrics.set(name, metric)
210
+
211
+ return metric
212
+ }
213
+
214
+ registerCounter (name: string, opts: CalculatedMetricOptions): void
215
+ registerCounter (name: string, opts?: MetricOptions): Counter
216
+ registerCounter (name: string, opts: any = {}): any {
217
+ if (name == null ?? name.trim() === '') {
218
+ throw new Error('Metric name is required')
219
+ }
220
+
221
+ if (opts?.calculate != null) {
222
+ // calculated metric
223
+ this.metrics.set(name, opts.calculate)
224
+ return
225
+ }
226
+
227
+ const metric = new DefaultGroupMetric()
228
+ this.metrics.set(name, metric)
229
+
230
+ return metric
231
+ }
232
+
233
+ registerCounterGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
234
+ registerCounterGroup (name: string, opts?: MetricOptions): CounterGroup
235
+ registerCounterGroup (name: string, opts: any = {}): any {
236
+ if (name == null ?? name.trim() === '') {
237
+ throw new Error('Metric name is required')
238
+ }
239
+
240
+ if (opts?.calculate != null) {
241
+ // calculated metric
242
+ this.metrics.set(name, opts.calculate)
243
+ return
244
+ }
245
+
246
+ const metric = new DefaultGroupMetric()
247
+ this.metrics.set(name, metric)
248
+
249
+ return metric
250
+ }
251
+ }
252
+
253
+ export function simpleMetrics (init: SimpleMetricsInit): (components: unknown) => Metrics {
254
+ return (components: unknown) => new SimpleMetrics(components, init)
255
+ }