Skip to content
This repository has been archived by the owner on Jun 19, 2023. It is now read-only.

feat: export metrics #71

Merged
merged 21 commits into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3a4fc5a
feat: metrics
lightsofapollo Jan 4, 2023
0f8bfd3
add metrics mocks for future use cases
lightsofapollo Jan 4, 2023
fd785a4
Merge branch 'main' into feat/64/export-metrics-reland
ddimaria Jan 10, 2023
56cc35b
Merge branch 'main' into feat/64/export-metrics-reland
ddimaria Feb 24, 2023
28c9183
Fix aegir lints
ddimaria Feb 24, 2023
29a9269
chore: removed unused dev deps (#64)
maschad Mar 15, 2023
e120acb
Merge branch 'test' into feat/64/export-metrics-reland
maschad Mar 15, 2023
85ac0d6
chore: fix dep versions (#64)
maschad Mar 15, 2023
ef1dc6e
deps: upgraded deps to fix typing issue (#64)
maschad Mar 16, 2023
24f20c8
deps: upgraded deps to fix typing issue (#64)
maschad Mar 16, 2023
90b2d1d
Merge branch 'feat/64/export-metrics-reland' of https://github.com/li…
maschad Mar 16, 2023
f4a4bd1
Merge branch 'main' into feat/64/export-metrics-reland
ddimaria Mar 20, 2023
835efc4
Merge branch 'main' into feat/64/export-metrics-reland
maschad Apr 23, 2023
2a40a0d
feat: add metrics callbacks + update constructors (#64)
maschad Apr 23, 2023
334a67d
Merge branch 'main' into feat/64/export-metrics-reland
maschad Apr 27, 2023
1a883c1
deps: remove unused deps (#64)
maschad Apr 27, 2023
de5caeb
feat: added tracking for outgoing stream + peer connections, updated …
maschad Apr 27, 2023
9a93f48
Merge branch 'main' into feat/64/export-metrics-reland
maschad May 3, 2023
8b7b419
feat: pr minor adjustments (#64)
maschad May 4, 2023
ddda415
Merge branch 'main' into feat/64/export-metrics-reland
maschad May 9, 2023
4364358
chore: linting fixes
maschad May 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
"dependencies": {
"@chainsafe/libp2p-noise": "^11.0.0",
"@libp2p/interface-connection": "^3.0.2",
"@libp2p/interface-metrics": "^4.0.4",
"@libp2p/interface-peer-id": "^2.0.0",
"@libp2p/interface-stream-muxer": "^3.0.0",
"@libp2p/interface-transport": "^2.0.0",
Expand All @@ -157,13 +158,14 @@
"uint8arrays": "^4.0.2"
},
"devDependencies": {
"@libp2p/interface-mocks": "^9.0.0",
"@libp2p/peer-id-factory": "^2.0.0",
"@protobuf-ts/plugin": "^2.8.0",
"@protobuf-ts/protoc": "^2.8.0",
"aegir": "^38.1.6",
"@libp2p/interface-mocks": "^9.2.1",
"@libp2p/peer-id-factory": "^2.0.2",
"@protobuf-ts/plugin": "^2.8.2",
"@protobuf-ts/protoc": "^2.8.2",
"@types/uuid": "^9.0.1",
"aegir": "^38.1.7",
"eslint-plugin-etc": "^2.0.2",
"it-first": "^2.0.0",
"libp2p": "^0.41.0"
"sinon": "^15.0.2",
"sinon-ts": "^1.0.0"
}
}
12 changes: 12 additions & 0 deletions src/maconn.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { MultiaddrConnection, MultiaddrConnectionTimeline } from '@libp2p/interface-connection'
import type { CounterGroup } from '@libp2p/interface-metrics'
import { logger } from '@libp2p/logger'
import type { Multiaddr } from '@multiformats/multiaddr'
import type { Source, Sink } from 'it-stream-types'
Expand All @@ -22,6 +23,11 @@ interface WebRTCMultiaddrConnectionInit {
* Holds the relevant events timestamps of the connection
*/
timeline: MultiaddrConnectionTimeline

/**
* Optional metrics counter group for this connection
*/
metrics?: CounterGroup
}

export class WebRTCMultiaddrConnection implements MultiaddrConnection {
Expand All @@ -40,6 +46,11 @@ export class WebRTCMultiaddrConnection implements MultiaddrConnection {
*/
timeline: MultiaddrConnectionTimeline

/**
* Optional metrics counter group for this connection
*/
metrics?: CounterGroup

/**
* The stream source, a no-op as the transport natively supports multiplexing
*/
Expand All @@ -62,6 +73,7 @@ export class WebRTCMultiaddrConnection implements MultiaddrConnection {
}

this.timeline.close = new Date().getTime()
this.metrics?.increment({ close: true })
this.peerConnection.close()
}
}
34 changes: 30 additions & 4 deletions src/muxer.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
import type { Stream } from '@libp2p/interface-connection'
import type { CounterGroup } from '@libp2p/interface-metrics'
import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface-stream-muxer'
import type { Source, Sink } from 'it-stream-types'

import { WebRTCStream } from './stream.js'
import { nopSink, nopSource } from './util.js'

export interface DataChannelMuxerFactoryInit {
peerConnection: RTCPeerConnection
metrics?: CounterGroup
}

export class DataChannelMuxerFactory implements StreamMuxerFactory {
/**
* WebRTC Peer Connection
*/
private readonly peerConnection: RTCPeerConnection

/**
* Optional metrics counter group for all incoming/outgoing mux events.
*/
private readonly metrics?: CounterGroup

/**
* The string representation of the protocol, required by `StreamMuxerFactory`
*/
protocol: string = '/webrtc'

constructor (peerConnection: RTCPeerConnection) {
constructor (init: DataChannelMuxerFactoryInit) {
const { metrics, peerConnection } = init
this.peerConnection = peerConnection
if (metrics != null) {
this.metrics = metrics
}
}

createStreamMuxer (init?: StreamMuxerInit | undefined): StreamMuxer {
return new DataChannelMuxer(this.peerConnection, init)
return new DataChannelMuxer(this.peerConnection, this.metrics, init)
}
}

Expand All @@ -33,6 +48,12 @@ export class DataChannelMuxer implements StreamMuxer {
* WebRTC Peer Connection
*/
private readonly peerConnection: RTCPeerConnection

/**
* Optional metrics for this data channel muxer
*/
private readonly metrics?: CounterGroup

/**
* The protocol as represented in the multiaddress
*/
Expand Down Expand Up @@ -63,7 +84,7 @@ export class DataChannelMuxer implements StreamMuxer {
*/
sink: Sink<Uint8Array, Promise<void>> = nopSink

constructor (peerConnection: RTCPeerConnection, init?: StreamMuxerInit) {
constructor (peerConnection: RTCPeerConnection, metrics?: CounterGroup, init?: StreamMuxerInit) {
/**
* Initialized stream muxer
*/
Expand Down Expand Up @@ -93,6 +114,7 @@ export class DataChannelMuxer implements StreamMuxer {
})

if ((init?.onIncomingStream) != null) {
this.metrics?.increment({ incoming_stream: true })
init.onIncomingStream(stream)
}
}
Expand All @@ -101,6 +123,10 @@ export class DataChannelMuxer implements StreamMuxer {
newStream (): Stream {
// The spec says the label SHOULD be an empty string: https://github.com/libp2p/specs/blob/master/webrtc/README.md#rtcdatachannel-label
const channel = this.peerConnection.createDataChannel('')
const closeCb = (stream: WebRTCStream): void => {
this.metrics?.increment({ stream_end: true })
this.init?.onStreamEnd?.(stream)
}
const stream = new WebRTCStream({
channel,
stat: {
Expand All @@ -109,7 +135,7 @@ export class DataChannelMuxer implements StreamMuxer {
open: 0
}
},
closeCb: this.init?.onStreamEnd
closeCb
})

return stream
Expand Down
26 changes: 24 additions & 2 deletions src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { dataChannelError, inappropriateMultiaddr, unimplemented, invalidArgumen
import { WebRTCMultiaddrConnection } from './maconn.js'
import { DataChannelMuxerFactory } from './muxer.js'
import type { WebRTCDialOptions } from './options.js'
import type { CounterGroup, Metrics } from '@libp2p/interface-metrics'
import * as sdp from './sdp.js'
import { WebRTCStream } from './stream.js'
import { genUfrag } from './util.js'
Expand Down Expand Up @@ -43,16 +44,30 @@ export const CERTHASH_CODE: number = 466
// @TODO(ddimaria): seems like an unnessary abstraction, consider removing
maschad marked this conversation as resolved.
Show resolved Hide resolved
export interface WebRTCTransportComponents {
peerId: PeerId
metrics?: Metrics
}

export interface WebRTCMetrics {
dialerEvents: CounterGroup
}

export class WebRTCTransport implements Transport {
/**
* The peer for this transport
maschad marked this conversation as resolved.
Show resolved Hide resolved
*/
private readonly components: WebRTCTransportComponents
private readonly metrics?: WebRTCMetrics

constructor (components: WebRTCTransportComponents) {
this.components = components
if (components.metrics != null) {
this.metrics = {
dialerEvents: components.metrics.registerCounterGroup('libp2p_webrtc_dialer_events_total', {
label: 'event',
help: 'Total count of WebRTC dial events by type'
})
}
}
}

/**
Expand Down Expand Up @@ -123,6 +138,7 @@ export class WebRTCTransport implements Transport {
const handshakeTimeout = setTimeout(() => {
const error = `Data channel was never opened: state: ${handshakeDataChannel.readyState}`
log.error(error)
this.metrics?.dialerEvents.increment({ openError: true })
reject(dataChannelError('data', error))
}, HANDSHAKE_TIMEOUT_MS)

Expand All @@ -137,6 +153,8 @@ export class WebRTCTransport implements Transport {
const errorTarget = event.target?.toString() ?? 'not specified'
const error = `Error opening a data channel for handshaking: ${errorTarget}`
log.error(error)
// NOTE: We use unknown error here but this could potentially be considered a reset by some standards.
this.metrics?.dialerEvents.increment({ unknownError: true })
reject(dataChannelError('data', error))
}
})
Expand Down Expand Up @@ -189,10 +207,14 @@ export class WebRTCTransport implements Transport {
remoteAddr: ma,
timeline: {
open: (new Date()).getTime()
}
},
metrics: this.metrics?.dialerEvents
})

const muxerFactory = new DataChannelMuxerFactory(peerConnection)
const muxerFactory = new DataChannelMuxerFactory({
peerConnection,
metrics: this.metrics?.dialerEvents
})

// For outbound connections, the remote is expected to start the noise handshake.
// Therefore, we need to secure an inbound noise connection from the remote.
Expand Down
10 changes: 9 additions & 1 deletion test/maconn.browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,33 @@

import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
import type { CounterGroup } from '@libp2p/interface-metrics'
import { stubObject } from 'sinon-ts'
import { WebRTCMultiaddrConnection } from './../src/maconn.js'

describe('Multiaddr Connection', () => {
it('can open and close', async () => {
const peerConnection = new RTCPeerConnection()
peerConnection.createDataChannel('whatever', { negotiated: true, id: 91 })
const remoteAddr = multiaddr('/ip4/1.2.3.4/udp/1234/webrtc/certhash/uEiAUqV7kzvM1wI5DYDc1RbcekYVmXli_Qprlw3IkiEg6tQ')
const metrics = stubObject<CounterGroup>({
increment: () => {},
reset: () => {}
})
const maConn = new WebRTCMultiaddrConnection({
peerConnection,
remoteAddr,
timeline: {
open: (new Date()).getTime()
}
},
metrics
})

expect(maConn.timeline.close).to.be.undefined

await maConn.close()

expect(maConn.timeline.close).to.not.be.undefined
expect(metrics.increment.calledWith({ close: true })).to.be.true
})
})
46 changes: 45 additions & 1 deletion test/transport.browser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
/* eslint-disable @typescript-eslint/no-floating-promises */

import type { Metrics, Metric, MetricGroup, Counter, CounterGroup } from '@libp2p/interface-metrics'
import * as underTest from './../src/transport.js'
import { expectError } from './util.js'
import { UnimplementedError } from './../src/error.js'
import { stubObject } from 'sinon-ts'
import { mockUpgrader } from '@libp2p/interface-mocks'
import { CreateListenerOptions, symbol } from '@libp2p/interface-transport'
import { multiaddr, Multiaddr } from '@multiformats/multiaddr'
import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import * as sinon from 'sinon'
import { expect, assert } from 'aegir/chai'

function ignoredDialOption (): CreateListenerOptions {
Expand All @@ -16,10 +19,51 @@ function ignoredDialOption (): CreateListenerOptions {

describe('WebRTC Transport', () => {
let components: underTest.WebRTCTransportComponents
let metrics: Metrics

before(async () => {
metrics = stubObject<Metrics>({
maschad marked this conversation as resolved.
Show resolved Hide resolved
maschad marked this conversation as resolved.
Show resolved Hide resolved
trackMultiaddrConnection: (maConn) => {},
trackProtocolStream: (stream, connection) => {},
registerMetric: (name, options) => {
return stubObject<Metric>({
increment: () => {},
reset: () => {},
decrement: () => {},
update: () => {},
timer: () => {
return sinon.spy()
}
})
},
registerMetricGroup: (name, options) => {
return stubObject<MetricGroup>({
increment: () => {
},
reset: () => {},
decrement: () => {},
update: () => {},
timer: () => {
return sinon.spy()
}
})
},
registerCounter: (name, options) => {
return stubObject<Counter>({
increment: () => {},
reset: () => {}
})
},
registerCounterGroup: (name, options) => {
return stubObject<CounterGroup>({
increment: () => {},
reset: () => {}
})
}
})
components = {
peerId: await createEd25519PeerId()
peerId: await createEd25519PeerId(),
metrics
}
})

Expand Down