cls-rtracer 读取器
请求跟踪器 - 为基于 CLS 的请求 id 生成,包括电池, Express & Koa 中间软件并快速化 & Hapi 插件. 用于在日志中添加请求ID的外框解决方案 。 检查出来 这个博客文章 说明其背后的理由 cls-rtracer。 。 。 .
自动生成一个 UUID V1 值, 作为每个请求的 ID , 并将其保存在 AsyncLocalStorage (CLS核心 API,见 这个博客文章) (中文(简体) ). 如果请求中包含 X-Request-Id 页眉, 使用它的值 。 允许稍后在路线的任何地方获取生成的请求ID,并用于伐木或任何其他目的.
与快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取.
支持的 Node.js 版本
作为 cls-rtracer v2 取决于 AsyncLocalStorage API,它要求节点 12.1.7.0+, 13.14.0+,或 14.0.0+. 如果你碰巧使用一个更古老的Node.js版本,你应该使用 cls-rtracer 单曲 基础 cls-hooked。 。 。 .
如何使用 - 步骤一
安装 :
npm install --save cls-rtracer
TypeScript用户的说明:包含打字.
如何使用 - 第2步(共同指令)
使用库在第一个需要访问请求ID的中间软件之前提供的中间软件(或插件). 注意一些中间软件,可能导致CLS上下文(即Async Hooks执行路径)丢失. 为避免这类问题,您应当使用不需要访问的第三方中间软件来请求ID 在此之前 你用这个中间软件。 参见第20期作例.
如何使用它 - 第2步( Express users)
使用库提供的中间软件 :
const express = require('express')
const rTracer = require('cls-rtracer')
const app = express()
// any third party middleware that does not need access to request ids goes here
// ...
app.use(rTracer.expressMiddleware())
// optionally, you can override default middleware config:
// app.use(rTracer.expressMiddleware({
// useHeader: true,
// headerName: 'X-Your-Request-Header'
// }))
// all code in middlewares, starting from here, has access to request ids
在收到的请求中获取请求ID:
// an example middleware for a generic find entity endpoint
app.get('/api/v1/entity/{id}', (req, res, next) => {
entityService.find(req.params.id)
.then((entity) => {
// you can obtain the request id here
const requestId = rTracer.id()
console.log(`requestId: ${requestId}`)
res.json(entity)
})
.catch(next)
})
您可以从无法访问快递的代码获取相同的请求ID' req 对象.
// an imaginary entity-service.js
async function find (entityId) {
// you can obtain the request id here
const requestId = rTracer.id()
// ...
}
如何使用 - 第2步(快活用户)
使用库提供的插件 :
const fastify = require('fastify')()
const rTracer = require('cls-rtracer')
// any third party plugin that does not need access to request ids goes here
// ...
fastify.register(rTracer.fastifyPlugin)
// optionally, you can override default plugin config:
// fastify.register(rTracer.fastifyPlugin, {
// useHeader: true,
// headerName: 'X-Your-Request-Header'
// }))
// all code in plugins or handlers, starting from here, has access to request ids
处理者收到请求时获取请求编号 :
// an example handler for a generic find entity endpoint
// router config is skipped for the sake of simplicity
app.get('/test', async (request, reply) => {
const entity = await entityService.find(request.params.id)
// you can obtain the request id here
const requestId = rTracer.id()
console.log(`requestId: ${requestId}`)
reply.send(entity)
})
您可以从无法访问 Fastify 的代码访问相同的请求 ID request 对象.
// an imaginary entity-service.js
async function find (entityId) {
// you can obtain the request id here
const requestId = rTracer.id()
// ...
}
后遗症 :
有一种连接式的中间软件可供Fastify v2使用,但它被贬值并可能在即将发布的版本中被移除. 如果您碰巧在应用程序中使用了它,应当迁移到快取插件.
fastify.use(rTracer.fastifyMiddleware())
如何使用 - 第2步(Koa用户)
使用库提供的中间软件 :
const Koa = require('koa')
const rTracer = require('cls-rtracer')
const app = new Koa()
// any third party middleware that does not need access to request ids goes here
// ...
app.use(rTracer.koaMiddleware())
// optionally, you can override default middleware config:
// app.use(rTracer.koaMiddleware({
// useHeader: true,
// headerName: 'X-Your-Request-Header'
// }))
// all code in middlewares, starting from here, has access to request ids
在收到的请求中获取请求ID:
// an example middleware for a generic find entity endpoint
// router config is skipped for the sake of simplicity
app.use(async (ctx) => {
const entity = await entityService.find(req.params.id)
// you can obtain the request id here
const requestId = rTracer.id()
console.log(`requestId: ${requestId}`)
ctx.body = entity
})
您可以从无法访问 Koa 代码的代码获取相同的请求 ID ctx 对象.
// an imaginary entity-service.js
async function find (entityId) {
// you can obtain the request id here
const requestId = rTracer.id()
// ...
}
Koa v1 支持
对 Koa v1 使用 koaV1Middleware(options) 函数.
如何使用它 - 步骤 2 (Hapi 用户)
使用库提供的插件 :
const Hapi = require('@hapi/hapi')
const rTracer = require('cls-rtracer')
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
})
// any third party plugin that does not need access to request ids goes here
// ...
await server.register({
plugin: rTracer.hapiPlugin
})
// optionally, you can override default middleware config:
// await server.register({
// plugin: rTracer.hapiPlugin,
// options: {
// useHeader: true,
// headerName: 'X-Your-Request-Header'
// }
// })
// all code in routes, starting from here, has access to request ids
}
init()
根据收到的请求在路由处理器中获取请求ID:
// an example route for a generic find entity endpoint
server.route({
method: 'GET',
path: '/test',
handler: async (request, h) => {
const entity = await entityService.find(request.params.id)
// you can obtain the request id here
const requestId = rTracer.id()
console.log(`requestId: ${requestId}`)
return entity
}
})
您可以从无法访问 Hapi 的代码获取相同的请求 ID request 对象.
// an imaginary entity-service.js
async function find (entityId) {
// you can obtain the request id here
const requestId = rTracer.id()
// ...
}
与伐木机整合
这个库的主要用途是请求id生成和日志自动化. 您可以在一个地方与任何日志库整合,并在您的应用程序的日志中获取请求ID.
在日志中没有请求 id 作为关联值, 您将无法确定哪些日志条目属于处理同一请求的代码 。 你可以手动生成请求代号,并将其存储在快递中' req 对象(或快取) request,或者科亚的 ctx),但随后您必须明确将对象传入路由上所有其他模块. 这时候 cls-rtracer 来救援!
这就是你如何融入 cls-rtracer 与 温通最受欢迎的伐木图书馆之一.
const { createLogger, format, transports } = require('winston')
const { combine, timestamp, printf } = format
// a custom format that outputs request id
const rTracerFormat = printf((info) => {
const rid = rTracer.id()
return rid
? `${info.timestamp} [request-id:${rid}]: ${info.message}`
: `${info.timestamp}: ${info.message}`
})
const logger = createLogger({
format: combine(
timestamp(),
rTracerFormat
),
transports: [new transports.Console()]
})
Express、Fastify和Koa的完整样本见 /samples/ 目录.
配置
这些是用于中件/插件函数的现有配置选项 。 所有配置条目都是可选的 .
{
// Add request id to response header (default: false).
// If set to true, the middleware/plugin will add request id to the specified
// header. Use headerName option to specify header name.
echoHeader: false,
// Respect request header flag (default: false).
// If set to true, the middleware/plugin will always use a value from
// the specified header (if the value is present).
useHeader: false,
// Request/response header name, case insensitive (default: 'X-Request-Id').
// Used if useHeader/echoHeader is set to true.
headerName: 'X-Request-Id',
// A custom function to generate your request ids (default: UUID v1).
// The function will receive the intercepted request (as-is from the framework
// being used) as its first argument. The returned id could be a usual string,
// or a number, or any custom object, like in the example below.
// Ignored if useHeader is set to true.
requestIdFactory: (req) => ({
id: 'Your request id',
customHeader: req.headers['X-Custom-Header']
}),
// Use request id generated by Fastify instead of generating a new id.
// Only available for the Fastify plugin.
useFastifyRequestId: false,
}
高级特性
在某些情况下,您可能想要在请求处理器范围之外,比如在作为背景工作的代码中提供身份标识。 在这种情况下,你可以使用 runWithId() 函数 :
const rTracer = require('cls-rtracer')
rTracer.runWithId(() => {
console.log(rTracer.id()) // id is available here
setInterval(() => {
console.log(rTracer.id()) // and here
}, 1000)
})
// you may override id by providing the 2nd argument
rTracer.runWithId(() => {
// ...
}, 42) // 42 is the id override here
// async/await syntax is also supported, as `runWithId()`
// returns the result of `fn`
await rTracer.runWithId(myAsyncFn)
解决问题
为了避免奇怪的行为:
- 确保您使用任何不需要访问的第三方中间软件( 或插件) 来请求 ID 在此之前 您使用时
cls-rtracer。见 本节。 。 。 .
注意:你使用一个难得的图书馆的机会很小, 所以,如果在上下文(因此,请求id)在某处Async调用链被丢失时面临问题,请将GitHub问题提交详细描述.
业绩影响
注意,由于Async Hooks API的使用,这个库对您的应用具有一定的性能影响. 因此,你需要决定,在没有任何锅炉板的木头中追踪请求的好处,是否比性能影响的缺点,对您更有价值.
这个图书馆的作者做了一些基本的性能测试. 见 这个推特 看结果。 间接费用也有所减少 cls-rtracer v2 由于迁移到核心API. 见 这个推特 学习更多.
许可证
根据麻省理工学院的许可.