診斷報告#

穩定性:2 - 穩定

生成 JSON 格式的診斷摘要,並寫入檔案。

該報告旨在用於開發、測試和生產環境,以捕獲並保留用於問題排查的資訊。它包括 JavaScript 和原生堆疊跟蹤、堆統計資訊、平臺資訊、資源使用情況等。啟用報告選項後,除了透過 API 呼叫以程式設計方式觸發外,還可以在未處理的異常、致命錯誤和使用者訊號上觸發診斷報告。

下面提供了一個在未捕獲異常時生成的完整示例報告,以供參考。

{
  "header": {
    "reportVersion": 5,
    "event": "exception",
    "trigger": "Exception",
    "filename": "report.20181221.005011.8974.0.001.json",
    "dumpEventTime": "2018-12-21T00:50:11Z",
    "dumpEventTimeStamp": "1545371411331",
    "processId": 8974,
    "cwd": "/home/nodeuser/project/node",
    "commandLine": [
      "/home/nodeuser/project/node/out/Release/node",
      "--report-uncaught-exception",
      "/home/nodeuser/project/node/test/report/test-exception.js",
      "child"
    ],
    "nodejsVersion": "v12.0.0-pre",
    "glibcVersionRuntime": "2.17",
    "glibcVersionCompiler": "2.17",
    "wordSize": "64 bit",
    "arch": "x64",
    "platform": "linux",
    "componentVersions": {
      "node": "12.0.0-pre",
      "v8": "7.1.302.28-node.5",
      "uv": "1.24.1",
      "zlib": "1.2.11",
      "ares": "1.15.0",
      "modules": "68",
      "nghttp2": "1.34.0",
      "napi": "3",
      "llhttp": "1.0.1",
      "openssl": "1.1.0j"
    },
    "release": {
      "name": "node"
    },
    "osName": "Linux",
    "osRelease": "3.10.0-862.el7.x86_64",
    "osVersion": "#1 SMP Wed Mar 21 18:14:51 EDT 2018",
    "osMachine": "x86_64",
    "cpus": [
      {
        "model": "Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz",
        "speed": 2700,
        "user": 88902660,
        "nice": 0,
        "sys": 50902570,
        "idle": 241732220,
        "irq": 0
      },
      {
        "model": "Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz",
        "speed": 2700,
        "user": 88902660,
        "nice": 0,
        "sys": 50902570,
        "idle": 241732220,
        "irq": 0
      }
    ],
    "networkInterfaces": [
      {
        "name": "en0",
        "internal": false,
        "mac": "13:10:de:ad:be:ef",
        "address": "10.0.0.37",
        "netmask": "255.255.255.0",
        "family": "IPv4"
      }
    ],
    "host": "test_machine"
  },
  "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)",
      "at Module._compile (internal/modules/cjs/loader.js:718:30)",
      "at Object.Module._extensions..js (internal/modules/cjs/loader.js:729:10)",
      "at Module.load (internal/modules/cjs/loader.js:617:32)",
      "at tryModuleLoad (internal/modules/cjs/loader.js:560:12)",
      "at Function.Module._load (internal/modules/cjs/loader.js:552:3)",
      "at Function.Module.runMain (internal/modules/cjs/loader.js:771:12)",
      "at executeUserCode (internal/bootstrap/node.js:332:15)"
    ]
  },
  "nativeStack": [
    {
      "pc": "0x000055b57f07a9ef",
      "symbol": "report::GetNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, v8::Local<v8::String>, std::ostream&) [./node]"
    },
    {
      "pc": "0x000055b57f07cf03",
      "symbol": "report::GetReport(v8::FunctionCallbackInfo<v8::Value> const&) [./node]"
    },
    {
      "pc": "0x000055b57f1bccfd",
      "symbol": " [./node]"
    },
    {
      "pc": "0x000055b57f1be048",
      "symbol": "v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [./node]"
    },
    {
      "pc": "0x000055b57feeda0e",
      "symbol": " [./node]"
    }
  ],
  "javascriptHeap": {
    "totalMemory": 5660672,
    "executableMemory": 524288,
    "totalCommittedMemory": 5488640,
    "availableMemory": 4341379928,
    "totalGlobalHandlesMemory": 8192,
    "usedGlobalHandlesMemory": 3136,
    "usedMemory": 4816432,
    "memoryLimit": 4345298944,
    "mallocedMemory": 254128,
    "externalMemory": 315644,
    "peakMallocedMemory": 98752,
    "nativeContextCount": 1,
    "detachedContextCount": 0,
    "doesZapGarbage": 0,
    "heapSpaces": {
      "read_only_space": {
        "memorySize": 524288,
        "committedMemory": 39208,
        "capacity": 515584,
        "used": 30504,
        "available": 485080
      },
      "new_space": {
        "memorySize": 2097152,
        "committedMemory": 2019312,
        "capacity": 1031168,
        "used": 985496,
        "available": 45672
      },
      "old_space": {
        "memorySize": 2273280,
        "committedMemory": 1769008,
        "capacity": 1974640,
        "used": 1725488,
        "available": 249152
      },
      "code_space": {
        "memorySize": 696320,
        "committedMemory": 184896,
        "capacity": 152128,
        "used": 152128,
        "available": 0
      },
      "map_space": {
        "memorySize": 536576,
        "committedMemory": 344928,
        "capacity": 327520,
        "used": 327520,
        "available": 0
      },
      "large_object_space": {
        "memorySize": 0,
        "committedMemory": 0,
        "capacity": 1520590336,
        "used": 0,
        "available": 1520590336
      },
      "new_large_object_space": {
        "memorySize": 0,
        "committedMemory": 0,
        "capacity": 0,
        "used": 0,
        "available": 0
      }
    }
  },
  "resourceUsage": {
    "rss": "35766272",
    "free_memory": "1598337024",
    "total_memory": "17179869184",
    "available_memory": "1598337024",
    "maxRss": "36624662528",
    "constrained_memory": "36624662528",
    "userCpuSeconds": 0.040072,
    "kernelCpuSeconds": 0.016029,
    "cpuConsumptionPercent": 5.6101,
    "userCpuConsumptionPercent": 4.0072,
    "kernelCpuConsumptionPercent": 1.6029,
    "pageFaults": {
      "IORequired": 0,
      "IONotRequired": 4610
    },
    "fsActivity": {
      "reads": 0,
      "writes": 0
    }
  },
  "uvthreadResourceUsage": {
    "userCpuSeconds": 0.039843,
    "kernelCpuSeconds": 0.015937,
    "cpuConsumptionPercent": 5.578,
    "userCpuConsumptionPercent": 3.9843,
    "kernelCpuConsumptionPercent": 1.5937,
    "fsActivity": {
      "reads": 0,
      "writes": 0
    }
  },
  "libuv": [
    {
      "type": "async",
      "is_active": true,
      "is_referenced": false,
      "address": "0x0000000102910900",
      "details": ""
    },
    {
      "type": "timer",
      "is_active": false,
      "is_referenced": false,
      "address": "0x00007fff5fbfeab0",
      "repeat": 0,
      "firesInMsFromNow": 94403548320796,
      "expired": true
    },
    {
      "type": "check",
      "is_active": true,
      "is_referenced": false,
      "address": "0x00007fff5fbfeb48"
    },
    {
      "type": "idle",
      "is_active": false,
      "is_referenced": true,
      "address": "0x00007fff5fbfebc0"
    },
    {
      "type": "prepare",
      "is_active": false,
      "is_referenced": false,
      "address": "0x00007fff5fbfec38"
    },
    {
      "type": "check",
      "is_active": false,
      "is_referenced": false,
      "address": "0x00007fff5fbfecb0"
    },
    {
      "type": "async",
      "is_active": true,
      "is_referenced": false,
      "address": "0x000000010188f2e0"
    },
    {
      "type": "tty",
      "is_active": false,
      "is_referenced": true,
      "address": "0x000055b581db0e18",
      "width": 204,
      "height": 55,
      "fd": 17,
      "writeQueueSize": 0,
      "readable": true,
      "writable": true
    },
    {
      "type": "signal",
      "is_active": true,
      "is_referenced": false,
      "address": "0x000055b581d80010",
      "signum": 28,
      "signal": "SIGWINCH"
    },
    {
      "type": "tty",
      "is_active": true,
      "is_referenced": true,
      "address": "0x000055b581df59f8",
      "width": 204,
      "height": 55,
      "fd": 19,
      "writeQueueSize": 0,
      "readable": true,
      "writable": true
    },
    {
      "type": "loop",
      "is_active": true,
      "address": "0x000055fc7b2cb180",
      "loopIdleTimeSeconds": 22644.8
    },
    {
      "type": "tcp",
      "is_active": true,
      "is_referenced": true,
      "address": "0x000055e70fcb85d8",
      "localEndpoint": {
        "host": "localhost",
        "ip4": "127.0.0.1",
        "port": 48986
      },
      "remoteEndpoint": {
        "host": "localhost",
        "ip4": "127.0.0.1",
        "port": 38573
      },
      "sendBufferSize": 2626560,
      "recvBufferSize": 131072,
      "fd": 24,
      "writeQueueSize": 0,
      "readable": true,
      "writable": true
    }
  ],
  "workers": [],
  "environmentVariables": {
    "REMOTEHOST": "REMOVED",
    "MANPATH": "/opt/rh/devtoolset-3/root/usr/share/man:",
    "XDG_SESSION_ID": "66126",
    "HOSTNAME": "test_machine",
    "HOST": "test_machine",
    "TERM": "xterm-256color",
    "SHELL": "/bin/csh",
    "SSH_CLIENT": "REMOVED",
    "PERL5LIB": "/opt/rh/devtoolset-3/root//usr/lib64/perl5/vendor_perl:/opt/rh/devtoolset-3/root/usr/lib/perl5:/opt/rh/devtoolset-3/root//usr/share/perl5/vendor_perl",
    "OLDPWD": "/home/nodeuser/project/node/src",
    "JAVACONFDIRS": "/opt/rh/devtoolset-3/root/etc/java:/etc/java",
    "SSH_TTY": "/dev/pts/0",
    "PCP_DIR": "/opt/rh/devtoolset-3/root",
    "GROUP": "normaluser",
    "USER": "nodeuser",
    "LD_LIBRARY_PATH": "/opt/rh/devtoolset-3/root/usr/lib64:/opt/rh/devtoolset-3/root/usr/lib",
    "HOSTTYPE": "x86_64-linux",
    "XDG_CONFIG_DIRS": "/opt/rh/devtoolset-3/root/etc/xdg:/etc/xdg",
    "MAIL": "/var/spool/mail/nodeuser",
    "PATH": "/home/nodeuser/project/node:/opt/rh/devtoolset-3/root/usr/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin",
    "PWD": "/home/nodeuser/project/node",
    "LANG": "en_US.UTF-8",
    "PS1": "\\u@\\h : \\[\\e[31m\\]\\w\\[\\e[m\\] >  ",
    "SHLVL": "2",
    "HOME": "/home/nodeuser",
    "OSTYPE": "linux",
    "VENDOR": "unknown",
    "PYTHONPATH": "/opt/rh/devtoolset-3/root/usr/lib64/python2.7/site-packages:/opt/rh/devtoolset-3/root/usr/lib/python2.7/site-packages",
    "MACHTYPE": "x86_64",
    "LOGNAME": "nodeuser",
    "XDG_DATA_DIRS": "/opt/rh/devtoolset-3/root/usr/share:/usr/local/share:/usr/share",
    "LESSOPEN": "||/usr/bin/lesspipe.sh %s",
    "INFOPATH": "/opt/rh/devtoolset-3/root/usr/share/info",
    "XDG_RUNTIME_DIR": "/run/user/50141",
    "_": "./node"
  },
  "userLimits": {
    "core_file_size_blocks": {
      "soft": "",
      "hard": "unlimited"
    },
    "data_seg_size_bytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "file_size_blocks": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "max_locked_memory_bytes": {
      "soft": "unlimited",
      "hard": 65536
    },
    "max_memory_size_bytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "open_files": {
      "soft": "unlimited",
      "hard": 4096
    },
    "stack_size_bytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "cpu_time_seconds": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "max_user_processes": {
      "soft": "unlimited",
      "hard": 4127290
    },
    "virtual_memory_bytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    }
  },
  "sharedObjects": [
    "/lib64/libdl.so.2",
    "/lib64/librt.so.1",
    "/lib64/libstdc++.so.6",
    "/lib64/libm.so.6",
    "/lib64/libgcc_s.so.1",
    "/lib64/libpthread.so.0",
    "/lib64/libc.so.6",
    "/lib64/ld-linux-x86-64.so.2"
  ]
} 

用法#

node --report-uncaught-exception --report-on-signal \
--report-on-fatalerror app.js 
  • --report-uncaught-exception 啟用在未捕獲異常時生成報告。在檢查 JavaScript 堆疊以及原生堆疊和其他執行時環境資料時很有用。

  • --report-on-signal 啟用在正在執行的 Node.js 程序接收到指定(或預定義)訊號時生成報告。(關於如何修改觸發報告的訊號,請參見下文。)預設訊號為 SIGUSR2。當需要從另一個程式觸發報告時很有用。應用程式監控器可以利用此功能定期收集報告,並將豐富的內部執行時資料繪製到其檢視中。

Windows 不支援基於訊號的報告生成。

在正常情況下,無需修改觸發報告的訊號。但是,如果 SIGUSR2 已被用於其他目的,則此標誌有助於更改用於報告生成的訊號,並保留 SIGUSR2 用於所述目的的原始含義。

  • --report-on-fatalerror 啟用在導致應用程式終止的致命錯誤(Node.js 執行時內部的錯誤,例如記憶體不足)時觸發報告。有助於檢查各種診斷資料元素,如堆、堆疊、事件迴圈狀態、資源消耗等,以分析致命錯誤的原因。

  • --report-compact 以緊湊格式(單行 JSON)寫入報告,比專為人類閱讀設計的預設多行格式更容易被日誌處理系統使用。

  • --report-directory 生成報告的位置。

  • --report-filename 寫入報告的檔名。

  • --report-signal 設定或重置用於報告生成的訊號(在 Windows 上不支援)。預設訊號為 SIGUSR2

  • --report-exclude-network 從診斷報告中排除 header.networkInterfaces 並停用 libuv.*.(remote|local)Endpoint.host 中的反向 DNS 查詢。預設情況下此選項未設定,網路介面會被包含在內。

  • --report-exclude-env 從診斷報告中排除 environmentVariables。預設情況下此選項未設定,環境變數會被包含在內。

也可以透過 JavaScript 應用程式的 API 呼叫來觸發報告。

process.report.writeReport(); 

此函式接受一個可選的附加引數 filename,它是報告寫入檔案的名稱。

process.report.writeReport('./foo.json'); 

此函式接受一個可選的附加引數 err,它是一個 Error 物件,將用作報告中列印的 JavaScript 堆疊的上下文。當使用報告處理回撥或異常處理程式中的錯誤時,這允許報告包含原始錯誤的位置以及處理它的位置。

try {
  process.chdir('/non-existent-path');
} catch (err) {
  process.report.writeReport(err);
}
// Any other code 

如果同時將檔名和錯誤物件傳遞給 writeReport(),則錯誤物件必須是第二個引數。

try {
  process.chdir('/non-existent-path');
} catch (err) {
  process.report.writeReport(filename, err);
}
// Any other code 

診斷報告的內容可以透過 JavaScript 應用程式的 API 呼叫以 JavaScript 物件的形式返回。

const report = process.report.getReport();
console.log(typeof report === 'object'); // true

// Similar to process.report.writeReport() output
console.log(JSON.stringify(report, null, 2)); 

此函式接受一個可選的附加引數 err,它是一個 Error 物件,將用作報告中列印的 JavaScript 堆疊的上下文。

const report = process.report.getReport(new Error('custom error'));
console.log(typeof report === 'object'); // true 

API 版本在從應用程式內部檢查執行時狀態時很有用,以便進行資源消耗、負載均衡、監控等的自我調整。

報告的內容包括一個頭部部分,包含事件型別、日期、時間、PID 和 Node.js 版本;包含 JavaScript 和原生堆疊跟蹤的部分;包含 V8 堆資訊的部分;包含 libuv 控制代碼資訊的部分;以及顯示 CPU 和記憶體使用情況及系統限制的作業系統平臺資訊部分。可以使用 Node.js REPL 觸發一個示例報告。

$ node
> process.report.writeReport();
Writing Node.js report to file: report.20181126.091102.8480.0.001.json
Node.js report completed
> 

當報告被寫入時,開始和結束訊息會輸出到 stderr,並且報告的檔名會返回給呼叫者。預設檔名包括日期、時間、PID 和一個序列號。如果為同一個 Node.js 程序多次生成報告,序列號有助於將報告轉儲與執行時狀態關聯起來。

報告版本#

診斷報告有一個關聯的單位數版本號(report.header.reportVersion),唯一地代表報告的格式。當新增或刪除新鍵,或值的資料型別發生變化時,版本號會增加。報告版本定義在 LTS 版本之間保持一致。

版本歷史#

版本 5#

userLimits 部分中,將鍵 data_seg_size_kbytesmax_memory_size_kbytesvirtual_memory_kbytes 分別替換為 data_seg_size_bytesmax_memory_size_bytesvirtual_memory_bytes,因為這些值是以位元組為單位給出的。

{
  "userLimits": {
    // Skip some keys ...
    "data_seg_size_bytes": { // replacing data_seg_size_kbytes
      "soft": "unlimited",
      "hard": "unlimited"
    },
    // ...
    "max_memory_size_bytes": { // replacing max_memory_size_kbytes
      "soft": "unlimited",
      "hard": "unlimited"
    },
    // ...
    "virtual_memory_bytes": { // replacing virtual_memory_kbytes
      "soft": "unlimited",
      "hard": "unlimited"
    }
  }
} 
版本 4#

tcpudp libuv 控制代碼端點添加了新欄位 ipv4ipv6。示例:

{
  "libuv": [
    {
      "type": "tcp",
      "is_active": true,
      "is_referenced": true,
      "address": "0x000055e70fcb85d8",
      "localEndpoint": {
        "host": "localhost",
        "ip4": "127.0.0.1", // new key
        "port": 48986
      },
      "remoteEndpoint": {
        "host": "localhost",
        "ip4": "127.0.0.1", // new key
        "port": 38573
      },
      "sendBufferSize": 2626560,
      "recvBufferSize": 131072,
      "fd": 24,
      "writeQueueSize": 0,
      "readable": true,
      "writable": true
    },
    {
      "type": "tcp",
      "is_active": true,
      "is_referenced": true,
      "address": "0x000055e70fcd68c8",
      "localEndpoint": {
        "host": "ip6-localhost",
        "ip6": "::1", // new key
        "port": 52266
      },
      "remoteEndpoint": {
        "host": "ip6-localhost",
        "ip6": "::1", // new key
        "port": 38573
      },
      "sendBufferSize": 2626560,
      "recvBufferSize": 131072,
      "fd": 25,
      "writeQueueSize": 0,
      "readable": false,
      "writable": false
    }
  ]
} 
版本 3#

以下記憶體使用鍵已新增到 resourceUsage 部分。

{
  "resourceUsage": {
    "rss": "35766272",
    "free_memory": "1598337024",
    "total_memory": "17179869184",
    "available_memory": "1598337024",
    "constrained_memory": "36624662528"
  }
} 
版本 2#

添加了 Worker 支援。有關更多詳細資訊,請參閱與工作執行緒的互動部分。

版本 1#

這是診斷報告的第一個版本。

配置#

報告生成的額外執行時配置可透過 process.report 的以下屬性獲得:

reportOnFatalError 當為 true 時,在致命錯誤時觸發診斷報告。預設為 false

reportOnSignal 當為 true 時,在接收到訊號時觸發診斷報告。這在 Windows 上不受支援。預設為 false

reportOnUncaughtException 當為 true 時,在未捕獲的異常時觸發診斷報告。預設為 false

signal 指定將用於攔截外部觸發器以生成報告的 POSIX 訊號識別符號。預設為 'SIGUSR2'

filename 指定檔案系統中輸出檔案的名稱。stdoutstderr 具有特殊含義。使用它們將導致報告被寫入到相應的標準流中。在使用標準流的情況下,directory 中的值將被忽略。不支援 URL。預設為一個包含時間戳、PID 和序列號的複合檔名。

directory 指定報告將被寫入的檔案系統目錄。不支援 URL。預設為 Node.js 程序的當前工作目錄。

excludeNetwork 從診斷報告中排除 header.networkInterfaces

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

// Trigger report for both internal errors as well as external signal.
process.report.reportOnFatalError = true;
process.report.reportOnSignal = true;
process.report.reportOnUncaughtException = false;

// Change the default signal to 'SIGQUIT' and enable it.
process.report.reportOnFatalError = false;
process.report.reportOnUncaughtException = false;
process.report.reportOnSignal = true;
process.report.signal = 'SIGQUIT';

// Disable network interfaces reporting
process.report.excludeNetwork = true; 

模組初始化時的配置也可以透過環境變數進行:

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

具體的 API 文件可以在 process API 文件部分找到。

與工作執行緒的互動#

Worker 執行緒可以像主執行緒一樣建立報告。

報告將作為 workers 部分的一部分,包含有關當前執行緒的任何子工作執行緒的資訊,每個工作執行緒都會以標準報告格式生成一份報告。

正在生成報告的執行緒將等待工作執行緒的報告完成。然而,這方面的延遲通常很低,因為正在執行的 JavaScript 和事件迴圈都會被中斷以生成報告。