Skip to content

feat: metrics instrumentation via typed EventEmitter#1751

Open
tomwilkie wants to merge 1 commit into
Koenkk:masterfrom
tomwilkie:metrics-instrumentation
Open

feat: metrics instrumentation via typed EventEmitter#1751
tomwilkie wants to merge 1 commit into
Koenkk:masterfrom
tomwilkie:metrics-instrumentation

Conversation

@tomwilkie

@tomwilkie tomwilkie commented Apr 26, 2026

Copy link
Copy Markdown

Summary

  • Replaces the injectable Metrics interface / setMetrics / noopMetrics pattern with a typed EventEmitter<MetricsEventMap> singleton exported as metrics
  • Instruments send methods (sendZclFrameToEndpoint, sendZdo, sendZclFrameToGroup, sendZclFrameToAll) directly in the controller via a shared instrumentSend helper, with timing and success/failure status
  • Adds metrics emission for adapter retries, ZDO responses, ZCL payload receives, and request queue length/duration
  • Enqueue timestamp (enqueuedAt) stored directly on Request alongside expires, replacing a parallel WeakMap on RequestQueue
  • Consumers subscribe via metrics.on("adapterSendZclUnicast", (data) => ...) etc. — metrics are always emitted unconditionally

Test plan

  • npm test — all 1744 tests pass
  • Subscribe to metrics.on(...) events and verify they fire on adapter sends

🤖 Generated with Claude Code

@Nerivec

Nerivec commented Apr 26, 2026

Copy link
Copy Markdown
Collaborator

This appears very targeted for Prometheus use (it is also not a no-op).
If we are to implement metrics in ZH, I think it should be something generic that any dependency-user (be it Z2M, iobroker, etc.) can use with the usual ZH events API (e.g. Z2M health system can make use of it, same as the rest).

We should also be very careful about possible performance degradation (be it small but widespread, or very large). Since this is "extra", it should have a very minimal impact overall.

@tomwilkie

Copy link
Copy Markdown
Author

Hi @Nerivec! Thank you for your feedback - I mostly just opened this PR to see if you were open to this and get feedback on the direction to go.

This appears very targeted for Prometheus use

I tried to make it as non-prometheus specific as I could - e.g. not encoding counter, gauge or histogram specifics, but instead specific events to measure. I'll admit I'm only familiar with Prometheus and OpenTelemetry, but if you have any other suggestions please let me know.

it is also not a no-op

I tried to make the calling sites as simple as possible (e.g. not having each one check if the metrics received is null but instead of having a no-op receiver) - but I could change that? WDYT?

I could also change the instrumentation of the adapter so that it only gets added if there is a metrics callback?

If we are to implement metrics in ZH, I think it should be something generic that any dependency-user (be it Z2M, iobroker, etc.) can use with the usual ZH events API (e.g. Z2M health system can make use of it, same as the rest).

100%. By "the usual ZH events API" do you mean e.g. making the Metrics interface follow the same pattern as ControllerEventMap and emit?

@tomwilkie tomwilkie changed the title feat: add injectable Metrics interface for instrumentation feat: metrics instrumentation via typed EventEmitter Apr 27, 2026
Comment thread src/adapter/metricsAdapter.ts Outdated
Comment thread src/controller/controller.ts Outdated
Comment thread src/utils/metrics.ts
requestQueueDuration: [data: RequestQueueDurationPayload];
}

export const metrics = new EventEmitter<MetricsEventMap>();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use of this in e.g. Z2M would require to import {metrics} from "zigbee-herdsman";, i.e. entirely separate from Controller (main entrypoint). I think that's fine for this purpose though.
Note: need to be sure we can easily add a mock in Z2M tests (similar to Controller), no matter the design we end up with.

Comment thread src/utils/metrics.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also identify available adapter's protocol-specific metrics.
I'm not sure if any other than ember currently have any, but currently we have a 3rd party tool to parse this from logs. Would be much better integrated with metrics directly.

Comment thread src/adapter/ember/adapter/emberAdapter.ts Outdated
@tomwilkie

Copy link
Copy Markdown
Author

Thanks for the review feedback! I'll work through it and integrate the changes into Koenkk/zigbee2mqtt#31645. I've got a build of the HA add on for testing in my local setup too, and need to build some dashboards etc.. so probably a few more days before I've got it working end to end.

@tomwilkie tomwilkie force-pushed the metrics-instrumentation branch from 94c8e6d to ee1dc06 Compare May 21, 2026 08:30
@tomwilkie tomwilkie force-pushed the metrics-instrumentation branch from ee1dc06 to 4de1f05 Compare June 25, 2026 09:47
@tomwilkie tomwilkie force-pushed the metrics-instrumentation branch from 4de1f05 to 3dc2f5d Compare July 2, 2026 12:00
Instrument adapter send operations and the controller to emit a single
discriminated `metric` event from the base Adapter class, exposing send
counts, retries and durations for observability. Adds src/utils/metrics.ts
with the MetricType definitions and full test coverage across the ember,
z-stack and zoh adapters, controller and device flows.

Signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
@tomwilkie tomwilkie force-pushed the metrics-instrumentation branch from 8258c2a to b6bdeea Compare July 3, 2026 11:46
@tomwilkie

Copy link
Copy Markdown
Author

I've got this all working end to end now, used in a PR in z2m (Koenkk/zigbee2mqtt#31645), built a add on container, deployed at home and built a nice set of dashboards in Grafana:

image

I think it could be ready for proper review? I've done a manual review myself. The thing to flag is none of the send metrics were working - intercepting send calls at the controller didn't work as the seems to be 20+ sites that call the adapater itself. I've tried to make the most minimal change, but putting the instrumentation in the base call, but would be keen on you thoughts. Thanks!

@tomwilkie tomwilkie marked this pull request as ready for review July 4, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants