Gathering detailed insights and metrics for @cjanietz/cls-rtracer
Gathering detailed insights and metrics for @cjanietz/cls-rtracer
Gathering detailed insights and metrics for @cjanietz/cls-rtracer
Gathering detailed insights and metrics for @cjanietz/cls-rtracer
cls-rtracer
Express & Koa middlewares and Fastify & Hapi plugins for CLS-based request id generation, batteries included
nestjs-cls
A continuation-local storage module compatible with NestJS's dependency injection.
cls-hooked
CLS using AsynWrap instead of async-listener - Node >= 4.7.0
@types/cls-hooked
TypeScript definitions for cls-hooked
npm install @cjanietz/cls-rtracer
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
311 Stars
70 Commits
24 Forks
7 Watching
2 Branches
11 Contributors
Updated on 22 Sept 2024
JavaScript (100%)
Cumulative downloads
Total Downloads
Last day
-18.7%
901
Compared to previous day
Last week
8.7%
3,983
Compared to previous week
Last month
34.4%
14,524
Compared to previous month
Last year
228.5%
282,840
Compared to previous year
1
Request Tracer - Express & Koa middlewares and Fastify & Hapi plugins for CLS-based request id generation, batteries included. An out-of-the-box solution for adding request ids into your logs. Check out this blog post that describes the rationale behind cls-rtracer
.
Automatically generates a UUID V1 value as the id for each request and stores it in AsyncLocalStorage
(CLS core API, see this blog post). Optionally, if the request contains X-Request-Id
header, uses its value instead. Allows to obtain the generated request id anywhere in your routes later and use it for logging or any other purposes.
Tested and works fine with Express v4, Fastify v2 and v3, Koa v1 and v2, and Hapi v18.
As cls-rtracer
v2 depends on AsyncLocalStorage API
, it requires Node.js 12.17.0+, 13.14.0+, or 14.0.0+. If you happen to use an older Node.js version, you should use cls-rtracer
v1 which is based on cls-hooked
.
Install:
1npm install --save cls-rtracer
Note for TypeScript users: typings are included.
Use the middleware (or plugin) provided by the library before the first middleware that needs to have access to request ids. Note that some middlewares, may cause CLS context (i.e. Async Hooks execution path) to get lost. To avoid such issues, you should use any third party middleware that does not need access to request ids before you use this middleware. See issue #20 as an example.
Use the middleware provided by the library:
1const express = require('express') 2const rTracer = require('cls-rtracer') 3 4const app = express() 5// any third party middleware that does not need access to request ids goes here 6// ... 7 8app.use(rTracer.expressMiddleware()) 9// optionally, you can override default middleware config: 10// app.use(rTracer.expressMiddleware({ 11// useHeader: true, 12// headerName: 'X-Your-Request-Header' 13// })) 14 15// all code in middlewares, starting from here, has access to request ids
Obtain request id in middlewares on the incoming request:
1// an example middleware for a generic find entity endpoint 2app.get('/api/v1/entity/{id}', (req, res, next) => { 3 entityService.find(req.params.id) 4 .then((entity) => { 5 // you can obtain the request id here 6 const requestId = rTracer.id() 7 console.log(`requestId: ${requestId}`) 8 9 res.json(entity) 10 }) 11 .catch(next) 12})
You can access the same request id from code that does not have access to the Express' req
object.
1// an imaginary entity-service.js 2async function find (entityId) { 3 // you can obtain the request id here 4 const requestId = rTracer.id() 5 // ... 6}
Use the plugin provided by the library:
1const fastify = require('fastify')() 2const rTracer = require('cls-rtracer') 3 4// any third party plugin that does not need access to request ids goes here 5// ... 6 7fastify.register(rTracer.fastifyPlugin) 8// optionally, you can override default plugin config: 9// fastify.register(rTracer.fastifyPlugin, { 10// useHeader: true, 11// headerName: 'X-Your-Request-Header' 12// })) 13 14// all code in plugins or handlers, starting from here, has access to request ids
Obtain request id in handlers on the incoming request:
1// an example handler for a generic find entity endpoint 2// router config is skipped for the sake of simplicity 3app.get('/test', async (request, reply) => { 4 const entity = await entityService.find(request.params.id) 5 // you can obtain the request id here 6 const requestId = rTracer.id() 7 console.log(`requestId: ${requestId}`) 8 9 reply.send(entity) 10})
You can access the same request id from code that does not have access to the Fastify's request
object.
1// an imaginary entity-service.js 2async function find (entityId) { 3 // you can obtain the request id here 4 const requestId = rTracer.id() 5 // ... 6}
There is a connect-style middleware available for Fastify v2, but it is deprecated and may be removed in one of upcoming releases. If you happen to use it in your application, you should migrate to the Fastify plugin.
1fastify.use(rTracer.fastifyMiddleware())
Use the middleware provided by the library:
1const Koa = require('koa') 2const rTracer = require('cls-rtracer') 3 4const app = new Koa() 5// any third party middleware that does not need access to request ids goes here 6// ... 7 8app.use(rTracer.koaMiddleware()) 9// optionally, you can override default middleware config: 10// app.use(rTracer.koaMiddleware({ 11// useHeader: true, 12// headerName: 'X-Your-Request-Header' 13// })) 14 15// all code in middlewares, starting from here, has access to request ids
Obtain request id in middlewares on the incoming request:
1// an example middleware for a generic find entity endpoint 2// router config is skipped for the sake of simplicity 3app.use(async (ctx) => { 4 const entity = await entityService.find(req.params.id) 5 // you can obtain the request id here 6 const requestId = rTracer.id() 7 console.log(`requestId: ${requestId}`) 8 9 ctx.body = entity 10})
You can access the same request id from code that does not have access to the Koa's ctx
object.
1// an imaginary entity-service.js 2async function find (entityId) { 3 // you can obtain the request id here 4 const requestId = rTracer.id() 5 // ... 6}
For Koa v1 use the koaV1Middleware(options)
function.
Use the plugin provided by the library:
1const Hapi = require('@hapi/hapi') 2const rTracer = require('cls-rtracer') 3 4const init = async () => { 5 const server = Hapi.server({ 6 port: 3000, 7 host: 'localhost' 8 }) 9 // any third party plugin that does not need access to request ids goes here 10 // ... 11 12 await server.register({ 13 plugin: rTracer.hapiPlugin 14 }) 15 16 // optionally, you can override default middleware config: 17 // await server.register({ 18 // plugin: rTracer.hapiPlugin, 19 // options: { 20 // useHeader: true, 21 // headerName: 'X-Your-Request-Header' 22 // } 23 // }) 24 25 // all code in routes, starting from here, has access to request ids 26} 27 28init()
Obtain request id in route handlers on the incoming request:
1// an example route for a generic find entity endpoint 2server.route({ 3 method: 'GET', 4 path: '/test', 5 handler: async (request, h) => { 6 const entity = await entityService.find(request.params.id) 7 // you can obtain the request id here 8 const requestId = rTracer.id() 9 console.log(`requestId: ${requestId}`) 10 11 return entity 12 } 13})
You can access the same request id from code that does not have access to the Hapi's request
object.
1// an imaginary entity-service.js 2async function find (entityId) { 3 // you can obtain the request id here 4 const requestId = rTracer.id() 5 // ... 6}
The main use case for this library is request id generation and logging automation. You can integrate with any logger library in a single place and get request ids in logs across your application.
Without having request id, as a correlation value, in your logs, you will not be able to determine which log entries belong to code that handles the same request. You could generate request ids manually and store them in the Express' req
object (or Fastify's request
, or Koa's ctx
), but then you will have to explicitly pass the object into all other modules on the route. And that's when cls-rtracer
comes to the rescue!
Complete samples for Express, Fastify and Koa are available in /samples/
directory.
Here is how you can integrate cls-rtracer
with winston, one of most popular logging libraries.
1const { createLogger, format, transports } = require('winston') 2const { combine, timestamp, printf } = format 3 4// a custom format that outputs request id 5const rTracerFormat = printf((info) => { 6 const rid = rTracer.id() 7 return rid 8 ? `${info.timestamp} [request-id:${rid}]: ${info.message}` 9 : `${info.timestamp}: ${info.message}` 10}) 11 12const logger = createLogger({ 13 format: combine( 14 timestamp(), 15 rTracerFormat 16 ), 17 transports: [new transports.Console()] 18})
This is how you can integrate cls-rtracer
with pino logger.
1 2// mixin function adds properties of the returned object to the logged JSON. 3const logger = require('pino')({ 4 mixin () { 5 return { requestId: rTracer.id() } 6 } 7}) 8
These are the available config options for the middleware/plugin functions. All config entries are optional.
1{ 2 // Add request id to response header (default: false). 3 // If set to true, the middleware/plugin will add request id to the specified 4 // header. Use headerName option to specify header name. 5 echoHeader: false, 6 // Respect request header flag (default: false). 7 // If set to true, the middleware/plugin will always use a value from 8 // the specified header (if the value is present). 9 useHeader: false, 10 // Request/response header name, case insensitive (default: 'X-Request-Id'). 11 // Used if useHeader/echoHeader is set to true. 12 headerName: 'X-Request-Id', 13 // A custom function to generate your request ids (default: UUID v1). 14 // The function will receive the intercepted request (as-is from the framework 15 // being used) as its first argument. The returned id could be a usual string, 16 // or a number, or any custom object, like in the example below. 17 // Ignored if useHeader is set to true. 18 requestIdFactory: (req) => ({ 19 id: 'Your request id', 20 customHeader: req.headers['X-Custom-Header'] 21 }), 22 // Use request id generated by Fastify instead of generating a new id. 23 // Only available for the Fastify plugin. 24 useFastifyRequestId: false, 25}
If you need something different from the default UUID v1 for id generation, you should use the requestIdFactory
function available in the middleware/plugin config. Note that this function allows storing any object as the id, not only primitive values or strings.
In certain situations you may want to have an id available outside of the request handler scope, say, in a code that acts as a background job. In this case you may use the runWithId()
function:
1const rTracer = require('cls-rtracer') 2 3rTracer.runWithId(() => { 4 console.log(rTracer.id()) // id is available here 5 setInterval(() => { 6 console.log(rTracer.id()) // and here 7 }, 1000) 8}) 9 10// you may override id by providing the 2nd argument 11rTracer.runWithId(() => { 12 // ... 13}, 42) // 42 is the id override here 14 15// async/await syntax is also supported, as `runWithId()` 16// returns the result of `fn` 17await rTracer.runWithId(myAsyncFn)
To avoid weird behavior:
cls-rtracer
. See this section.Note: there is a small chance that you are using one of rare libraries that do not play nice with Async Hooks API. So, if you face the issue when the context (and thus, the request id) is lost at some point of async calls chain, please submit GitHub issue with a detailed description.
Note that this library has a certain performance impact on your application due to Async Hooks API usage. So, you need to decide if the benefit of being able to trace requests in logs without any boilerplate is more valuable for you than the disadvantage of performance impact.
The author of this library did some basic performance testing. See this tweet to see the results. The overhead also decreased in cls-rtracer
v2 due to migration to the core API. See this tweet to learn more.
Licensed under MIT.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
no dangerous workflow patterns detected
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
Reason
Found 8/29 approved changesets -- score normalized to 2
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More