Nodejs Async Hooks
for painless correlation id handling
Generate correlation id
Send the it in the headers
Read the header
Send the header
Read the header
Send the header
Read the header
Read the header
C#, ASP.NET
// module.cs
httpContextAccessor.HttpContext.Items.Add("correlation-id", correlationId);
// later in logger.cs
void log(message) {
var correlationId = httpContextAccessor.HttpContext.Items["correlation-id"];
logMessage($"{correlationId}: {message}");
}
// AsyncLocal<T> is used under the hood:
var s = new AsyncLocal<string>();
s.Value = "this value is local for the current async flow"
// module.cs
httpContextAccessor.HttpContext.Items.Add("correlation-id", correlationId);
// later in logger.cs
void log(message) {
var correlationId = httpContextAccessor.HttpContext.Items["correlation-id"];
logMessage($"{correlationId}: {message}");
}
// AsyncLocal<T> Example:
var s = new AsyncLocal<string>();
s.Value = "this value is local for the current async flow"
NodeJS, expressjs
// notifications.module.js
async function processSingleOffer(req, res) {
......
await offerProcessor.processSingleOffer(offerInfo, req.query.isFake, req.correlation);
......
}
// offer-processor.js
async function processSingleOffer(offerInfo, isFake, correlation) {
.....
logger.info(`Processing offer ${offerInfo.hashedExternalId}`, correlation());
.....
const offer = await submissionsProvider.getOffer(offerInfo, correlation);
.....
const result = await digester.digest(offer, correlation);
.....
logger.info(`Processed offer`, correlation());
}
// offer-digester.js
async digest(offer, correlation) {
....
await this.firstStep.execute(offer, result, correlation);
....
}
// offer-updater.js
async setAsSentPerEmailAndPostDelivery(offer, result, correlation) {
.....
await http.new(correlation, config.paapiUrl).put(offer.links.offer, offer, requestConfig);
.....
}
Nodejs async hooks
Added in: v8.1.0
Stability: 1 - Experimental
const async_hooks = require('async_hooks');
Tracking the lifetime of asynchronous resources.
Asynchronous resources represent objects with an associated callback.
Nodejs async hooks
async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
// the asynchronous object asyncId has been initialized
// type can be
},
before(asyncId) {
// the asynchronous object asyncId is going to call its callback
},
after(asyncId) {
// the asynchronous object asyncId callback execution was finished
},
destroy(asyncId) {
// Called after the resource corresponding to asyncId is destroyed
},
}).enable();
FSEVENTWRAP, FSREQWRAP, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPPARSER,
JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP, SHUTDOWNWRAP,
SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVER, TCPWRAP, TIMERWRAP, TTYWRAP,
UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,
RANDOMBYTESREQUEST, TLSWRAP, Timeout, Immediate, TickObject, PROMISE
async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
// the asynchronous object asyncId has been initialized
// type can be
},
before(asyncId) {
// the asynchronous object asyncId is going to call its callback
},
after(asyncId) {
// the asynchronous object asyncId callback execution was finished
},
destroy(asyncId) {
// Called after the resource corresponding to asyncId is destroyed
},
}).enable();
FSEVENTWRAP, FSREQWRAP, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPPARSER,
JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP, SHUTDOWNWRAP,
SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVER, TCPWRAP, TIMERWRAP, TTYWRAP,
UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,
RANDOMBYTESREQUEST, TLSWRAP, Timeout, Immediate, TickObject, PROMISE
async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
// the asynchronous object asyncId has been initialized
// type can be
},
before(asyncId) {
// the asynchronous object asyncId is going to call its callback
},
after(asyncId) {
// the asynchronous object asyncId callback execution was finished
},
destroy(asyncId) {
// Called after the resource corresponding to asyncId is destroyed
},
}).enable();
FSEVENTWRAP, FSREQWRAP, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPPARSER,
JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP, SHUTDOWNWRAP,
SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVER, TCPWRAP, TIMERWRAP, TTYWRAP,
UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,
RANDOMBYTESREQUEST, TLSWRAP, Timeout, Immediate, TickObject, PROMISE
async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
// the asynchronous object asyncId has been initialized
// type can be
},
before(asyncId) {
// the asynchronous object asyncId is going to call its callback
},
after(asyncId) {
// the asynchronous object asyncId callback execution was finished
},
destroy(asyncId) {
// Called after the resource corresponding to asyncId is destroyed
},
}).enable();
FSEVENTWRAP, FSREQWRAP, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPPARSER,
JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP, SHUTDOWNWRAP,
SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVER, TCPWRAP, TIMERWRAP, TTYWRAP,
UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,
RANDOMBYTESREQUEST, TLSWRAP, Timeout, Immediate, TickObject, PROMISE
Types:
PROMISE, FSEVENTWRAP, FSREQWRAP, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPPARSER, JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP, SHUTDOWNWRAP, SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVER, TCPWRAP, TIMERWRAP, TTYWRAP, UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST, RANDOMBYTESREQUEST, TLSWRAP, Timeout, Immediate, TickObject
Nodejs async hooks
setTimeout(() => {
setTimeout(() => {
setTimeout(() => {
log("HELLO");
}, 10);
}, 10);
}, 10);
const async_hooks = require('async_hooks');
const fs = require('fs');
let indent = 0;
function log(mes) {
const indentStr = ' '.repeat(indent);
fs.writeSync(1, `${indentStr}${mes}\n`);
}
async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
log(`INIT ${asyncId}, type ${type},
trigger ${triggerAsyncId}`);
},
before(asyncId) {
log(`BEFORE ${asyncId}`);
indent += 2;
},
after(asyncId) {
indent -= 2;
log(`AFTER ${asyncId}`);
}
}).enable();
Nodejs async hooks
// lead.module.js
async function setLeadPaid(ctx) {
const correlationId = generateCorrelationId()
globalAsyncStorage.setCorrelationId(correlationId);
await leadService.setLeadPaid(ctx.params.leadId)
....
}
// lead.service.js
async function setLeadPaid(leadId) {
await db.updateOne('leads', { $set: { status: 'paid' } });
logger.log(`Lead ${leadId} status set to paid`);
await http.post(`${notifierUrl}/notify-lead-paid`, { leadId });
}
// logger.js
function log(message) {
const correlationId = globalAsyncStorage.getCorrelationId();
fs.appendFile('log.txt', `${correlationId}: ${message}`);
}
Correlation Id managers
https://www.npmjs.com/package/correlation-id https://www.npmjs.com/package/express-correlation-id https://www.npmjs.com/package/async-local-storage https://www.npmjs.com/package/cls-hooked https://bitbucket.check24.de/projects/BU/repos/npm-current-context-storage
All are using async_hooks
Nodejs Async Hooks
By Pavel L
Nodejs Async Hooks
- 1,291