report

Diagnostic Report

Purpose: Collects and saves information to help diagnose problems in running scripts or programs.

Sections:

  1. Header

  • Basic information: report version, event (e.g., exception), trigger (e.g., unhandled error), filename, dump time, process ID (PID), command line, Node.js version, OS information (name, version, etc.).

  • Component versions: Node.js, V8, UV, Zlib, OpenSSL, and other modules.

  • CPU, network, and host data: CPU speed, network interface details, host machine name, etc.

Example:

{
  "header": {
    "trigger": "Exception",
    "processId": 1234,
    "nodejsVersion": "12.0.0",
    "osName": "Linux",
    "osVersion": "3.10.0-862.el7.x86_64",
    "cpu": [
      {
        "model": "Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz"
      }
    ]
  }
}
  1. JavaScript Stack

  • Error message: The actual error message encountered.

  • Stack trace: A list of functions that were called, leading to the error.

Example:

{
  "javascriptStack": {
    "message": "Error: *** test-exception.js: throwing uncaught Error",
    "stack": [
      "at myException (/home/nodeuser/project/node/test/report/test-exception.js:9:11)",
      "at Object.<anonymous> (/home/nodeuser/project/node/test/report/test-exception.js:12:3)"
    ]
  }
}
  1. Native Stack

  • Native call stack: Shows the C++ functions that were called during the error.

Example:

{
  "nativeStack": [
    {
      "symbol": "report::GetNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, v8::Local<v8::String>, std::ostream&) [./node]"
    },
    {
      "symbol": "report::GetReport(v8::FunctionCallbackInfo<v8::Value> const&) [./node]"
    }
  ]
}
  1. JavaScript Heap

  • Memory statistics: Total, used, and available memory, garbage collection information, and details about different heap spaces (e.g., read-only, old, code).

Example:

{
  "javascriptHeap": {
    "totalMemory": 5660672,
    "usedMemory": 4816432,
    "heapSpaces": {
      "read_only_space": {
        "used": 30504
      },
      "new_space": {
        "used": 985496
      },
      "old_space": {
        "used": 1725488
      }
    }
  }
}
  1. Resource Usage

  • System resource information: Memory (RSS), CPU usage, page faults, file system activity.

Example:

{
  "resourceUsage": {
    "rss": "35766272",
    "userCpuSeconds": 0.040072,
    "cpuConsumptionPercent": 5.6101
  }
}
  1. UV Thread Resource Usage

  • Resource usage for libuv threads: Similar to the previous section, but specifically for libuv threads.

Example:

{
  "uvThreadResourceUsage": {
    "userCpuSeconds": 0.039843,
    "cpuConsumptionPercent": 5.578
  }
}
  1. Node.js UV Handles

  • List of UV handles: Different types of handles, such as async, timers, checks, and signals, along with their state and details.

Example:

{
  "uvthreadResourceUsage": [
    {
      "type": "async",
      "is_active": true,
      "is_referenced": false
    },
    {
      "type": "timer",
      "is_active": false,
      "is_referenced": false
    }
  ]
}
  1. Workers

  • Web Workers: If any worker threads were created during the script execution.

Example:

{
  "workers": []
}
  1. Environment Variables

  • List of environment variables: Variables passed to the script from the operating system (e.g., PATH, USER, HOME).

Example:

{
  "environmentVariables": {
    "PATH": "/home/nodeuser/project/node:/opt/rh/devtoolset-3/root/usr/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin",
    "USER": "nodeuser"
  }
}
  1. User Limits

  • System resource limits: Soft and hard limits for various resources (e.g., maximum memory size, CPU time, file size).

Example:

{
  "userLimits": {
    "max_memory_size_kbytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    }
  }
}
  1. Shared Objects

  • List of shared libraries: Libraries used by the script (e.g., libc, libdl, etc.).

Example:

{
  "sharedObjects": ["/lib64/libdl.so.2", "/lib64/librt.so.1"]
}

Real-World Applications:

  • Debugging: Diagnosing errors or performance issues in scripts or programs.

  • Incident response: Analyzing causes and gathering evidence for incidents involving Node.js applications.

  • Performance analysis: Understanding memory usage, CPU consumption, and other performance metrics.

  • Security investigations: Identifying potential security vulnerabilities or suspicious activity.


Reporting Runtime Errors and Health Data

Node.js has a reporting module that allows you to generate reports that provide information about the runtime environment, errors, and resource usage of your Node.js applications.

Generating Reports

There are three main ways to generate a report:

  • Uncaught Exceptions: Node.js will automatically generate a report if an unhandled exception occurs in your code.

  • Signal: You can send a signal to Node.js (by default, SIGUSR2) to trigger a report.

  • Fatal Errors: Node.js will generate a report when a fatal error occurs that causes the application to terminate.

Additional Flags

In addition to the default report generation methods, you can also use the following flags to customize the report:

  • --report-compact: Generates a single-line JSON report for easy processing.

  • --report-directory: Specifies the directory where the report should be saved.

  • --report-filename: Specifies the name of the report file.

  • --report-signal: Sets the signal that triggers the report (not supported on Windows).

Generating Reports from JavaScript

You can also generate reports programmatically using the process.report API:

process.report.writeReport(); // Generate a report and save it to a file with a default name.

You can pass a filename to writeReport() to save the report to a specific file:

process.report.writeReport("./my-report.json");

You can also pass an Error object to writeReport() to include the error stack in the report:

try {
  process.chdir("/non-existent-path");
} catch (err) {
  process.report.writeReport(err); // Include the error stack in the report.
}

Report Content

Reports contain detailed information about the Node.js runtime environment, including:

  • Header: Event type, date, time, PID, Node.js version

  • JavaScript and native stack traces

  • V8 heap information

  • libuv handle information

  • OS platform information (CPU, memory usage, system limits)

Real-World Applications

Diagnostic reports are useful for:

  • Debugging unhandled exceptions and fatal errors

  • Inspecting resource usage and performance bottlenecks

  • Monitoring application health and stability

  • Automating error reporting and performance analysis


Customizing Report Generation in Node.js

Runtime Configuration (via process.report object):

1. Error Triggers:

  • reportOnFatalError: Generates a report when a fatal error (internal error) occurs.

  • reportOnSignal: Generates a report when a specific signal is sent to the process (not supported on Windows).

  • reportOnUncaughtException: Generates a report when an uncaught exception occurs.

2. Report File:

  • filename: Specifies the output file name.

  • directory: Specifies the directory where the report file will be saved.

Code Snippet:

// Enable report generation on uncaught exceptions only
process.report.reportOnFatalError = false;
process.report.reportOnSignal = false;
process.report.reportOnUncaughtException = true;

Environment Variable Configuration:

Environment variables can also be used to configure report generation at startup:

NODE_OPTIONS="--report-uncaught-exception --report-on-fatalerror --report-on-signal=SIGUSR2 --report-filename=./report.json --report-directory=/home/nodeuser"

Real-World Applications:

  • Debugging: Reports can provide valuable information for debugging errors and identifying their causes.

  • Error Monitoring: Reports can be collected and analyzed to monitor error trends and identify potential issues.

  • Crash Reporting: Reports can be automatically sent to a central server to track and resolve crashes.

Complete Code Implementation:

// Enable report generation on uncaught exceptions and fatal errors
process.report.reportOnUncaughtException = true;
process.report.reportOnFatalError = true;

// Save the report file to a custom location
process.report.filename = "./my-custom-report.json";
process.report.directory = "/path/to/my-report-directory";

Interaction with Workers

Imagine your computer as a house with multiple rooms. The main room is where the main program runs.

Workers are like separate rooms in the house that can run code independently from the main room.

When the main room creates a report, it can also include reports from the workers.

The main room will wait for the workers to finish their reports before finishing its own report. But since computers are very fast, this waiting time is usually very short.

Code Snippet:

// Main thread (main room)
const { Worker } = require("worker_threads");

const worker = new Worker("./worker.js");

worker.postMessage({ message: "Generate report" });

worker.on("message", (msg) => {
  console.log("Worker report:", msg); // Log the worker's report
});

Real-World Application:

Workers can be used to perform tasks that can be done independently from the main program, such as:

  • Data processing

  • Image manipulation

  • Video encoding

  • Background calculations