Skip to content

Performance Monitoring Guide

The built-in Performance Monitor captures latency metrics across core operation types: http, database, cache, queue, websocket, and custom spans you instrument manually.

Goals

  • Cheap, low-overhead aggregation
  • Fast identification of slow paths
  • Export-friendly structured reports

Initialization

Often implicitly initialized; explicit usage:

ts
import { PerformanceMonitor } from '@scpxl/nodejs-framework/performance';

const perf = PerformanceMonitor.getInstance({
  enabled: true,
  logSlowOperations: true,
  logAllOperations: false,
  thresholds: { http: 800, database: 400 },
});

Measuring Code

Manual Start/End

ts
const token = perf.startMeasure('list-users', 'database');
// ... query
perf.endMeasure(token);

Async Wrapper

ts
await perf.measureAsync({
  name: 'email-send',
  type: 'queue',
  fn: async () => sendEmail(data),
});

Custom Spans

Use custom for anything not covered:

ts
await perf.measureAsync({ name: 'image-resize', type: 'custom', fn: resizeFn });

Report Anatomy

perf.generateReport() returns:

jsonc
{
  "summary": {
    "counts": { "http": 42, "database": 18 },
    "averages": { "http": 120.5, "database": 45.1 },
    "percentiles": { "http": { "p50": 90, "p95": 250, "p99": 400 } },
  },
  "spans": [{ "name": "GET /users", "type": "http", "durationMs": 132, "ts": 1733333333 }],
}

Use generateFormattedReport('simple') for human-readable output.

Threshold Strategy

Set thresholds slightly tighter than your SLO target so warnings show early. Example mapping:

TypeSLO p95Threshold
http1000 ms800 ms
database500 ms400 ms
cache80 ms60 ms

Update dynamically:

ts
perf.setThresholds({ http: 700 });

Logging Slow Operations

If logSlowOperations is true, operations exceeding threshold emit a warn-level log with metadata. Keep this enabled in staging to tune before production.

Exporting Metrics

Periodic push:

ts
setInterval(() => {
  const { summary } = perf.generateReport();
  pushMetrics('perf_http_avg_ms', summary.averages.http);
  pushMetrics('perf_http_p95_ms', summary.percentiles?.http?.p95);
}, 15000);

Use tags/labels (service, version, env) when integrating with a TSDB.

Span Naming Conventions

ContextPatternExample
HTTPMETHOD pathTemplateGET /users/:id
DBaction:entityselect:user
QueuejobNameemail:send
Cacheop:keyPrefixget:user:
Customdomain:actionmedia:transcode

Consistent naming improves aggregation & dashboards.

Disabling Temporarily

ts
perf.setEnabled(false); // stops recording new spans

Memory Considerations

If span count grows large, you can periodically truncate detailed spans while preserving aggregated counters (future optimization may be available). For now, build exporters that reset state if necessary.

Dashboard Ideas

ChartMetric
HTTP p95perf_http_p95_ms
DB avgperf_db_avg_ms
Slow op countCount of spans > threshold grouped by type
Queue processing timeperf_queue_avg_ms

Troubleshooting

IssueCauseResolution
No spans recordedMonitor disabledEnable or ensure initialization
Missing percentilesInsufficient samplesIncrease traffic / wait
Excessive warn logsThresholds too lowTune upward gradually
Memory usage growthRetaining too many spansReduce retention / export & reset

Example Export + Reset Cycle

ts
setInterval(() => {
  const report = perf.generateReport();
  ship(report);
  perf.reset(); // if a reset method exists in implementation
}, 60000);

Next


Planned: OpenTelemetry bridge adapter for distributed traces.

Released under the ISC License.