node-debug-hooked
A wrapper around the debug package that prints out an arbitrary id (requestId, contextId) from the asnyc context along with each line. It uses the cls-hooked package to follow async contexts. CLS stands for continuation local storage and is very similar to Java's ThreadLocal. cls-hooked is the implementation based on async-hooks introduced in Node.js 8.x.
Using this package, all your debug entries will be prefixed with a unique ID (e.g. HTTP request id) so you can follow the lifecycle of each incoming requests.
NOTE: right now, this package only works with Node.js 8.x+ as the compilation target is ES2017.
Usage with Express.js
The following is a small example for Express.js but you can easily refactor to other frameworks like Koa.js.
const express = const uuidV4 = const cls = const debugHookedFactory = // request is the name of the CLS namespace that will hold the request idconst clsNamespaceName = 'request'const ns = cls// it wraps the debug package, you can use it the same way as the original debug packageconst debugHooked = // example express middleware that binds cls contexts to incoming requests// based on the following example: https://clakech.github.io/cls-hooked-sample/// ns.run() is the important part as cls namespaces only work if the starting code// is wrapped in an ns.run() callconst clsBinder = { ns ns ns} const app = app // create a new wrapped debug logger. this can be used the same way as the original debugconst debug = app app
Copy the code above to a javascript file (sample.js) and start it with the following command:
DEBUG=sample* node sample.js
Then call your server with the following:
curl http://localhost:3000/hello
You'll see something like this on the standard output of your server:
sample:hello [51f53654-647f-455a-8e45-91584aa31d1b] before +0ms
sample:hello [51f53654-647f-455a-8e45-91584aa31d1b] after +5ms
Usage with some simple async code
This code sample uses async-await, but can be easily refactored.
const uuidV4 = const cls = const debugHookedFactory = // request is the name of the CLS namespace that will hold the async context idconst clsNamespaceName = 'asyncCtx'const ns = cls// it wraps the debug package, you can use it the same way as the original debug packageconst debugHooked = // create a new wrapped debug logger. this can be used the same way as the original debugconst debug = { const res = await return { }} { return { }} 1 2 3
Copy the code above to a javascript file (sample.js) and start it with the following command:
DEBUG=sample* node sample.js
You'll see something like this on the standard output:
sample:asyncawait [async_1] myOtherAsyncFunction running +0ms
sample:asyncawait [async_1] myOtherAsyncFunction +3ms
sample:asyncawait [async_2] myOtherAsyncFunction running +1ms
sample:asyncawait [async_2] myOtherAsyncFunction +0ms
sample:asyncawait [async_3] myOtherAsyncFunction running +0ms
sample:asyncawait [async_3] myOtherAsyncFunction +0ms
sample:asyncawait [async_1] myOtherAsyncFunction result: myOtherAsyncFunction result +801ms
sample:asyncawait [async_2] myOtherAsyncFunction result: myOtherAsyncFunction result +1ms
sample:asyncawait [async_3] myOtherAsyncFunction result: myOtherAsyncFunction result +1ms
sample:asyncawait [async_1] myAsyncFunction result: myAsyncFunction result +999ms
sample:asyncawait [async_2] myAsyncFunction result: myAsyncFunction result +1ms
sample:asyncawait [async_3] myAsyncFunction result: myAsyncFunction result +1ms
You can find the above Javascript code in the sample directory in async-await.js as well.