Logging
Proper logging is an essential practice for any application. Logs are crucial, and Fastify provides an almost perfect implementation along with Pino.
Logs are JSON-formatted objects written to a stream, typically stdout
.
To enable Fastify logging, you need to activate the logger
property of Fastify.
import fastify from 'fastify';
const app = fastify({
logger: true
});
By doing this, you will automatically see logs in the console:
{"level":30,"time":1712271794784,"pid":1026864,"hostname":"kitajs","msg":"Server listening at http://127.0.0.1:1227"}
All logs will follow the above format. Having the JSON format is super important for servers in production, where these logs can be collected and analyzed by tools like Elasticsearch, Logstash, and Kibana.
Pretty logs
JSON is not human-readable, and during development, we need friendlier feedback. To achieve this, it is recommended to use pino-pretty.
npm install -D pino-pretty
With/Without pino-pretty
// Without pino-pretty
{"level":30,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo","myProperty":1}
// With pino-pretty
[14:35:28.992] INFO (42): hello world
myProperty: 1
2
3
4
5
6
Formatting logs requires processing, and one thing we want to avoid is diverting processing power that could be used to serve requests to format logs.
Therefore, it is recommended to use pino-pretty
via pipelines.
In your development script, after using the node
command, you can use the pino-pretty
command to format the logs.
{
"scripts": {
// Example of a dev script.
"dev:server": "node --watch dist/index.js | pino-pretty"
}
}
2
3
4
5
6
In production, it is still recommended to store logs as JSON, and if necessary, you can use pino-pretty
later to format logs in a post processing step.
Read more about Pino and Pino Pretty.
Using in a route
The FastifyRequest
interface has a log
method that automatically includes the req.id
in the log to correlate logs from the same request.
import type { FastifyRequest } from 'fastify';
export function get({ log, ip }: FastifyRequest) {
log.info({ ip }, 'I am a log message');
return { hello: 'Arthur!' };
}
There you go, you automatically have correlated logs for each request:
[20:25:48.786] INFO (1060221): incoming request
reqId: "req-1"
req: {
"method": "GET",
"url": "/",
"hostname": "localhost:1227",
"remoteAddress": "127.0.0.1",
"remotePort": 52670
}
[20:25:48.787] INFO (1060221): I am a log message
reqId: "req-1"
ip: "127.0.0.1"
[20:25:48.788] INFO (1060221): request completed
reqId: "req-1"
res: {
"statusCode": 200
}
responseTime: 1.7987719997763634
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Filtering logs
By default, when using logger: true
, only logs with a level of info
or higher are displayed.
To display logs with lower levels, such as trace
or debug
, you need to change the logger level.
import fastify from 'fastify';
const app = fastify({
logger: {
level: process.env.LOG_LEVEL || 'trace'
}
});
It is recommended to filter logs in production to avoid unnecessary logs. You can do this by either changing the logger.level
as demonstrated above, or by using the -L
flag of pino pretty:
{
"scripts": {
// Shows debug+ logs
"dev:server": "node --watch dist/index.js | pino-pretty -L debug"
}
}
2
3
4
5
6