microsoft / microsoft/duroxide-node

Expose rich runtime metrics and structured logs to Node.js embedders

Open
#16 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
36
Forks
20
Avg merge
2d 11h
Merged PRs (30d)
4

Description

Summary

Expose Duroxide's provider-safe operational telemetry and structured native logs through duroxide-node so Node.js hosts can route them into their own OpenTelemetry pipeline.

The application must remain responsible for exporter choice, resource attributes, sampling, redaction, and backend configuration. Do not embed a vendor-specific exporter or send telemetry by default.

Core dependency: microsoft/duroxide#53

What exists today

duroxide-node 0.1.29 exposes:

  • Runtime.metricsSnapshot() with process-local cumulative counters;
  • Client.getSystemMetrics();
  • Client.getQueueDepths();
  • Client.getOrchestrationStats();
  • runtime options for log format, log level, service name, and service version.

Duroxide core records richer counters, gauges, and histograms through Rust's metrics facade and emits structured diagnostics through Rust tracing. Node hosts cannot currently receive most of those observations as typed values or route native log records through a Node OpenTelemetry LoggerProvider with active trace correlation.

Requested Node binding work

1. Bind the richer core observability contract

When microsoft/duroxide#53 defines the provider/runtime contract, expose it through typed JavaScript/TypeScript APIs without reconstructing data from provider tables.

The binding should preserve:

  • metric name/identity;
  • instrument kind: gauge, monotonic counter, interval delta, or histogram;
  • unit;
  • bounded labels/dimensions;
  • process-local versus provider-wide scope;
  • reset/generation identity;
  • unsupported-capability state;
  • histogram bucket boundaries/counts or another lossless aggregate representation.

Keep current APIs compatible where possible. A versioned Runtime.operationalMetricsSnapshot() / Client.getOperationalMetrics() pair is preferable to silently changing the meaning of existing fields.

2. Expose native structured log records

Add an opt-in structured event sink, for example a runtime option or subscription API, that delivers typed native log records to JavaScript.

Suggested record shape:

interface DuroxideLogRecord {
  timestampMs: number;
  severity: "trace" | "debug" | "info" | "warn" | "error";
  target: string;
  eventName?: string;
  message: string;
  fields: Record<string, string | number | boolean>;
  traceId?: string;
  spanId?: string;
}

Exact naming is open, but the contract should provide stable severity, target/event identity, bounded typed fields, and correlation when available.

Implementation requirements:

  • use a thread-safe, bounded Rust-to-JS delivery mechanism;
  • never block a Duroxide dispatcher, activity, provider operation, or shutdown on JavaScript log handling;
  • define overflow/drop behavior and expose a dropped-record counter;
  • allow filtering by severity/target before crossing N-API;
  • bound record/field sizes;
  • preserve JSON/stdout logging as an independent fallback;
  • make callback exceptions non-fatal to the runtime.

Do not expose arbitrary activity input/output, orchestration payloads, provider connection details, raw SQL, credentials, or exception objects by default.

3. Support host trace correlation

Provide a replay-safe way for a Node host to associate native Duroxide work with an application trace.

Desired behavior:

  • accept W3C traceparent/tracestate or an opaque host correlation context at a documented scheduling boundary;
  • surface that context on native log records and operational callbacks;
  • distinguish new execution from deterministic replay so replay does not create misleading wall-clock child spans;
  • preserve context across queue/provider persistence only where the core contract explicitly supports it;
  • avoid treating an orchestration's intentional durable wait as a single continuously active span.

This may require coordinated core changes in microsoft/duroxide#53. The Node binding should expose, not invent, the durable semantics.

4. Expose binding health

Make the host able to observe:

  • telemetry subscription enabled/disabled state;
  • records delivered and dropped;
  • last callback error;
  • snapshot generation/reset identity;
  • native runtime start and shutdown outcome;
  • in-flight callback count during drain.

PilotSwarm use case

PilotSwarm phase 1 can consume today's snapshots and ingest Duroxide JSON stdout without SDK changes. Phase 2 needs this issue to provide:

  • queue age and scheduled-to-start latency;
  • lock/lease acquisition, renewal, loss, and reclaim signals;
  • timer lateness, retry, replay, dispatcher, and provider-operation observations;
  • richly labeled counters and histogram aggregates across N-API;
  • structured native logs routed through PilotSwarm's OTEL LoggerProvider;
  • correlation between native Duroxide diagnostics and PilotSwarm turn traces.

Acceptance criteria

  1. TypeScript definitions expose the complete core observability contract from microsoft/duroxide#53 with units, scope, temporality, and reset semantics intact.
  2. Existing metricsSnapshot(), getSystemMetrics(), getQueueDepths(), and getOrchestrationStats() callers remain compatible or have a documented migration path.
  3. A Node test installs a structured log sink and receives typed records for runtime start, successful work, and classified failure.
  4. A slow or throwing callback does not block or fail orchestration/activity processing.
  5. Overflow produces explicit drop accounting and remains memory-bounded.
  6. Severity/target filtering happens before N-API delivery.
  7. A host correlation context can be attached and recovered according to documented replay semantics.
  8. No telemetry is exported and no callback is active unless the embedding application opts in.
  9. No secrets, raw provider details, or orchestration/activity payloads are exposed by default.
  10. Example code demonstrates adapting metrics to an OpenTelemetry MeterProvider and native records to an OpenTelemetry LoggerProvider without making OTEL a hard dependency of duroxide-node.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading microsoft/duroxide#53 and the existing Runtime.metricsSnapshot(), Client.getSystemMetrics(), Client.getQueueDepths(), and Client.getOrchestrationStats() entry points. Then inspect the TypeScript definitions, Node tests, and example code to map compatibility requirements. Done means the core contract, bounded opt-in log delivery, correlation semantics, health reporting, and acceptance tests are implemented without blocking runtime work or exporting telemetry by default.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js, rust, typescript
Domain
api, backend, observability
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.