Trasys
Node.js SDK

Custom Metrics

Record custom counters, gauges, and histograms with the monitor object — every call is automatically tagged with the active trace, user, and tenant ID.

Custom Metrics

Alongside the metrics the SDK captures automatically (HTTP latency, DB query time, AI token usage), you can record your own business metrics through the monitor object — the same one returned by createSdk().

const { monitor } = require('./trasys');

monitor.counter('orders.created', 1, { plan: 'pro' });
monitor.gauge('queue.depth', pendingJobs.length, { queue: 'emails' });
monitor.histogram('file.upload.bytes', file.size, { type: file.mimetype });

All three share the same shape: (name, value?, tags?, options?).

monitor.counter(name, value?, tags?)

Increments a running total. value defaults to 1, so monitor.counter('payments.processed') is a valid one-argument call for simple "count this happened" events.

monitor.counter('payments.processed', 1, { currency: 'INR' });
monitor.counter('webhook.delivery.failed'); // value defaults to 1

monitor.gauge(name, value, tags?)

Records a point-in-time snapshot — the current value of something, not a running total. Use this for things like queue depth or active connection count, where the latest value is what matters, not the sum of every value you've ever recorded.

monitor.gauge('queue.depth', currentDepth);
monitor.gauge('active_connections', pool.totalCount);

Internally this uses an OTel ObservableGauge — calling monitor.gauge() just stores the latest value; the SDK reports it on the next metrics export cycle (transport.metricExportIntervalMs, default every 15 seconds — see Transport). If you call it multiple times between export cycles, only the most recent value per unique tag combination is reported.

monitor.histogram(name, value, tags?)

Records a single observation into a distribution — use this for anything you want percentiles on, like request size or processing duration.

monitor.histogram('api.latency.ms', responseTime);
monitor.histogram('file.upload.bytes', file.size, { type: file.mimetype });

Histograms use fixed bucket boundaries tuned for millisecond-scale durations: 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 10000. If your values are on a very different scale (bytes, dollars), the percentiles are still computed correctly — the buckets just won't be as finely tuned.

Automatic trace correlation

Every monitor.* call automatically attaches trace_id, user_id, and tenant_id tags (when available from the active request context) on top of whatever tags you pass — no extra code needed to correlate a custom metric back to the request that produced it.

monitor.counter('orders.created', 1, { plan: 'pro' });
// recorded tags: { plan: 'pro', trace_id: '...', user_id: '...', tenant_id: '...' }

Instrument reuse

Calling monitor.counter('orders.created') repeatedly reuses the same underlying OTel instrument rather than creating a new one each time — OTel logs a warning if you create two instruments with the same name, so this is handled for you automatically.


Next steps

  • Sampling — control which requests get recorded, independent of custom metrics
  • TQL — query custom metrics with FROM metrics
  • Transport — how often metrics are flushed and what happens when the network is down

On this page