A logger using `express-http-context` to provide JSON logging with request ids and meta data to application logs.
npm install @askeladden/logger-nodeA logger using express-http-context to provide JSON logging with request ids and
meta data to application logs.
After setting up the logger (see below), logging can be done with our without context data:
``ts
import Logger from '../Logger.ts';
// Simple logging
Logger.info('Fetching available from Waitwhile');
// With related data
enum DOMAINS {
ONBOARDING = 'ONBOARDING',
BOOKING = 'BOOKING',
AUTH = 'AUTH',
}
Logger.info('User signed up', {
customerId: '1234',
domain: DOMAINS.ONBOARDING,
action: 'USER_SIGNUP',
value: '30DAY_TRIAL',
});
// With custom data
Logger.info('Something special happened', {
myownkey: 18,
anotherCustomKey: {
custom: 1,
},
});
`
Setting up logging is done in two steps:
1. Adding a logger.
2. Add middleware to capture http context
3. Extracting logs from our hosting provider (Heroku, AWS, Vercel), to a log provider like Logtail, Logz.io or LogDNA.
yarn add @askeladden/logger-node
`ts
/*
* Logger.ts
* Usage:
* import Logger from '../Logger.ts';
*
* Logger.info('User signed up', {
* action: 'USER_SIGNUP',
* })
*/
import LoggerNode from '@askeladden/logger-node';
import config from './config';
const Logger = new LoggerNode({
logLevel: process.env.LOG_LEVEL || 'debug',
env: process.env.ENVIRONMENT || 'development',
serviceName: 'candystore-api',
});
export default Logger;
`
The middleware adds metadata to requests with express-http-context.
The following context fields are included in the output log.
`ts`
reqId;
method;
url;
path;
query;
userId;
extra;
By adding this middleware, we:
- set url, path, query, method automatically
- extract reqId from header X-Request-Id, or generate a new one if header not present.req.user?.id || req.user?.sub
- extract userId from or a JWT token in the Authorization header.
#### 2.1 Node express
`ts
import { middlewares } from '@askeladden/logger-node';
const addMiddlewares = (app: Express) => {
app.use(middlewares.node);
};
`
#### 2.2. NextJS
Adding the logging middleware to NextJS requires us to add support for express-middlewares.
yarn add next-connect
`ts
import type { NextApiRequest, NextApiResponse } from "next";
import baseHandler from "../../middlewares/handler";
import Logger from "../../logger";
const handler = baseHandler().get((req, res) => {
Logger.info("User signed up", {
action: "USER_SIGNUP",
customerId: '123',
});
res.status(200).json({ id: '123' });
}
export default handler;
`
`ts
// middlewares/handler.ts
import nc from 'next-connect';
import logMiddleware from '../logger/middleware';
import { NextApiRequest, NextApiResponse } from 'next';
import { RequestContext } from '../logger/types';
import { getHttpContext } from '../logger/request.utils';
import Logger from '../logger';
export default function requestHandler
// Context of express-http-context is not set inside onError handler
// and instead set manually here from a middleware
let context: RequestContext | undefined;
return nc
onError: (err, req: NextApiRequest, res: NextApiResponse, next) => {
// (Suitable spot for adding Sentry event)
Logger.error(String(err), { ...context, stack: err.stack });
res.status(500).send({ message: 'An error occured' });
},
})
.use(logMiddleware)
.use((req, res, next) => {
context = getHttpContext();
next();
});
}
`
If you need to set additional http context data, you can do this by populating extra like this:
`ts
import httpContext from 'express-http-context';
import { middlewares } from '@askeladden/logger-node';
app.use(middlewares.node);
app.use((req, res, next) => {
httpContext.set('extra', {
myfield: 'customvalue',
});
next();
});
``
_You can also override fields that way._
Adding log management is done by consuming the log drains of
the infrastructure that the app runs on.
E.g. for Logtail on Heroku, see this guide.