You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This library provides an express middleware enabling request bundling. The library instantiates child loggers with trace/span IDs and makes it available under req.log for each request. This approach requires to pass the logger instance through all functions of your code and increases complexity.
An alternative approach could be to use asynchronous context tracking. The express middleware would need to call the downstream middlewares within the context of AsyncLocalStorage. With this, users of this library could simple create one global logger instance and make use of it. The logger instance will automatically add the trace/span IDs based on the current request context.
Describe alternatives you've considered
It is possible to do the implementation without the library. But for convenience it would be better to provide this functionality within this library.
Additional context/notes
I have implemented this with some tweaks. See below for my code. It could be easily adopted to be part of this library.
requestAwareLogger.ts
import{GoogleAuth}from'google-auth-library';import{LOGGING_SAMPLED_KEY,LOGGING_SPAN_KEY,LOGGING_TRACE_KEY,LoggingWinston}from'@google-cloud/logging-winston';import{HttpRequest,Log,middleware}from'@google-cloud/logging';import{NextFunction,Request,Response}from'express';importwinstonfrom'winston';import{Handler}from'express';import{AsyncLocalStorage}from'node:async_hooks';exportconstcreateRequestAwareLogger=async<T>(logger: winston.Logger,asyncLocalStorage: AsyncLocalStorage<T>): Promise<Handler[]>=>{consttraceBundleMiddlware: Handler=awaitcreateTraceBundleMiddlware(logger);constrequestContextMiddleware: Handler=awaitcreateRequestContextMiddleware(asyncLocalStorage);return[traceBundleMiddlware,requestContextMiddleware];};constcreateTraceBundleMiddlware=async(logger: winston.Logger)=>{constprojectId=awaitnewGoogleAuth().getProjectId();// This function will be invoked at the very end of a request and logs the http request and responseconstemitParentLogEntry=(httpRequest: HttpRequest,trace: string,span?: string,sampled?: boolean)=>{constcloudTransport=logger.transports.find((t)=>tinstanceofLoggingWinston);constrequestLogName=Log.formatName_(projectId,`${cloudTransport?.common.logName}_reqlog`);logger.info(httpRequest.requestUrl||'http request',{logName: requestLogName,[LOGGING_TRACE_KEY]: trace,[LOGGING_SPAN_KEY]: span,[LOGGING_SAMPLED_KEY]: sampled,
httpRequest
});};// This function is responsible to create an object (trace bundle) which will be used as the object for the local storageconstcreateTraceBundle=(trace: string,span?: string,sampled?: boolean)=>{return{[LOGGING_TRACE_KEY]: trace,[LOGGING_SPAN_KEY]: span,[LOGGING_SAMPLED_KEY]: sampled};};// This creates a middleware which create the trace bundle and makes it available under req.logreturnmiddleware.express.makeMiddleware(projectId,createTraceBundle,emitParentLogEntry);};// This middleware initiates the request context using the local storage.// See https://nodejs.org/api/async_context.htmlconstcreateRequestContextMiddleware=<T>(asyncLocalStorage: AsyncLocalStorage<T>): Handler=>{return(req: Request,res: Response,next: NextFunction)=>{// eslint-disable-next-line @typescript-eslint/no-explicit-anyconsttraceBundle=(reqasany).log;// Run the next function within the scope of the async local storage.// With this, the remaining middlewares (and route handlers) can access// the the logger making use of the trace bundle.asyncLocalStorage.run(traceBundle,()=>{next();});};};
logger.ts
import{AsyncLocalStorage}from'node:async_hooks';import*aswinstonfrom'winston';import{LoggingWinston}from'@google-cloud/logging-winston';exportconstasyncLocalStorage=newAsyncLocalStorage();// Create a winston format to add the traceId information based on asyncLocalStorage// This function will be run at each log entry.// With this, all logs of a request will be bundled together.constlogBundleFormat=winston.format((transformableInfo: winston.Logform.TransformableInfo)=>{// eslint-disable-next-line @typescript-eslint/no-explicit-anyconststore=asyncLocalStorage.getStore()asany;return{
...transformableInfo,
...store};});// Use Google's LoggingWinston transport to directly write logs to Cloud Logging without writing to stdout/consoleconsttransports: winston.transport[]=[newLoggingWinston({maxEntrySize: 250000// a little less than the maximum log message size of 256 kb})];constlogger=winston.createLogger({transports: transports,format: winston.format.combine(logBundleFormat(),winston.format.json())});exportdefaultlogger;
Now, the express app could look like this:
importexpressfrom'express';importlogger,{asyncLocalStorage}from'./logger.js';import{createRequestAwareLogger}from'./requestAwareLogger.js';constapp=express();constrequestAwareLogger=awaitcreateRequestAwareLogger(logger,asyncLocalStorage);app.use(requestAwareLogger);// all logs in the downstream middlewares have context awarenessapp.use((req,res)=>{logger.info("Yay, I am context aware!");logger.info("And I am as well - we will be logged in a bundle.");res.status(200).send();})// run serverconstserver=app.listen(3000,()=>{logger.info(`Express.js server is running on port 3000`);});exportdefaultapp;
The text was updated successfully, but these errors were encountered:
What would you like to see in the library?
This library provides an express middleware enabling request bundling. The library instantiates child loggers with trace/span IDs and makes it available under
req.log
for each request. This approach requires to pass the logger instance through all functions of your code and increases complexity.An alternative approach could be to use asynchronous context tracking. The express middleware would need to call the downstream middlewares within the context of
AsyncLocalStorage
. With this, users of this library could simple create one global logger instance and make use of it. The logger instance will automatically add the trace/span IDs based on the current request context.Describe alternatives you've considered
It is possible to do the implementation without the library. But for convenience it would be better to provide this functionality within this library.
Additional context/notes
I have implemented this with some tweaks. See below for my code. It could be easily adopted to be part of this library.
requestAwareLogger.ts
logger.ts
Now, the express app could look like this:
The text was updated successfully, but these errors were encountered: