Skip to content

Commit

Permalink
feat: add diagnostics channel support
Browse files Browse the repository at this point in the history
Closes Integrate diagnostic channels #579
  • Loading branch information
dnlup committed Nov 2, 2023
1 parent ebd33a0 commit 97ca4b4
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 11 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Its API lets you access both computed and raw values, where possible.
+ [`gcAggregatedEntry.flags.allExternalMemory`](#gcaggregatedentryflagsallexternalmemory)
+ [`gcAggregatedEntry.flags.scheduleIdle`](#gcaggregatedentryflagsscheduleidle)
* [`doc.errors`](#docerrors)
* [Diagnostics Channel support](#diagnostics-channel-support)
- [License](#license)

<!-- tocstop -->
Expand Down Expand Up @@ -519,6 +520,25 @@ In the `errors` object are exported all the custom errors used by the module.
| `InvalidArgumentError` | `DOC_ERR_INVALID_ARG` | An invalid option or argument was used |
| `NotSupportedError` | `DOC_ERR_NOT_SUPPORTED` | A metric is not supported on the Node.js version used |

### Diagnostics Channel support

Node [diagnostics channel](https://nodejs.org/dist/latest-v20.x/docs/api/diagnostics_channel.html) are supported.

```js
const diagnosticsChannel = require('diagnostics_channel')
const doc = require('@dnlup/doc)
diagnostics_channel.subscribe(doc.constants.DOC_CHANNEL, s => {
console.log('A new instance', s)
})
diagnostics_channel.subscribe(doc.constants.DOC_SAMPLES_CHANNEL, s => {
console.log('A new sample', s)
})
doc()
```
## License
[ISC](./LICENSE)
21 changes: 15 additions & 6 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import { Sampler, SamplerOptions } from './types/sampler'
import {
Sampler,
SamplerOptions,
InstancesDiagnosticChannelHookData,
SamplesDiagnosticChannelHookData
} from './types/sampler'
import errors from './types/errors'
import { CPUMetric } from './types/cpuMetric'
import { EventLoopDelayMetric } from './types/eventLoopDelayMetric'
import { ResourceUsageMetric } from './types/resourceUsageMetric'
import { EventLoopUtilizationMetric } from './types/eventLoopUtilizationMetric'
import { GCEntry, GCAggregatedEntry, GCMetric } from './types/gcMetric'
import * as constants from './types/constants'

declare module 'doc' {

}
declare namespace doc {
export { errors }
export type {
Sampler, SamplerOptions,
Sampler,
SamplerOptions,
InstancesDiagnosticChannelHookData,
SamplesDiagnosticChannelHookData,
CPUMetric,
EventLoopDelayMetric,
ResourceUsageMetric,
EventLoopUtilizationMetric,
GCEntry, GCAggregatedEntry, GCMetric
GCEntry,
GCAggregatedEntry,
GCMetric,
constants
}
export const createSampler: (options?: SamplerOptions) => Sampler
export { createSampler as default }
Expand Down
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const Sampler = require('./lib/sampler')
const errors = require('./lib/errors')
const constants = require('./lib/constants')

function createSampler (options = {}) {
return new Sampler(options)
Expand All @@ -13,3 +14,4 @@ module.exports.default = createSampler

module.exports.Sampler = Sampler
module.exports.errors = errors
module.exports.constants = constants
4 changes: 4 additions & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use strict'

exports.DOC_CHANNEL = 'dnlup.doc.sampler'
exports.DOC_SAMPLES_CHANNEL = 'dnlup.doc.samples'
11 changes: 11 additions & 0 deletions lib/sampler.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const EventEmitter = require('events')
const diagnosticsChannel = require('diagnostics_channel')
const EventLoopDelayMetric = require('./eventLoopDelay')
const EventLoopUtilizationMetric = require('./eventLoopUtilization')
const CpuMetric = require('./cpu')
Expand All @@ -26,6 +27,10 @@ const {
kStart,
kStop
} = require('./symbols')
const { DOC_CHANNEL, DOC_SAMPLES_CHANNEL } = require('./constants')

const samples = diagnosticsChannel.channel(DOC_SAMPLES_CHANNEL)
const instances = diagnosticsChannel.channel(DOC_CHANNEL)

class Sampler extends EventEmitter {
constructor (options) {
Expand Down Expand Up @@ -57,6 +62,9 @@ class Sampler extends EventEmitter {
if (this[kOptions].autoStart) {
this.start()
}
if (instances.hasSubscribers) {
process.nextTick(() => instances.publish(this))
}
}

start () {
Expand Down Expand Up @@ -112,6 +120,9 @@ class Sampler extends EventEmitter {
this[kSample]()
this.emit('sample')
this[kReset]()
if (samples.hasSubscribers) {
samples.publish(this)
}
}

[kSample] () {
Expand Down
23 changes: 23 additions & 0 deletions test/diagnostics_channels.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict'

const { test } = require('tap')
const diagnosticsChannel = require('diagnostics_channel')
const doc = require('../')

test('diagnostics channel support', t => {
t.plan(2)
t.teardown(() => {
sampler.stop()
})
const onInstance = s => {
t.equal(s, sampler)
diagnosticsChannel.unsubscribe(doc.constants.DOC_CHANNEL, onInstance)
}
const onSample = (s) => {
t.equal(s, sampler)
diagnosticsChannel.unsubscribe(doc.constants.DOC_SAMPLES_CHANNEL, onSample)
}
diagnosticsChannel.subscribe(doc.constants.DOC_CHANNEL, onInstance)
diagnosticsChannel.subscribe(doc.constants.DOC_SAMPLES_CHANNEL, onSample)
const sampler = doc({ unref: false })
})
2 changes: 2 additions & 0 deletions types/constants.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const DOC_CHANNEL = 'dnlup.doc.sampler'
export const DOC_SAMPLES_CHANNEL = 'dnlup.doc.samples'
9 changes: 4 additions & 5 deletions types/sampler.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ import { GCMetric, GCMetricOptions } from './gcMetric'

declare interface SamplerOptions {
/**
* Sample interval (ms), each sampleInterval ms a data event is emitted.
* On Node 10 the default value is 500 while on Node >= 12 is 1000.
* Under the hood the package uses monitorEventLoopDelay where available to
* sample the event loop and this allows to increase the default sample
* interval on Node >= 12.
* Sample interval (ms), each `sampleInterval` ms a data event is emitted.
*/
sampleInterval?: number,

Expand Down Expand Up @@ -56,3 +52,6 @@ export class Sampler extends EventEmitter {
start(): void
stop(): void
}

export type InstancesDiagnosticChannelHookData = Sampler
export type SamplesDiagnosticChannelHookData = Sampler

0 comments on commit 97ca4b4

Please sign in to comment.