Tutorials Logic, IN info@tutorialslogic.com

Node.js Performance and Observability

Production Evidence

Optimize from a measured service objective. Correlate request latency with event-loop delay, CPU, memory, dependency time, queue pressure, errors, and deployment revision before changing code.

Define the Performance Question

State the user-visible objective, workload, payload, concurrency, hardware, runtime version, and acceptable error rate. Throughput without latency distribution can hide slow users; average latency can hide severe p95 or p99 behavior.

Benchmark warm and cold behavior separately and compare one controlled change at a time. Use production-shaped data and protect the system under test from unrelated traffic. A microbenchmark is evidence only for the narrow operation it measures.

  • Measure percentiles and errors together.
  • Keep workload and environment reproducible.
  • Optimize the proven bottleneck.

Performance Marks and Measures

node:perf_hooks provides a monotonic performance clock, marks, measures, resource observations, and event-loop tools. Use performance.mark around meaningful operation boundaries and performance.measure to calculate duration without wall-clock changes.

Clear high-volume marks and measures after export. Instrumentation has cost, so choose stable operation names and sample detailed observations where needed. Do not place user IDs or unbounded URLs in metric names.

Measure an Application Operation

Measure an Application Operation
import { performance } from 'node:perf_hooks';

performance.mark('catalog:start');
await Promise.resolve();
performance.mark('catalog:end');
performance.measure('catalog.load', 'catalog:start', 'catalog:end');
const [entry] = performance.getEntriesByName('catalog.load');
console.log(entry.name, entry.duration >= 0);
performance.clearMarks();
performance.clearMeasures();
Output
catalog.load true

A stable operation name can be correlated with the surrounding request and dependency timings.

Event-Loop Delay and Utilization

monitorEventLoopDelay samples delay before callbacks can run; eventLoopUtilization estimates active versus idle loop time. High delay with CPU-heavy callbacks suggests blocking. High latency with low utilization may point to a dependency, queue, lock, connection pool, or unconsumed stream.

Sampling resolution and collection add overhead. Establish a healthy baseline per service and Node version, observe percentiles rather than one spike, and correlate with traffic and garbage collection before drawing conclusions.

CPU and Memory Diagnosis

Use CPU profiles to find hot stacks and flame graphs to understand repeated call paths. A profiler changes execution somewhat, so capture representative windows and confirm improvements with the original workload.

Track RSS, heap used and total, external memory, array buffers, allocation rate, and garbage-collection behavior. A heap snapshot can reveal retained object paths but may pause the process and contain sensitive data. Buffer and native memory can grow even when JavaScript heap looks stable.

Logs, Metrics, and Traces

Logs explain discrete events, metrics summarize numeric behavior, and traces connect work across boundaries. Use all three around the same stable service, route template, operation, status class, and deployment attributes. Avoid high-cardinality metric labels such as raw URL, email, or request ID.

Structured logs need timestamp, severity, event name, correlation ID, safe context, and error cause. Sampling must preserve rare failures and security events. Redact secrets at creation rather than relying only on a downstream filter.

Async Context Correlation

AsyncLocalStorage can carry request-scoped context across many promise and callback boundaries. Initialize it at the request boundary and read it in logging or tracing adapters instead of passing a request ID through every function.

Context is not business state or authorization proof. Some unusual callback integrations may lose context; test third-party boundaries. Disable or exit context when starting unrelated background work so request data does not leak into later operations.

Attach a Request ID with AsyncLocalStorage

Attach a Request ID with AsyncLocalStorage
import { AsyncLocalStorage } from 'node:async_hooks';
import { randomUUID } from 'node:crypto';

const requestContext = new AsyncLocalStorage();
function handle(request, response) {
  requestContext.run({ requestId: randomUUID() }, () => {
    console.log({ event: 'request.start', ...requestContext.getStore() });
    response.end('ok');
  });
}

The context stores correlation metadata; authorization remains explicit application data.

Production Diagnostics and Regression Gates

Diagnostic reports capture runtime, stack, heap, libuv handles, and system facts for severe incidents. Process reports, profiles, snapshots, and traces may contain paths, environment data, headers, or payload fragments; restrict generation, access, and retention.

Set release gates from budgets: response percentiles, error rate, event-loop delay, memory ceiling, startup time, and dependency latency. Load tests should include slow clients, large valid payloads, downstream delay, and graceful shutdown, not only a fast happy endpoint.

Before you move on

Performance Evidence Check

4 checks
  • I define a workload and service objective before optimizing.
  • I correlate event-loop, CPU, memory, and dependency evidence.
  • My telemetry uses bounded attributes and redaction.
  • I compare changes with repeatable regression gates.

Node JS Questions Learners Ask

Start with request latency percentiles, throughput, errors, event-loop delay, CPU, memory, and dependency latency under a defined workload.

No. RSS also includes Buffers, ArrayBuffers, native libraries, stacks, and allocator behavior. Compare heap and external memory before diagnosing.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.