Skip to content

Commit ee6dfe5

Browse files
authored
fix: add error metrics (#39)
Adds error event metrics similar to the TCP transport
1 parent ffb35af commit ee6dfe5

File tree

3 files changed

+108
-45
lines changed

3 files changed

+108
-45
lines changed

src/connection.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ interface QuicConnectionInit {
1111
logger: ComponentLogger
1212
direction: Direction
1313
metrics?: CounterGroup
14+
metricsPrefix?: string
1415
}
1516

1617
interface QuicConnectionEvents {
@@ -23,6 +24,9 @@ export class QuicConnection extends TypedEventEmitter<QuicConnectionEvents> impl
2324
readonly log: Logger
2425
readonly remoteAddr: Multiaddr
2526
readonly metrics?: CounterGroup
27+
readonly metricsPrefix: string
28+
29+
private remoteClosed?: boolean
2630

2731
timeline: MultiaddrConnectionTimeline = {
2832
open: Date.now()
@@ -38,9 +42,12 @@ export class QuicConnection extends TypedEventEmitter<QuicConnectionEvents> impl
3842
this.log = init.logger.forComponent(`libp2p:quic:connection:${this.#connection.id()}:${init.direction}`)
3943
this.remoteAddr = multiaddr(this.#connection.remoteMultiaddr())
4044
this.metrics = init.metrics
45+
this.metricsPrefix = init.metricsPrefix ?? ''
4146

4247
// close maconn when connection is closed by remote
4348
this.#connection.closed().then(() => {
49+
this.remoteClosed = true
50+
this.metrics?.increment({ [`${this.metricsPrefix}end`]: true })
4451
this.close()
4552
.catch(err => {
4653
this.abort(err)
@@ -59,7 +66,11 @@ export class QuicConnection extends TypedEventEmitter<QuicConnectionEvents> impl
5966

6067
this.timeline.close = Date.now()
6168
this.log('closed')
62-
this.metrics?.increment({ close: true })
69+
70+
if (this.remoteClosed !== true) {
71+
this.metrics?.increment({ [`${this.metricsPrefix}close`]: true })
72+
}
73+
6374
this.safeDispatchEvent('close')
6475
}
6576

@@ -72,7 +83,7 @@ export class QuicConnection extends TypedEventEmitter<QuicConnectionEvents> impl
7283

7384
this.timeline.close = Date.now()
7485
this.log('aborted - %e', err)
75-
this.metrics?.increment({ abort: true })
86+
this.metrics?.increment({ [`${this.metricsPrefix}abort`]: true })
7687
this.safeDispatchEvent('close')
7788
}
7889
}

src/listener.ts

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export interface QuicCreateListenerOptions extends CreateListenerOptions {
1313
}
1414

1515
export interface QuicListenerMetrics {
16-
events: CounterGroup
16+
events?: CounterGroup
17+
errors?: CounterGroup
1718
}
1819

1920
interface QuicListenerInit {
@@ -40,8 +41,9 @@ export class QuicListener extends TypedEventEmitter<ListenerEvents> implements L
4041
readonly init: QuicListenerInit
4142
readonly options: QuicCreateListenerOptions
4243
readonly log: Logger
43-
readonly metrics?: QuicListenerMetrics
44+
readonly metrics: QuicListenerMetrics
4445
private readonly shutdownController: AbortController
46+
private addr: string
4547

4648
state: QuicListenerState = { status: 'ready' }
4749

@@ -51,17 +53,36 @@ export class QuicListener extends TypedEventEmitter<ListenerEvents> implements L
5153
this.init = init
5254
this.options = init.options
5355
this.log = init.logger.forComponent('libp2p:quic:listener')
56+
this.addr = 'unknown'
5457

5558
this.shutdownController = new AbortController()
5659
setMaxListeners(Infinity, this.shutdownController.signal)
5760

58-
if (init.metrics != null) {
59-
this.metrics = {
60-
events: init.metrics.registerMetricGroup('libp2p_quic_listener_events_total', {
61-
label: 'address',
62-
help: 'Total count of QUIC listener events by type'
63-
})
61+
init.metrics?.registerMetricGroup('libp2p_quic_inbound_connections_total', {
62+
label: 'address',
63+
help: 'Current active connections in QUIC listener',
64+
calculate: () => {
65+
if (this.state.status !== 'listening') {
66+
return {
67+
[this.addr]: 0
68+
}
69+
}
70+
71+
return {
72+
[this.addr]: this.state.connections.size
73+
}
6474
}
75+
})
76+
77+
this.metrics = {
78+
events: init.metrics?.registerMetricGroup('libp2p_quic_listener_events_total', {
79+
label: 'address',
80+
help: 'Total count of QUIC listener events by type'
81+
}),
82+
errors: init.metrics?.registerMetricGroup('libp2p_quic_listener_errors_total', {
83+
label: 'address',
84+
help: 'Total count of QUIC listener errors by type'
85+
})
6586
}
6687

6788
this.log('new')
@@ -91,6 +112,7 @@ export class QuicListener extends TypedEventEmitter<ListenerEvents> implements L
91112
const addr = ma.nodeAddress()
92113
const controller = new AbortController()
93114
const listener = new napi.Server(this.#config, addr.address, addr.port)
115+
this.addr = `${addr.address}:${addr.port === 0 ? listener.port() : addr.port}`
94116

95117
// replace wildcard port with actual listening port
96118
if (addr.port === 0) {
@@ -143,15 +165,18 @@ export class QuicListener extends TypedEventEmitter<ListenerEvents> implements L
143165
try {
144166
const listenerPromise = this.state.listener.inboundConnection()
145167
listenerPromise
146-
.then(() => this.metrics?.events.increment({ connect: true }))
147-
.catch(() => this.metrics?.events.increment({ error: true }))
168+
.then(() => this.metrics.events?.increment({ [`${this.addr} connect`]: true }))
169+
.catch((err) => {
170+
this.log.error('%a error awaiting inbound connection - %e', listenAddr, err)
171+
this.metrics.events?.increment({ [`${this.addr} error`]: true })
172+
})
148173

149174
const connection = await raceSignal(listenerPromise, signal)
150175
this.onInboundConnection(connection).catch((e) => {
151-
this.log.error('%s error handling inbound connection', listenAddr.toString(), e)
176+
this.log.error('%a error handling inbound connection - %e', listenAddr, e)
152177
})
153178
} catch (e) {
154-
this.log.error('%s error accepting connection', listenAddr.toString(), e)
179+
this.log.error('%a error accepting connection - %e', listenAddr, e)
155180

156181
if (signal.aborted) {
157182
break
@@ -170,12 +195,19 @@ export class QuicListener extends TypedEventEmitter<ListenerEvents> implements L
170195
return
171196
}
172197

173-
const maConn = new QuicConnection({
174-
connection,
175-
logger: this.init.logger,
176-
direction: 'inbound',
177-
metrics: this.metrics?.events
178-
})
198+
let maConn: QuicConnection
199+
try {
200+
maConn = new QuicConnection({
201+
connection,
202+
logger: this.init.logger,
203+
direction: 'inbound',
204+
metrics: this.metrics?.events,
205+
metricsPrefix: `${this.addr} `
206+
})
207+
} catch (err) {
208+
this.metrics.errors?.increment({ [`${this.addr} inbound_to_connection`]: true })
209+
throw err
210+
}
179211

180212
try {
181213
await this.options.upgrader.upgradeInbound(maConn, {
@@ -196,6 +228,7 @@ export class QuicListener extends TypedEventEmitter<ListenerEvents> implements L
196228
}, { once: true })
197229
} catch (err) {
198230
this.log.error('%s error handling inbound connection', this.state.listenAddr.toString(), err)
231+
this.metrics.errors?.increment({ [`${this.addr} inbound_upgrade`]: true })
199232
maConn.abort(err as Error)
200233
}
201234
}

src/transport.ts

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import type { Connection, CounterGroup, Listener, Logger, MultiaddrFilter, Trans
1111
import type { Multiaddr } from '@multiformats/multiaddr'
1212

1313
interface QuicTransportMetrics {
14-
events: CounterGroup
14+
events?: CounterGroup
15+
errors?: CounterGroup
1516
}
1617

1718
export class QuicTransport implements Transport {
@@ -23,7 +24,7 @@ export class QuicTransport implements Transport {
2324

2425
readonly log: Logger
2526
readonly components: QuicComponents
26-
readonly metrics?: QuicTransportMetrics
27+
readonly metrics: QuicTransportMetrics
2728

2829
readonly #config: napi.QuinnConfig
2930

@@ -48,13 +49,15 @@ export class QuicTransport implements Transport {
4849
ip6: new napi.Client(this.#config, 1)
4950
}
5051

51-
if (this.components.metrics != null) {
52-
this.metrics = {
53-
events: this.components.metrics?.registerCounterGroup('libp2p_quic_dialer_events_total', {
54-
label: 'event',
55-
help: 'Total count of QUIC dialer events by type'
56-
})
57-
}
52+
this.metrics = {
53+
events: this.components.metrics?.registerCounterGroup('libp2p_quic_dialer_events_total', {
54+
label: 'event',
55+
help: 'Total count of QUIC dialer events by type'
56+
}),
57+
errors: this.components.metrics?.registerCounterGroup('libp2p_quic_dialer_errors_total', {
58+
label: 'event',
59+
help: 'Total count of QUIC dialer errors by type'
60+
})
5861
}
5962

6063
this.listenFilter = listenFilter
@@ -74,25 +77,41 @@ export class QuicTransport implements Transport {
7477

7578
const dialPromise = dialer.outboundConnection(addr.address, addr.port)
7679
dialPromise
77-
.then(() => this.metrics?.events.increment({ connect: true }))
78-
.catch(() => this.metrics?.events.increment({ error: true }))
80+
.then(() => this.metrics.events?.increment({ connect: true }))
81+
.catch(() => this.metrics.events?.increment({ error: true }))
7982
const connection = await dialPromise
8083

81-
const maConn = new QuicConnection({
82-
connection,
83-
logger: this.components.logger,
84-
direction: 'outbound',
85-
metrics: this.metrics?.events
86-
})
87-
return options.upgrader.upgradeOutbound(maConn, {
88-
skipEncryption: true,
89-
skipProtection: true,
90-
muxerFactory: new QuicStreamMuxerFactory({
84+
let maConn: QuicConnection
85+
86+
try {
87+
maConn = new QuicConnection({
9188
connection,
92-
logger: this.components.logger
93-
}),
94-
signal: options.signal
95-
})
89+
logger: this.components.logger,
90+
direction: 'outbound',
91+
metrics: this.metrics?.events
92+
})
93+
} catch (err) {
94+
this.metrics.errors?.increment({ outbound_to_connection: true })
95+
throw err
96+
}
97+
98+
try {
99+
this.log('new outbound connection %a', maConn.remoteAddr)
100+
return await options.upgrader.upgradeOutbound(maConn, {
101+
skipEncryption: true,
102+
skipProtection: true,
103+
muxerFactory: new QuicStreamMuxerFactory({
104+
connection,
105+
logger: this.components.logger
106+
}),
107+
signal: options.signal
108+
})
109+
} catch (err: any) {
110+
this.metrics.errors?.increment({ outbound_upgrade: true })
111+
this.log.error('error upgrading outbound connection - %e', err)
112+
maConn.abort(err)
113+
throw err
114+
}
96115
}
97116

98117
createListener (options: QuicCreateListenerOptions): Listener {

0 commit comments

Comments
 (0)