Giter Club home page Giter Club logo

cls-rtracer's Introduction

Build Status Coverage Status npm npm

cls-rtracer

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.

Supported Node.js versions

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.

How to use it - Step 1

Install:

npm install --save cls-rtracer

Note for TypeScript users: typings are included.

How to use it - Step 2 (Common instructions)

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.

How to use it - Step 2 (Express users)

Use the middleware provided by the library:

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

Obtain request id in middlewares on the incoming request:

// 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)
})

You can access the same request id from code that does not have access to the Express' req object.

// an imaginary entity-service.js
async function find (entityId) {
  // you can obtain the request id here
  const requestId = rTracer.id()
  // ...
}

How to use it - Step 2 (Fastify users)

Use the plugin provided by the library:

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

Obtain request id in handlers on the incoming request:

// 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)
})

You can access the same request id from code that does not have access to the Fastify's request object.

// an imaginary entity-service.js
async function find (entityId) {
  // you can obtain the request id here
  const requestId = rTracer.id()
  // ...
}

Legacy Fastify middleware

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.

fastify.use(rTracer.fastifyMiddleware())

How to use it - Step 2 (Koa users)

Use the middleware provided by the library:

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

Obtain request id in middlewares on the incoming request:

// 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
})

You can access the same request id from code that does not have access to the Koa's ctx object.

// an imaginary entity-service.js
async function find (entityId) {
  // you can obtain the request id here
  const requestId = rTracer.id()
  // ...
}

Koa v1 support

For Koa v1 use the koaV1Middleware(options) function.

How to use it - Step 2 (Hapi users)

Use the plugin provided by the library:

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()

Obtain request id in route handlers on the incoming request:

// 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
  }
})

You can access the same request id from code that does not have access to the Hapi's request object.

// an imaginary entity-service.js
async function find (entityId) {
  // you can obtain the request id here
  const requestId = rTracer.id()
  // ...
}

Integration with loggers

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.

Winston

Here is how you can integrate cls-rtracer with winston, one of most popular logging libraries.

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()]
})

Pino

This is how you can integrate cls-rtracer with pino logger.

// mixin function adds properties of the returned object to the logged JSON.
const logger = require('pino')({
    mixin () {
        return { requestId: rTracer.id() }
    }
})

Configuration

These are the available config options for the middleware/plugin functions. All config entries are optional.

{
  // 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,
}

Advanced features

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:

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)

Troubleshooting

To avoid weird behavior:

  • Make sure you use any third party middleware (or plugin) that does not need access to request ids before you use 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.

Performance impact

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.

License

Licensed under MIT.

cls-rtracer's People

Contributors

batrudinych avatar crccheck avatar gabriel-pinheiro avatar marciopd avatar puzpuzpuz avatar simenb avatar sluukkonen avatar srknzl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

cls-rtracer's Issues

Why `cls-hooked` is peer dependency?

IMHO peer dependencies are required if you are building plugins for some library. For example lets say you are building webpack plugin then webpack will become your peer dependency.

And some who is using webpack can use use your plugin.

But here I think we only need to use this library cls-rtracer, and I am not using cls-hooked.

By this logic I think we can add cls-hooked under your dependencies section from peerDependencies.

Any thoughts?

I can raise a pull request if you guys agree.

Why does the Hapi plugin implementation use enterWith() instead of run()?

Hi, I had a question on the implementation for the Hapi plugin. I noticed that for the rest of the other framework middleware/plugins you use AsyncLocalStorage.run() but for the Hapi plugin enterWith() is used instead? Why is that and what is it about Hapi specifically that it had to be done this way?

From the Node docs

run() should be preferred over enterWith() unless there are strong reasons to use the latter method.

https://nodejs.org/api/async_context.html#asynclocalstorageenterwithstore

Thank you

add Additional information along with requestId

Hi, this question might be wrong but I want to customise the cls-rtracer with additional feature.

Along with requestId, I also want to pass a custom header value and retrieve it later and may be used in my logger to log the info. with requestIdFactory I could set a requestId. is it possible to save additional information in the same context, let's say some sessionId? In our application, both are different. Sometimes sessionId might not exist. storing requestId+sessionId as requestId seems not a solution rather feels like a hack.

Extend support for node > 10.4.0

Is it possible to extend library support for versions greater than 10.4.0?

npm WARN notsup (current: {"node":"10.18.0","npm":"6.13.4"})
Unsupported engine for [email protected]: wanted: {"node":">=8.0.0 - <10.0.0 || >=10.4.0"}

It's a very useful toot, and I'd like to add it to my project!

I have a question

Hi, I'm so impressed by cls-rtracer so I researched about cls and cls-hooked.

I'm creating my module using cls-hooked to learn deeply.

I've following and trying this post: here

However, It's a little bit different implemented code against your code.
https://github.com/puzpuzpuz/cls-rtracer/blob/master/index.js#L61

And my code is :

const traceId = (opts: { test: number } | undefined): Middleware => {
  return async (ctx: Context, next: Function) => {
    // req and res are event emitters. We want to access CLS context inside of their event callbacks
    clsNamespace.bindEmitter(ctx.req);
    clsNamespace.bindEmitter(ctx.res);

    const traceID = uuidv4();

    clsNamespace.run(() => {
      clsNamespace.set('traceID', traceID);
      return next();
    });
  };
};

I'm wondering why you didn't use clsNamespace.run() .
I think I'm not enough to understand at all. Is it same working code?

best wishes.

Steve.

Can you set a unique id?

We have a trace id coming from a third party service and would like to use that one, in that request for the logs in our service that calls it. is that possible? here is an example of what we are hoping for

const rTracer = require('cls-rtracer');

const fakeEndpoint = async (req, res) => {
  const traceIdFromThirdParty = req.traceId;
  
  rTracer.setId(traceIdFromThirdParty);
}

id generation

Discovered this lib yesterday and it works perfectly for my needs, the main thing I have an issue with is that it is using uuid v1.Could you please upgrade, or allow us to initialize it with our own generation function?

ID not got when using mongoose debug

I am using mongoose like below

require('mongoose').set('debug', function (collectionName, method, query, options) {
  logger.info(`db.${collectionName}.${method}(${JSON.stringify(query)}, ${JSON.stringify(options)});`);
});

I have configured my logger to append rTracer.id() when ever its found, above does never get the ID. even though its a http request.

Question about multiple request

This may be a dumb question, but i just wanted to be sure that if a route gets hit by x number of users at the same time (or you know, possibly very close cause we cant always recreate a real world scenario) that the trace ids will be different per question and its not depended on when u define/initial the library or specific to the folder.

thank you

Set ID manually

Our app does some periodic polling, which is started by a setInterval and not an external call to our app. This call is missing a trace id. Would it be possible for us to manually set an ID before kicking of the async calls?

Or code is essentially this

export async function setupBroadcastPolling(client: SomeClient) {
  async function loadMessages() {
    try {
      const data = await loadBroadcastMessages(client);

      if (data) {
        broadcastMessages = data.messages;
      }
    } catch (error) {
      logger.error({ error }, 'Unable to poll broadcast messages');
    }
  }

  // reload every minute
  setInterval(loadMessages, 60 * 1000).unref();

  await loadMessages();
}

It would be great if we could have an ID in our logs in this case as well. In this case I'd love to somehow set an ID if it's missing within the loadMessages function. Our client does a bunch of logging as it works, which are all missing the request ID

How to choose id generation algorithm?

I found it used uuid1 as default implementation.

UUID v1 is unique in theory, but still has potential problem. As we know UUID v1 is generated by using a combination the host computers MAC address and the current date and time. I am worried about node.js cluster. Does it safe on node.js cluster on a single machine?

And also, for some systems, they use their own id generator, I think it should have a way to pass user's own generator.

How to use with WebSocket?

It would be really helpful to know that how to use this package with WebSocket communications.
An example would be very useful too.

Add typings

This seems to be the only library I can find that will allow me to get a request ID from the request object, which I need for pino-http (pinojs/pino-http#63). So I'd love TypeScript support.

Context is lost when using passport.js with session: false

When passport.js is used with session:false the async context is lost and rTracer.id() stops returning an ID. I'm pretty sure this a similar issue as described in the README and #20. That said, it seems to be something slightly different, given that #20 is an issue with express-session and I've explicitly disabled sessions here.

Specifically I'm using passport.js configured to accept JWTs with passport-jwt, using code that looks something like this:

const express = require('express');
const jwt = require('express-jwt');
const passport = require('passport');
const passportJwt = require('passport-jwt');
const rTracer = require('cls-rtracer')

passport.use('jwt', new passportJwt.Strategy(/* jwt options */));

const app = express();

app.use(passport.initialize());

app.use(rTracer.expressMiddleware());

app.use((req, res, next) => {
	console.log(rTracer.id()); // prints an id
});

app.get('/foo',
	passport.authenticate('jwt', {session: false}),
	(req, res) => {
		console.log(rTracer.id()); // prints 'undefined' 
	}
);

app.get('/bar', (req, res) => {
	console.log(rTracer.id()); // prints an id 
});

I assume this is fundamentally an issue with either passport or passport-jwt, and so it's not fixable on your end. Your readme said to submit an issue, though, so I hope this is helpful.

Can't make it work with HAPI

Hello, I can't make it work with HAPI (version 20).

I'm following the documentation :

const server: Server = hapiServer({
    host,
    (...)
})

await server.register({
  plugin: rTracer.hapiPlugin
})    

 (other plugins, routes, ...)

And in a route :

handler: (request: Request, h: ResponseToolkit) => {
  const requestId = rTracer.id()
  console.log(`requestId: ${requestId}`)

rTracer.id() always returns undefined

By debugging in rtracer.js, I can see that I enter als.enterWith with a generated id, but when the route handler is called als.getStore() alwasy returns undefined

What am I missing ?

Fastify Pino logger integration sample

Not an issue, but rather request. Fastify already uses Pino under the hood as de facto logger. I am struggling to integrate cls-tracer with Pino in Fastify, so far without success. Would it be possible to provide an example of using cls-traces with Pino in fastify?

How to add an optional/custom parameter in the cls-tracer express middleware request

I want to pass an additional optional parameter as username to trace the HTTP request and log the entries. Currently, I am using rTracer.id() of cls-rtracer npm module in my express application to trace the http request. I would really appreciate if someone can help me to pass 'username' as an optional parameter using cls-tracer module.

From React Application, I am passing username in headers and making http request to node.js application. I want to capture username in express application and pass to my outbound service request(Node.js Micro service). In the Node.js Microservice, I want to trace the incoming request and log using Username

Dynamically add values to the current rTracer Ids

Is there any way to add new Ids to the existing ones that are already set up with rTracer?

e.g after having set it up with

    app.use(rTracer.koaMiddleware({
        requestIdFactory: (req) => ({
          correlationId: req.get('X-CorrelationId') || randomUUID(),
          otherVars...
        }),
      }));

And then later on, for example after we finish some authentication middleware, and we want to store some extra information only available after that middleware has completed.

So after we can add like Third Party contextual information or where the request came from etc.

Thanks!

Integration with winston JSON

I am not able to read the rtracer.id() in the winston logger config file and set the value as separate attribute in the json log. Can someone please help if the similar is implemented with sample piece of code. Thanks

cls-rtracer causes pino-http >= version 7.1 to log success message (responses) twice

When the cls-rtracer middleware is added before a logging middleware based on pino-http >= 7.1.0 the success messages (responses) are logged twice.

The problem happens since the merge of this PR in pino-http: pinojs/pino-http#219
So after this response event listener was added: res.on('close', onResFinished) in addition to the already existing one res.on('finish', onResFinished)

For further reference please check the rejected issue at pino-http: pinojs/pino-http#234
and the sample app to reproduce the issue provided by @Simon-Campbell : https://github.com/Simon-Campbell/pino-http-double-success

Memory leak in 2.6.1

Hello,

We did a release two days ago that bumped many packages including a cls-rtracer bump from 2.6.0 to 2.6.1.
We quickly started experiencing memory leaks on all our services as shown below (sample).
image
We did rollback the release and then released again yesterday. By dumping the memory I did come to the conclusion that [email protected] was leaking. After rollbacking to 2.6.0 and deploying everything again, leaks have disappeared.

Can't share full heapdump but I did saw a pattern of increasing memory usage of cls-rtracer objects.
image

Then 7 minutes later
image

Also
image

Then 7 minutes later
image

Sporadic tracer id logging

I spent about 2 hours and a good deal of coffee tracking this down and thought it might be beneficial to others.

TL;DR. calling app.use(rTracer.expressMiddleware()) before express-session middleware caused erratic logging behavior. Trace Ids were not correct/showing inconsistently.

In the troubleshooting section of the readme it says to open a github issue if we run across any issues around async calls. Even if this isn't that exact issue I thought I should let someone smarter than me know about it.

I was seeing sporadic tracer id logging happening. See the image below. As you can see there is the normal express started message. Then the log statements for the api requests through some custom authentication middleware. I call the endpoint a few times in the next few minutes. Then magically there is a log with the tracer id, then nothing, then tracer id again, but now its showing the same tracer id for four different requests.
image

I change the following middleware order from:

app.use(rTracer.expressMiddleware());
app.use(session(sessionOptions));
app.use(authenticationMiddleware);

to

app.use(session(sessionOptions));
app.use(rTracer.expressMiddleware());
app.use(authenticationMiddleware);

and no more issues.

image

Example integration with Winston..

Hi guys!

I am new to javascript. Was trying to implement this with express and winston...

app.js

const rTracer = require("cls-rtracer");
const requestId = rTracer.id();
const { createLogger } = require("./logger"); //Here i define the transports and logic to add 
                                                                            // namespace of the file name. 
const logger = createLogger(__filename);
logger.debug("Created logger successfully");
const express = require("express");
const bodyParser = require("body-parser");

const app = express();

app.use(bodyParser.json());
app.use(rTracer.expressMiddleware());

app.use(
  "/graphql",
  graphqlHTTP({
    schema: graphQLSchema,
    rootValue: graphQLResolvers,
    graphiql: true
  })
);

app.listen(3001, () => logger.info("Listening on port 3001")); 

logger.js

  const rTracer = require("cls-rtracer");
  const { createLogger, transports, format } = require("winston");
  const packageInfo = require("../../package.json");
  const path = require("path");

function logger(filename, options = []) {
  let prefix = createPrefix(filename);
  ...
  const loggerConfig = {
    transports: []
  };
  const rid = rTracer.id();
  console.log(rid);

  loggerConfig.transports.push(
      new transports.Console({
        level: LOGGING_CLI_LEVEL,
        format: format.combine(
          format.timestamp(),
          format.label({
            label: `[${prefix}]`
          }),
          format.splat(),
          format.prettyPrint(),
          format.simple(),
          format.ms(),
          format.printf((msg) => {
            return colorizer.colorize(
              msg.level,
              `${msg.label} ${
                msg.timestamp
              } [request-id:${rid}] - ${msg.level.toUpperCase()}: ${
                msg.message
              } ${msg.ms}`
            );
          })
        )
      })
    );
  return createLogger({
    ...options,
    ...loggerConfig
  });

I get 2019-07-29T22:04:24.150Z [request-id:undefined] - INFO: Listening on port 3001 +0ms,
which i think i understand as it has not recieved a request yet. So the id reqId is generated by the middleware only right?
Now, the problem it seems to be in the way i use winston. i import and create the logger per module and as it is imported on load, it always get undefined. and then the reqId is never updated, and never .get from the store. Would be nice an example of multiple files where winston logger is imported. Any hints apreciatted :)

Just to add more context.

streams.js

const { createLogger } = require("../logger");
const logger = createLogger(__filename);
logger.debug("Created logger successfully");
const rTracer = require("cls-rtracer");

async function getData() {
  const requestId = rTracer.id();
  ...
  logger.info("Grabbing records from timestamp: " + timestamp); //get Undefined
  console.log(requestId); // Perfect. Same as in the middleware console.log test.
}

cannot get request id with koa-morgan

So I am trying to include the request-id in my logging, but it doesn't work with koa-morgan.

Here's my code:

import Koa from "koa"
import morgan from "koa-morgan"
import rTracer from "cls-rtracer"

const app = new Koa()
app.use(rTracer.koaMiddleware())
app.use(getMorgan())

app.use(() => {
  console.log("it works here", getReqId())
})

app.listen(8080)

const getReqId = () => {
  return (rTracer.id() ?? "") as string
}

const getMorgan = () => {
  morgan.token("reqId", getReqId)
  return morgan("it doesn't work here  :reqId")
}

then when I run the app

> curl --location --request GET 'localhost:8080/'

it works here fc7170b0-b328-11ed-b4d4-11d1313525c1
it doesn't work here -

cls-rtracer uses synchronous API

Running node with --trace-sync-io for an application that uses cls-rtracer outputs warnings such as this one:

(node:31771) WARNING: Detected use of sync API
    at randomBytes (internal/crypto/random.js:54:31)
    at nodeRNG ([...]/node_modules/cls-rtracer/node_modules/uuid/lib/rng.js:7:17)
    at v4 ([...]/node_modules/cls-rtracer/node_modules/uuid/v4.js:13:52)
    at [...]/node_modules/cls-rtracer/index.js:30:30
    at handle ([...]/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix ([...]/node_modules/express/lib/router/index.js:317:13)
    at [...]/node_modules/express/lib/router/index.js:284:7
    at process_params ([...]/node_modules/express/lib/router/index.js:335:12)
    at next ([...]/node_modules/express/lib/router/index.js:275:10)

Using a synchronous API is undesirable even if the randomBytes() function is normally very fast. If for no other reason, just for the fact that it makes --trace-sync-io much less useful when cls-rtracer creates a lot of noise.

I would expect that a UUID V1 should be plenty good enough for cls-rtracer. uuid.v1() is 5 times faster than uuid.v4() and does not trigger the "WARNING: Detected use of sync API" when run with --trace-sync-io as demonstrated by this little experiment:

$ cat experiment.js 
const uuid = require("uuid");
const { performance } = require("perf_hooks");

const iterations = 5000000;

let t_begin = performance.now();
for (let i = 0; i < iterations; ++i) {
    uuid.v1();
}
console.log(`${iterations} uuid.v1(): ${(performance.now() - t_begin).toFixed(0)} ms`);

t_begin = performance.now();
for (let i = 0; i < iterations; ++i) {
    uuid.v4();
}
console.log(`${iterations} uuid.v4(): ${(performance.now() - t_begin).toFixed(0)} ms`);

$ node experiment.js 
5000000 uuid.v1(): 2941 ms
5000000 uuid.v4(): 14589 ms

Feature request - add generic request context object

Hello,

Thank you for the wonderful library. It's helped a bunch in terms of zeroing in on relevant log messages for a single request.

I have a feature request and was wondering what your thoughts might be around opening up cls-rtracer to have a generic request context object. Rather than simply providing an id that exists throughout the lifetime of the request, there's also a request context object that one can access and put in whatever they want.

Example use case:

Applications that have authorized actions can store the authorized account's id in the request context once authorized. This allows apps to always log an account_id in a single place when relevant, making tracing all of an account's actions very convenient.

Expected requestId object is not returned with runWithId method

In a setup where requestIdFactory is configured to return a map of particular Ids, when we use runWithId without the optional Id parameter, it should use that same requestIdFactory to assign a new map of the required Ids but with the current implementation, it is assigning a uuid id string.

Restify Plugin Support

good job !

my question is tha i am using restify (another nodejs framework),just wanna to know which plugin can i use ? thx!

Cannot access Request ID in form-data request using runWithId

I'm trying to access request id in a form-data request with file upload, I'm using runWithId if the request id is passed in headers otherwise expressMiddleware, but It says undefined. Please find the code below:

const express = require('express');
const app = express();
const RequestTracer = require('cls-rtracer');
const multipart = require('connect-multiparty');
const http = require('http');
const server = http.createServer(app);

app.use((req, res, next) => {
    console.log('app.js->reqId:', req.headers['reqid']); // request-100001

    if (req.headers['reqid']) {
        RequestTracer.runWithId(next, req.headers['reqid']);
    } else {
        RequestTracer.expressMiddleware()(req, res, next);
    }
});

// Form Data request with multipart middleware - Doesn't work
app.post('/upload', multipart(), (req, res, next) => {
    res.json({ reqId: RequestTracer.id() }); // undefined
});

// Form Data request without multipart middleware - Works
app.post('/formdata', (req, res, next) => {
    res.json({ reqId: RequestTracer.id() }); // undefined
});

// Works
app.get('/download', (req, res, next) => {
    res.json({ reqId: RequestTracer.id() }); // request-100001
});

const port = 3000;
server.listen(port);
console.log(`Listening on port: ${port}`);

Here's a repl to run it: https://replit.com/@rohitkhatri/cls-rtracer-code#index.js

It says undefined most of the times when id is passed, but works sometimes, but If the request id is not passed, It always works.

Can anyone please help me here?

cls-rtracer is Generating same request id for multiple requests even for different routes.

This might be the dumbest question you've seen in your life or might be I'm doing something wrong. In past I've used this package and it worked like charm. But recently I wanted to use it in out existing app but I'm facing problem with same request id being used for multiple request. (Different routes even).

I'm attaching the sample app for reference. Please let me know if I'm doing something wrong or missing something.

"cls-rtracer": "^2.6.3",
"express": "^4.18.3",
const express = require('express');
const rTracer = require('cls-rtracer');

const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(rTracer.expressMiddleware());

// Middleware to set request ID in headers
app.use((req, res, next) => {
  const rTracerId = rTracer.id();
  req.headers['X-POC-Request-ID'] = rTracerId;
  res.header('X-POC-Request-ID', rTracerId);
  next();
});

app.get('/fast-route', (req, res) => {
  const requestId = rTracer.id();
  console.log('Request ID in fast-route:', requestId);

  logMe();

  res.status(200).json({ success: true, requestId });
});

app.get('/slow-route', async (req, res) => {
  const requestId = rTracer.id();
  console.log('Request ID in slow-route:', requestId);

  const response = await new Promise((resolve) => {
    setTimeout(() => {
      return resolve({ message: 'Timed Out....', requestId });
    }, 3000);
  });

  res.status(200).json({
    success: true,
    data: response,
  });
});

function logMe() {
  const requestId = rTracer.id();
  console.log('Request ID in logMe:', requestId);
}

// Start server
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.