first commit
This commit is contained in:
104
docs/networking/events/broadcast-channel/src/otel/metrics.md
Normal file
104
docs/networking/events/broadcast-channel/src/otel/metrics.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# otel/metrics
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module declares the six OpenTelemetry counter instruments emitted by
|
||||
`@suaveplan/broadcast-channel`. Every counter is registered against the meter
|
||||
`@suaveplan/broadcast-channel@0.1.0` so a single global `MeterProvider`
|
||||
configuration in the host application picks all of them up. The instruments
|
||||
close the observability gap the enterprise audit flagged ("No structured
|
||||
logging / OTel counters"): each lifecycle event a channel observes is
|
||||
counted, attributed by channel name and either message type or drop reason.
|
||||
|
||||
The `refreshBroadcastChannelMeters()` function re-resolves every instrument
|
||||
against the current global `MeterProvider`. Tests call it after wiring the
|
||||
in-memory exporter from `tests/setup/polyfills.ts` so subsequent record calls
|
||||
land on the exporter rather than the noop meter resolved at module load.
|
||||
|
||||
## Features
|
||||
|
||||
- Six counters: `channel.message.{sent,received,dropped}`, `channel.tab.{joined,left}`, `channel.leader.elected`.
|
||||
- Stable attribute shape: `{ channel }` plus `{ type }` or `{ reason }`.
|
||||
- `refreshBroadcastChannelMeters()` for test rebinding.
|
||||
- No span / tracer surface — counters only, by design.
|
||||
- Module-load-safe under the noop meter when no `MeterProvider` is configured.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { metrics } from "@opentelemetry/api";
|
||||
import { MeterProvider } from "@opentelemetry/sdk-metrics";
|
||||
import {
|
||||
refreshBroadcastChannelMeters,
|
||||
TypedBroadcastChannel,
|
||||
nativeChannelFactory,
|
||||
} from "@suaveplan/broadcast-channel";
|
||||
|
||||
metrics.setGlobalMeterProvider(new MeterProvider({ readers: [] }));
|
||||
refreshBroadcastChannelMeters();
|
||||
|
||||
const channel = new TypedBroadcastChannel({
|
||||
config: { name: "app-bus" },
|
||||
factory: nativeChannelFactory,
|
||||
});
|
||||
channel.open();
|
||||
channel.send("greet", "hi"); // increments channel.message.sent
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { metrics } from "@opentelemetry/api";
|
||||
import {
|
||||
AggregationTemporality,
|
||||
InMemoryMetricExporter,
|
||||
MeterProvider,
|
||||
PeriodicExportingMetricReader,
|
||||
} from "@opentelemetry/sdk-metrics";
|
||||
import { refreshBroadcastChannelMeters } from "@suaveplan/broadcast-channel";
|
||||
|
||||
const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE);
|
||||
const reader = new PeriodicExportingMetricReader({
|
||||
exporter,
|
||||
exportIntervalMillis: 60_000,
|
||||
});
|
||||
metrics.setGlobalMeterProvider(new MeterProvider({ readers: [reader] }));
|
||||
refreshBroadcastChannelMeters();
|
||||
|
||||
// Later, in a test assertion:
|
||||
const collected = exporter.getMetrics();
|
||||
const names = collected.flatMap((rm) =>
|
||||
rm.scopeMetrics.flatMap((sm) => sm.metrics.map((m) => m.descriptor.name))
|
||||
);
|
||||
expect(names).toContain("channel.message.sent");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `refreshBroadcastChannelMeters(): void` — re-resolve every instrument against the current global `MeterProvider`.
|
||||
- `recordMessageSent(type: string, channelName: string): void`
|
||||
- `recordMessageReceived(type: string, channelName: string): void`
|
||||
- `recordMessageDropped(reason: "malformed", channelName: string): void`
|
||||
- `recordTabJoined(channelName: string): void`
|
||||
- `recordTabLeft(channelName: string): void`
|
||||
- `recordLeaderElected(channelName: string): void`
|
||||
|
||||
Each `record*` helper calls `.add(1, attrs)` on the corresponding counter.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The instruments are stored in module-local `let` bindings so
|
||||
`refreshBroadcastChannelMeters` can rebind them without forcing callers to
|
||||
hold a stable reference. This mirrors the pattern used by
|
||||
`@suaveplan/slack-client`: at module load the meter is whatever the global
|
||||
provider returns (typically the noop provider, in which case `.add()` is a
|
||||
no-op), and the explicit refresh after `setGlobalMeterProvider` swaps in
|
||||
real instruments. The attribute keys (`channel`, `type`, `reason`) are kept
|
||||
short to minimise cardinality blow-up; consumer dashboards group on
|
||||
`channel` first to slice per logical bus. No histograms are exposed — the
|
||||
package has no meaningful latency axis (`send` is synchronous, dispatch is
|
||||
in-process) so a histogram would be empty noise.
|
||||
Reference in New Issue
Block a user