在项目中我们经常会日志打印出来,但是,由于请求一般的异步或者多线程的。N个请求生命周期中的日志会杂乱的显示在一起,我们很难判断出某个日志是同一请求的。这个时候,我们需求用到traceIdl来标记。在java中,我们会利用slf4j的MDC记录traceId。那么,在nodejs中,我们要怎么做呢?
我们都知道,nodejs在v10.5.0之后才增加了对多线程的支持。正常情况下,我们还是用单线程去执行的,然后,内部执行是异步的。那么,既然是异步的,我们就不可能通过类似Java的ThreadLocal的方案去做。那么,我们只能通过参数传递发方式去把ctx传递到需要的地方。
在node8.2中新增了async_hooks模块,虽然好像该api一直是试验阶段,但是,目前看来,还是可行的。
官方介绍async_hooks是这么用的
const asyncHook = require('async_hooks');
const hook = asyncHooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
},
before(asyncId) {
},
after(asyncId) {
},
destroy(asyncId) {
}
});
hook.enable();
在实际使用的时候,我们更多的是用init和destory就够了。下面,我们就以日志的traceId为例,写一个demo
1.AsyncHookTrace.js
const asyncHooks = require("async_hooks");
const traceStore = new Map();
const asyncHook = asyncHooks.createHook({
init(asyncId,type,triggerAsyncId,resource){
const parentTrace = traceStore.get(triggerAsyncId);
if(parentTrace){
traceStore.set(asyncId,parentTrace);
}
},
destroy(asyncId) {
traceStore.delete(asyncId);
}
});
asyncHook.enable();
class AsyncHookTrace {
static setTrace(trace){
traceStore.set(asyncHooks.executionAsyncId(),trace);
}
static getTrace(){
return traceStore.get(asyncHooks.executionAsyncId());
}
}
export default AsyncHookTrace
2.写一个middleware去赋值,这个中间件建议放在route中,因为,koa的项目有很多的中间件,而中间件又都是异步的,所以会给traceStore塞很多值。
import uuid from "uuid/v4";
import AsyncHookTrace from "../../async_hooks/AsyncHookTrace";
function trace(root,ops) {
return async function trace(ctx,next){
try {
AsyncHookTrace.setTrace(uuid());
await next();
}catch (e) {
throw e
}
}
}
export default trace
3.logger中格式化
import moment from "moment";
import appConfig from "./AppConfig";
const { createLogger, format, transports } = require('winston');
const { combine, timestamp, label, printf } = format;
const myFormat = printf(({ level, message, label, timestamp }) => {
let tradeId = AsyncHookTrace.getTrace();
return `[${moment(timestamp).format("YYYY-MM-DD hh:mm:ss")}] [${tradeId}] : ${message}`;
});
const logger = createLogger({
level: appConfig.get("logger.level"),
format: combine(
timestamp(),
myFormat
),
transports: [new transports.Console()]
});
export default logger;
运行结果如下:
[2019-11-27 11:09:34] [02e04e04-d9a4-4630-ba18-daca8f702f8a] : getaddrinfo ENOTFOUND localhost localhost:8300
traceId打印成功