Node.jsのCoreにレポート機能が入った
結構前から進行してて入れたいねーってなってたらこんなにかかってしまいました。
semver-minorなので、次のリリースで入るでしょう。
目的
主な目的としては、何かのエラーで例外をキャッチしたときにその時の詳細情報をコア側から提供し、原因特定の手助けをします。
node-report
node-reportとは、公式が出しているレポーターです。
主に以下の情報を提供します。
- JavaScript Stack Trace
- Native Stack Trace
- JavaScript Heap and Garbage Collector
- Resource Usage
- Node.js libuv Handle Summary
- System Information
ネイティブのスタックトレース、ヒープ統計情報、プラットフォーム情報、リソース使用状況などが人間が読める形でレポート化されます。
また、未処理の例外や致命的なエラーにも対応します。
以下は、現在npmに置かれているリンクです。
(今後はコアに入りますが、npmにもpublishされるかは自分は知りません)
node-report単体で動かす場合は以下のように動かします。
$ npm i node-report $ node -r node-report test.js $ cat node-report.20171105.202142.9066.001.txt ================================================================================ ==== Node Report =============================================================== ... Node.js version: v9.0.0 ... ================================================================================ ==== JavaScript Stack Trace ==================================================== ... ================================================================================ ==== Native Stack Trace ======================================================== ... ================================================================================ ==== JavaScript Heap and Garbage Collector ===================================== ... ================================================================================ ==== Resource Usage ============================================================ ... ================================================================================ ==== Node.js libuv Handle Summary ============================================== ... ================================================================================ ==== System Information ======================================================== ... ================================================================================
注意点として、node-reportはテキスト形式でしたが、コアではjsonが採用されています。
使い方
ここからは、coreから使う方法を説明します。
現在、report
はstability:1なので、実行するときには実験中フラグ(--experimental-report
)が必要となります。
使用方法としては、CLIから指定して使う方法とコードから呼ぶ方法があります。
CLI
$ node --experimental-report --diagnostic-report-uncaught-exception \ --diagnostic-report-on-fatalerror --diagnostic-report-on-signal \ --diagnostic-report-signal=SIGUSR2 --diagnostic-report-filename=./report.json \ --diagnostic-report-directory=/home/nodeuser --diagnostic-report-verbose index.js
--diagnostic-report-uncaught-exception
uncaught-exceptionをトリガーにします。
--diagnostic-report-on-signal
実行中のNode.jsプロセスへの指定されたシグナルを受信したときにレポートを生成します。
デフォルトはSIGUSR2
です。
この機能では、レポートを他のプログラムから起動する必要がある場合に便利で、モニタリングアプリケーションはこの機能を利用して定期的にレポートを収集することが可能です。
--diagnostic-report-on-fatalerror
アプリケーションの終了につながる致命的なエラー(e.g メモリ不足等のNode.jsランタイム内の内部エラー)をレポートをトリガーにします。
--diagnostic-report-directory
出力先のディレクトリを指定します。
--diagnostic-report-filename
出力のファイル名を指定します。
--diagnostic-report-signal
レポート生成のシグナルを設定またはリセットします。(windowsサポート外)
--diagnostic-report-verbose
レポート生成中に追加で情報を入れます。
コードから
基本的に特定箇所のエラーでレポートしてほしいときには、コードから呼ぶのがよいでしょう。
try { console.log('hi!'); throw new Error('bye!'); } catch(err) { // エラーオブジェクトを渡す process.report.triggerReport('report.json', err); }
$ node --experimental-report index.js
{ "header": { "event": "JavaScript API", "location": "TriggerReport", "filename": "report.json", "dumpEventTime": "2019-01-24T08:27:53Z", "dumpEventTimeStamp": "1548286073604", "processId": "56468", "commandLine": [ "./node", "--experimental-report", "b.js" ], "nodejsVersion": "v12.0.0-pre", "wordSize": "64 bit", "componentVersions": { "node": "12.0.0-pre", "v8": "7.1.302.33-node.10", ... }, "osVersion": "Darwin 18.2.0 Darwin Kernel Version 18.2.0: Mon Nov 12 20:24:46 PST 2018; root:xnu-4903.231.4~2/RELEASE_X86_64", "machine": "Darwin 18.2.0 Darwin Kernel Version 18.2.0: Mon Nov 12 20:24:46 PST 2018; root:xnu-4903.231.4~2/RELEASE_X86_64about-hiroppy.local x86_64" }, "javascriptStack": { "message": "Error: bye!", "stack": [ "at Object.<anonymous> (/Users/about_hiroppy/Programming/nodejs/node/out/Release/b.js:19:9)", "at Module._compile (internal/modules/cjs/loader.js:737:30)", "at Object.Module._extensions..js (internal/modules/cjs/loader.js:748:10)", "at Module.load (internal/modules/cjs/loader.js:629:32)", "at tryModuleLoad (internal/modules/cjs/loader.js:572:12)", "at Function.Module._load (internal/modules/cjs/loader.js:564:3)", "at Function.Module.runMain (internal/modules/cjs/loader.js:802:12)", "at executeUserCode (internal/bootstrap/node.js:497:15)" ] }, "nativeStack": [ " [pc=0x100130ed1] report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, v8::Local<v8::String>) [/Users/about_hiroppy/Programming/nodejs/node/out/Release/./node]", ... ], "javascriptHeap": { "totalMemory": "5603328", "totalCommittedMemory": "3743952", "usedMemory": "2601416", "availableMemory": "1521802280", "memoryLimit": "1526909922", "heapSpaces": { "read_only_space": { "memorySize": "524288", "committedMemory": "39208", "capacity": "515584", "used": "30504", "available": "485080" }, "new_space": { "memorySize": "2097152", "committedMemory": "1877472", "capacity": "1031168", "used": "828632", "available": "202536" }, "old_space": { "memorySize": "1748992", "committedMemory": "1308424", "capacity": "1273648", "used": "1273608", "available": "40" }, "code_space": { "memorySize": "696320", "committedMemory": "185920", "capacity": "153152", "used": "153152", "available": "0" }, "map_space": { "memorySize": "536576", "committedMemory": "332928", "capacity": "315520", "used": "315520", "available": "0" }, "large_object_space": { "memorySize": "0", "committedMemory": "0", "capacity": "1521114624", "used": "0", "available": "1521114624" }, "new_large_object_space": { "memorySize": "0", "committedMemory": "0", "capacity": "0", "used": "0", "available": "0" } } }, "resourceUsage": { "userCpuSeconds": "0.082528", "kernelCpuSeconds": "0.022165", "cpuConsumptionPercent": "0.000000", "maxRss": "25232932864", "pageFaults": { "IORequired": "0", "IONotRequired": "6375" }, "fsActivity": { "reads": "0", "writes": "0" } }, "libuv": [ { "type": "async", "is_active": "1", "is_referenced": "0", "address": "4339086624", "details": "" }, { "type": "timer", "is_active": "0", "is_referenced": "0", "address": "140732920753032", "details": "repeat: 0, timeout in: 140734958161789 ms" }, { "type": "check", "is_active": "1", "is_referenced": "0", "address": "140732920753184", "details": "" }, { "type": "idle", "is_active": "0", "is_referenced": "1", "address": "140732920753304", "details": "" }, { "type": "prepare", "is_active": "0", "is_referenced": "0", "address": "140732920753424", "details": "" }, { "type": "check", "is_active": "0", "is_referenced": "0", "address": "140732920753544", "details": "" }, { "type": "async", "is_active": "1", "is_referenced": "0", "address": "4320933640", "details": "" }, ... ], "environmentVariables": { "TMPDIR": "/var/folders/0k/t5s25c2d30dgvmkr26mj83_h0000gn/T/", ... }, "userLimits": { "core_file_size_blocks": { "soft": "", "hard": "unlimited" }, "data_seg_size_kbytes": { "soft": "unlimited", "hard": "unlimited" }, "file_size_blocks": { "soft": "unlimited", "hard": "unlimited" }, "max_locked_memory_bytes": { "soft": "unlimited", "hard": "unlimited" }, "max_memory_size_kbytes": { "soft": "unlimited", "hard": "unlimited" }, "open_files": { "soft": "unlimited", "hard": "unlimited" }, "stack_size_bytes": { "soft": "unlimited", "hard": "67104768" }, "cpu_time_seconds": { "soft": "unlimited", "hard": "unlimited" }, "max_user_processes": { "soft": "unlimited", "hard": "2128" }, "virtual_memory_kbytes": { "soft": "unlimited", "hard": "unlimited" } }, "sharedObjects": [ "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", ... ] }
このように基本情報からjs, nativeのスタック、jsのヒープ関連、リソースの使われ方、イベントループ(libuv)、ユーザーリミット等を確認することができます。
また、以下のように特定イベント時に取得することも可能です。
// uncaught exceptions時のみトリガーさせる process.report.setDiagnosticReportOptions({ events: ['exception'] }); // 内部エラーと外部のシグナル時のみトリガーさせる process.report.setDiagnosticReportOptions({ events: ['fatalerror', 'signal'] });
余談
まだ、リリースすらされてないのでライブラリを作るなら今!
webpack-dashboardみたいなのが今後出てきそうな気がしています。