Node.js Error Tracking: Exceptions with Stack Traces

Node.js Error Tracking: Debug Uncaught Exceptions with Contextual Stack Traces

Introduction

Uncaught exceptions are the silent killers of Node.js services. One missing try/catch can terminate a process, erase valuable debugging information, and increase mean time to recovery (MTTR). This guide walks you through a complete project—from idea to production—showing how to instrument your code, capture contextual stack traces, and leverage Lescopr’s observability platform to keep your service reliable and compliant.


Planning the Project

Defining Requirements

Before writing any code, list the concrete goals you want to achieve:

  • Capture every uncaught exception with full request context.
  • Correlate errors with SLA metrics such as response time and error rate.
  • Enable real‑time alerts for error spikes that breach your service‑level agreement.
  • Store traces in a GDPR‑compliant way, respecting user consent.

These requirements shape the architecture and the choice of tooling.

Choosing the Stack

For this tutorial we use a classic Express server running on Node.js 18. The stack includes:

  • Express – lightweight HTTP framework.
  • Lescopr APM SDK – provides automatic instrumentation and contextual stack traces.
  • Winston – structured logging that forwards to Lescopr.
  • Docker – containerised deployment for reproducibility.

Why Express? It is the most common entry point for Node.js APIs, making the example instantly relatable for most backend engineers.

Implementing Error Tracking

Installing Lescopr SDK

npm install @lescopr/apm

The SDK auto‑patches core modules (http, fs, net) and adds a global handler for uncaught exceptions.

Configuring Contextual Stack Traces

Lescopr enriches each stack frame with metadata such as request ID, user ID, and custom tags. Follow these steps:

  1. Initialize the SDK early in your entry file (app.js).
  2. Register a request‑level context using middleware.
  3. Add custom tags that capture business‑critical data (e.g., tenant ID, feature flag state).
const lescopr = require('@lescopr/apm');

lescopr.init({
  serviceName: 'order‑service',
  environment: process.env.NODE_ENV,
  captureUncaught: true,
});

app.use((req, res, next) => {
  lescopr.setContext({
    requestId: req.headers['x‑request‑id'] || lescopr.generateId(),
    userId: req.user?.id,
    route: req.path,
  });
  next();
});

Result: When an exception occurs, the trace sent to Lescopr includes every key‑value pair from the context, turning a raw stack trace into a searchable incident record.

Handling Uncaught Exceptions

Even with automatic capture, you should still guard critical async flows:

  • Wrap async/await calls in try/catch blocks.
  • Use process.on('unhandledRejection') to log promise rejections.
  • For legacy callbacks, employ domain or async‑hooks to propagate context.
process.on('unhandledRejection', (reason) => {
  lescopr.captureException(reason);
  console.error('Unhandled Rejection:', reason);
});

Testing and Observability

Simulating Errors

Create a test endpoint that deliberately throws an error:

app.get('/debug/crash', (req, res) => {
  JSON.parse('invalid‑json'); // triggers SyntaxError
});

Send a request with a custom header (X‑User‑Id) and watch Lescopr display the enriched stack trace.

Analyzing Traces

In the Lescopr UI, filter by serviceName:order‑service and error.type:SyntaxError. The panel shows:

  • Timestamp
  • Request ID
  • User ID (from the context)
  • Full stack with line numbers.

These details let you reproduce the bug locally in minutes instead of hours.

Setting Up Alerts

Configure an alert rule that triggers when the error rate exceeds 0.5 % over a five‑minute window:

  • Metric: error.count
  • Threshold: > 0.5%
  • Notification: Slack channel #sre‑alerts

This proactive stance reduces MTTR by notifying the on‑call engineer before the issue escalates.

Launching and Monitoring in Production

Deploying with CI/CD

Integrate the Lescopr SDK into your CI pipeline:

  • Run npm test with the SDK in dry‑run mode to ensure no runtime errors.
  • Use Docker multi‑stage builds to keep the final image lean.
  • Deploy to Kubernetes with a side‑car that forwards logs to Lescopr.

SLA Dashboards

Create a dashboard that visualises:

  • 99.9 % uptime (availability)
  • Average response time (latency)
  • Error rate (percentage of uncaught exceptions)

Link the dashboard to your SLA contract so stakeholders can verify compliance in real time.

Continuous Improvement

After each incident, perform a post‑mortem that records:

  • Root cause (e.g., missing validation).
  • Time to detect and resolve.
  • Action items (e.g., add stricter schema validation).

Feed these insights back into the codebase, and watch the error rate gradually decline.


Conclusion

By instrumenting your Node.js service with Lescopr, you turn opaque uncaught exceptions into rich, searchable events that include request context and precise stack information. This approach shortens MTTR, improves SLA adherence, and provides a solid foundation for continuous reliability engineering.

To go further, Lescopr's documentation covers step-by-step setup.


Internal Links