This repository has been archived by the owner on Jun 26, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: return metrics objects from register instead of updating with …
…an options object (#310) Removes the component update methods in favour of registering metrics once with methods that return objects which can wrap metric gathering implementations. This lets us (for example) return a wrapped prometheus gauge and avoid concatenating strings, etc when extracting metrics. Register methods for metrics (prometheus gauges) and counters are present, extra `registerHistogram` etc methods can be added at a later date when required. ## Single metrics Before: ```js metrics.updateComponentMetric({ system: 'libp2p', component: 'connection-manager', metric: 'incoming-connections', value: 5 }) ``` After: ```js const metric = metrics.registerMetric('libp2p_connection_manager_incoming_connections') // call repeatedly metric.update(5) // or metric.increment() // or metric.increment(5) // or const stopTimer = metric.timer() // later stopTimer() // or metric.reset() // etc ``` ## Metric groups Before: ```js metrics.updateComponentMetric({ system: 'libp2p', component: 'connection-manager', metric: 'connections', value: { incoming: 5, outgoing: 10 } }) ``` After: ```js const metric = metrics.registerMetricGroup('libp2p_connection_manager_connections') // call repeatedly metric.update({ incoming: 5, outgoing: 10 }) ``` ## Extracting metrics This has got much simpler, this example is from an upcoming PR to js-ipfs: ```js import client from 'prom-client' // Endpoints for handling debug metrics export default [{ method: 'GET', path: '/debug/metrics/prometheus', /** * @param {import('../../types').Request} request * @param {import('@hapi/hapi').ResponseToolkit} h */ async handler (request, h) { if (!process.env.IPFS_MONITORING) { throw Boom.notImplemented('Monitoring is disabled. Enable it by setting environment variable IPFS_MONITORING') } return h.response(await client.register.metrics()) .type(client.register.contentType) } }] ``` BREAKING CHANGE: the global/per-peer moving average tracking has been removed from the interface as it's expensive and requires lots of timers - this functionality can be replicated by implementations if it's desirable. It's better to have simple counters instead and let an external system like Prometheus or Graphana calculate the values over time
- Loading branch information
1 parent
9ce0f8f
commit 3b106ce
Showing
7 changed files
with
295 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,197 +1,187 @@ | ||
import type { PeerId } from '@libp2p/interface-peer-id' | ||
import type { Duplex } from 'it-stream-types' | ||
|
||
export interface MetricsInit { | ||
enabled: boolean | ||
computeThrottleMaxQueueSize: number | ||
computeThrottleTimeout: number | ||
movingAverageIntervals: number[] | ||
maxOldPeersRetention: number | ||
} | ||
|
||
export interface MovingAverage { | ||
variance: number | ||
movingAverage: number | ||
deviation: number | ||
forecast: number | ||
|
||
push: (time: number, value: number) => void | ||
} | ||
|
||
export interface MovingAverages { | ||
dataReceived: MovingAverage[] | ||
dataSent: MovingAverage[] | ||
} | ||
|
||
export interface TransferStats { | ||
dataReceived: bigint | ||
dataSent: bigint | ||
} | ||
import type { MultiaddrConnection, Stream, Connection } from '@libp2p/interface-connection' | ||
|
||
export interface Stats { | ||
/** | ||
* Create tracked metrics with these options. Loosely based on the | ||
* interfaces exposed by the prom-client module | ||
*/ | ||
export interface MetricOptions { | ||
/** | ||
* Returns a clone of the current stats. | ||
* Optional label for the metric | ||
*/ | ||
getSnapshot: () => TransferStats | ||
label?: string | ||
|
||
/** | ||
* Returns a clone of the internal movingAverages | ||
* Optional help for the metric | ||
*/ | ||
getMovingAverages: () => MovingAverages | ||
help?: string | ||
} | ||
|
||
/** | ||
* A function that returns a tracked metric which may be expensive | ||
* to calculate so it is only invoked when metrics are being scraped | ||
*/ | ||
export type CalculateMetric<T = number | bigint> = (() => T) | (() => Promise<T>) | ||
|
||
/** | ||
* Create tracked metrics that are expensive to calculate by passing | ||
* a function that is only invoked when metrics are being scraped | ||
*/ | ||
export interface CalculatedMetricOptions<T = number | bigint> extends MetricOptions { | ||
/** | ||
* Pushes the given operation data to the queue, along with the | ||
* current Timestamp, then resets the update timer. | ||
* An optional function invoked to calculate the component metric instead of | ||
* using `.update`, `.increment`, and `.decrement` | ||
*/ | ||
push: (counter: string, inc: number) => void | ||
calculate: CalculateMetric<T> | ||
} | ||
|
||
export interface TrackStreamOptions { | ||
/** | ||
* A duplex iterable stream | ||
*/ | ||
stream: Duplex<{ byteLength: number }, any> | ||
/** | ||
* Call this function to stop the timer returned from the `.timer` method | ||
* on the metric | ||
*/ | ||
export interface StopTimer { (): void } | ||
|
||
/** | ||
* A tracked metric loosely based on the interfaces exposed by the | ||
* prom-client module | ||
*/ | ||
export interface Metric { | ||
/** | ||
* The id of the remote peer that's connected | ||
* Update the stored metric to the passed value | ||
*/ | ||
remotePeer: PeerId | ||
update: (value: number | bigint) => void | ||
|
||
/** | ||
* The protocol the stream is running | ||
* Increment the metric by the passed value or 1 | ||
*/ | ||
protocol?: string | ||
} | ||
increment: (value?: number | bigint) => void | ||
|
||
export interface StreamMetrics { | ||
/** | ||
* Returns the global `Stats` object | ||
* Decrement the metric by the passed value or 1 | ||
*/ | ||
getGlobal: () => Stats | ||
decrement: (value?: number | bigint) => void | ||
|
||
/** | ||
* Returns a list of `PeerId` strings currently being tracked | ||
* Reset this metric to its default value | ||
*/ | ||
getPeers: () => string[] | ||
reset: () => void | ||
|
||
/** | ||
* Returns the `Stats` object for the given `PeerId` whether it | ||
* is a live peer, or in the disconnected peer LRU cache. | ||
* Start a timed metric, call the returned function to | ||
* stop the timer | ||
*/ | ||
forPeer: (peerId: PeerId) => Stats | undefined | ||
timer: () => StopTimer | ||
} | ||
|
||
/** | ||
* A group of related metrics loosely based on the interfaces exposed by the | ||
* prom-client module | ||
*/ | ||
export interface MetricGroup { | ||
/** | ||
* Returns a list of all protocol strings currently being tracked. | ||
* Update the stored metric group to the passed value | ||
*/ | ||
getProtocols: () => string[] | ||
update: (values: Record<string, number | bigint>) => void | ||
|
||
/** | ||
* Returns the `Stats` object for the given `protocol` | ||
* Increment the metric group keys by the passed number or | ||
* any non-numeric value to increment by 1 | ||
*/ | ||
forProtocol: (protocol: string) => Stats | undefined | ||
increment: (values: Record<string, number | bigint | unknown>) => void | ||
|
||
/** | ||
* Should be called when all connections to a given peer | ||
* have closed. The `Stats` collection for the peer will | ||
* be stopped and moved to an LRU for temporary retention. | ||
* Decrement the metric group keys by the passed number or | ||
* any non-numeric value to decrement by 1 | ||
*/ | ||
onPeerDisconnected: (peerId: PeerId) => void | ||
decrement: (values: Record<string, number | bigint | unknown>) => void | ||
|
||
/** | ||
* Replaces the `PeerId` string with the given `peerId`. | ||
* If stats are already being tracked for the given `peerId`, the | ||
* placeholder stats will be merged with the existing stats. | ||
* Reset the passed key in this metric group to its default value | ||
* or all keys if no key is passed | ||
*/ | ||
updatePlaceholder: (placeholder: PeerId, peerId: PeerId) => void | ||
reset: () => void | ||
|
||
/** | ||
* Tracks data running through a given Duplex Iterable `stream`. If | ||
* the `peerId` is not provided, a placeholder string will be created and | ||
* returned. This allows lazy tracking of a peer when the peer is not yet known. | ||
* When the `PeerId` is known, `Metrics.updatePlaceholder` should be called | ||
* with the placeholder string returned from here, and the known `PeerId`. | ||
* Start a timed metric for the named key in the group, call | ||
* the returned function to stop the timer | ||
*/ | ||
trackStream: (data: TrackStreamOptions) => void | ||
timer: (key: string) => StopTimer | ||
} | ||
|
||
/** | ||
* Used to update a tracked metric. Value can either be a number, an object containing | ||
* key/value pairs or an (optionally async) function to return a number or an object of | ||
* key/value pairs. | ||
* A tracked counter loosely based on the Counter interface exposed | ||
* by the prom-client module - counters are metrics that only go up | ||
*/ | ||
export interface ComponentMetricsUpdate { | ||
/** | ||
* Name of the system, e.g. libp2p, ipfs, etc | ||
*/ | ||
system: string | ||
|
||
/** | ||
* Name of the system component that contains the metric | ||
*/ | ||
component: string | ||
|
||
export interface Counter { | ||
/** | ||
* Name of the metric being tracked | ||
* Increment the metric by the passed value or 1 | ||
*/ | ||
metric: string | ||
increment: (value?: number | bigint) => void | ||
|
||
/** | ||
* The value or function to calculate the value | ||
* Reset this metric to its default value | ||
*/ | ||
value: ComponentMetric | CalculateComponentMetric | ||
reset: () => void | ||
} | ||
|
||
/** | ||
* A group of tracked counters loosely based on the Counter interface | ||
* exposed by the prom-client module - counters are metrics that only | ||
* go up | ||
*/ | ||
export interface CounterGroup { | ||
/** | ||
* Optional label for the metric | ||
* Increment the metric group keys by the passed number or | ||
* any non-numeric value to increment by 1 | ||
*/ | ||
label?: string | ||
increment: (values: Record<string, number | bigint | unknown>) => void | ||
|
||
/** | ||
* Optional help for the metric | ||
* Reset the passed key in this metric group to its default value | ||
* or all keys if no key is passed | ||
*/ | ||
help?: string | ||
reset: () => void | ||
} | ||
|
||
export type ComponentMetric = number | ComponentMetricsGroup | ||
|
||
/** | ||
* Used to group related metrics together by label and value | ||
* The libp2p metrics tracking object. This interface is only concerned | ||
* with the collection of metrics, please see the individual implementations | ||
* for how to extract metrics for viewing. | ||
*/ | ||
export type ComponentMetricsGroup = Record<string, number> | ||
|
||
/** | ||
* Used to calculate metric values dynamically | ||
*/ | ||
export interface CalculateComponentMetric { (): Promise<ComponentMetric> | ComponentMetric } | ||
|
||
export interface TrackedMetric { | ||
export interface Metrics { | ||
/** | ||
* In systems that support them, this label can help make graphs more interpretable | ||
* Track a newly opened multiaddr connection | ||
*/ | ||
label?: string | ||
trackMultiaddrConnection: (maConn: MultiaddrConnection) => void | ||
|
||
/** | ||
* In systems that support them, this help text can help make graphs more interpretable | ||
* Track a newly opened protocol stream | ||
*/ | ||
help?: string | ||
trackProtocolStream: (stream: Stream, connection: Connection) => void | ||
|
||
/** | ||
* A function that returns a value or a group of values | ||
* Register an arbitrary metric. Call this to set help/labels for metrics | ||
* and update/increment/decrement/etc them by calling methods on the returned | ||
* metric object | ||
*/ | ||
calculate: CalculateComponentMetric | ||
} | ||
registerMetric: ((name: string, options?: MetricOptions) => Metric) & ((name: string, options: CalculatedMetricOptions) => void) | ||
|
||
export interface ComponentMetricsTracker { | ||
/** | ||
* Returns tracked metrics key by system, component, metric, value | ||
* Register a a group of related metrics. Call this to set help/labels for | ||
* groups of related metrics that will be updated with by calling `.update`, | ||
* `.increment` and/or `.decrement` methods on the returned metric group object | ||
*/ | ||
getComponentMetrics: () => Map<string, Map<string, Map<string, TrackedMetric>>> | ||
registerMetricGroup: ((name: string, options?: MetricOptions) => MetricGroup) & ((name: string, options: CalculatedMetricOptions<Record<string, number | bigint>>) => void) | ||
|
||
/** | ||
* Update the stored metric value for the given system and component | ||
* Register an arbitrary counter. Call this to set help/labels for counters | ||
* and increment them by calling methods on the returned counter object | ||
*/ | ||
updateComponentMetric: (data: ComponentMetricsUpdate) => void | ||
} | ||
|
||
export interface Metrics extends StreamMetrics, ComponentMetricsTracker { | ||
registerCounter: ((name: string, options?: MetricOptions) => Counter) & ((name: string, options: CalculatedMetricOptions) => void) | ||
|
||
/** | ||
* Register a a group of related counters. Call this to set help/labels for | ||
* groups of related counters that will be updated with by calling the `.increment` | ||
* method on the returned counter group object | ||
*/ | ||
registerCounterGroup: ((name: string, options?: MetricOptions) => CounterGroup) & ((name: string, options: CalculatedMetricOptions<Record<string, number | bigint>>) => void) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.