file-stream-rotator
file-stream-rotator copied to clipboard
Usage with Pino as a Transport
I wanted to use this library as a Pino Transport.
The problem is that Pino can only accept streams using the streams API which then doesn't allow the usage of other transports.
So if for example I wanted to save the logs to a file and also pretty-print them, I would have to setup both the FileStreamRotator
and the pretty-print as streams using the stream API and I can't just setup the pretty-print as a normal transport.
And in my app I have 7 different transports and I really didn't want to have to change the code to use the stream API.
So here is what I came up with:
Create a file named 'logger.ts' and add this code to it:
import pino from 'pino'
import * as FileStreamRotator from 'file-stream-rotator'
const transport = pino.transport({
targets: [
{
level: 'info',
target: './logger.ts',
options: {
filename: '/tmp/test-%DATE%',
frequency: 'daily',
date_format: 'YYYY-MM-DD',
size: '100M',
max_logs: '10',
audit_file: '/tmp/audit.json',
extension: '.log',
create_symlink: true,
symlink_name: 'tail-current.log'
}
}
]
})
export const logger = pino(transport)
export default (options: any): any => FileStreamRotator.getStream(options)
Since Pino forces us to pass the target
prop as string that points to a transport file, instead of creating a new file just for this, I export the stream as the default of this file and then point the target to myself.
It's super hacky but that's the only way I found that wouldn't require creating a new file and then having to deal with windows/mac/linux file paths nonsense.
If anyone has a better, cleaner solution, I would love to hear it . Thanks.
I wanted to use this library as a Pino Transport. The problem is that Pino can only accept streams using the streams API which then doesn't allow the usage of other transports. So if for example I wanted to save the logs to a file and also pretty-print them, I would have to setup both the
FileStreamRotator
and the pretty-print as streams using the stream API and I can't just setup the pretty-print as a normal transport.And in my app I have 7 different transports and I really didn't want to have to change the code to use the stream API.
So here is what I came up with:
Create a file named 'logger.ts' and add this code to it:
import pino from 'pino' import * as FileStreamRotator from 'file-stream-rotator' const transport = pino.transport({ targets: [ { level: 'info', target: './logger.ts', options: { filename: '/tmp/test-%DATE%', frequency: 'daily', date_format: 'YYYY-MM-DD', size: '100M', max_logs: '10', audit_file: '/tmp/audit.json', extension: '.log', create_symlink: true, symlink_name: 'tail-current.log' } } ] }) export const logger = pino(transport) export default (options: any): any => FileStreamRotator.getStream(options)
Since Pino forces us to pass the
target
prop as string that points to a transport file, instead of creating a new file just for this, I export the stream as the default of this file and then point the target to myself.It's super hacky but that's the only way I found that wouldn't require creating a new file and then having to deal with windows/mac/linux file paths nonsense.
If anyone has a better, cleaner solution, I would love to hear it . Thanks.
Please check out adzejs as an alternative logger to Pino. It just works with everything. Here's an example of setting up this library with Adze:
import adze, { createShed, Configuration, Constraints } from 'adze';
import { Shed } from 'adze/dist/shed';
import * as FileStreamRotator from 'file-stream-rotator';
/*
We are applying a constraints type to our logger in order to force developers
to add their namespaces to a centralized list. This makes it easy to know what
namespaces are being used throughout the application for filtering purposes.
*/
interface AppConstraints extends Constraints {
allowedNamespaces: 'optin' | 'optin-controller';
}
/**
* Configure our Adze logger for the development environment.
*/
function setupDevLogger(shed: Shed) {
// This Adze configuration is used in the development environment
const devConfig: Configuration = {
useEmoji: true,
logLevel: 8,
};
// Create our listener for writing to our log file
shed.addListener('*', async (data, render) => {
const logString = render[1][0];
logStream.write(`${logString}\n`);
});
// Set up our base logger
return adze<AppConstraints>(devConfig).timestamp.seal();
}
/**
* Configure our Adze logger for the production environment.
*/
function setupProdLogger(shed: Shed) {
// This Adze configuration is used in the production environment
const prodConfig: Configuration = {
unstyled: true,
machineReadable: true,
logLevel: 5,
};
// Create our listener for writing to our log file
shed.addListener('*', async (data, render) => {
const logString = render[1][0];
logStream.write(`${logString}\n`);
});
// Set up our base logger
return adze<AppConstraints>(prodConfig).timestamp.seal();
}
// Create a rotating file stream for writing our logs to a file.
const logStream = FileStreamRotator.getStream({
filename: './logs/%DATE%',
frequency: 'daily',
date_format: 'MM-DD-YYYY',
size: '2M',
max_logs: '14',
audit_file: './logs/audit.json',
extension: '.log',
create_symlink: true,
symlink_name: 'current.log',
});
// Create an instance of shed for controlling our loggers and adding listeners
const shed = createShed({ cacheLimit: 0 });
// Create our logger factory for use throughout our application
const logger = process.env.ENV === 'development' ? setupDevLogger(shed) : setupProdLogger(shed);
export default logger;