Logger module
npm install @dediapp/loggerConfigurable winston logger
Logger is based on winston library. This module allows to configure and create a winston logger via a JSON configuration file or via environment variables. Several properties and options are available to configure the logger.
Three environment variables are available:
* LOG_TRANSPORTS: selects the "interface" to display logs. It must be equal to "Console" or "File" or event both "Console,File"
* LOG_LEVEL: selects the level of logs that should be displayed (available values from the most restrictive to the less restrictive: "error", "warn", "info", "http", "verbose", "debug", "silly")
* LOG_FILE: enables to specify the path to the file where logs should be written if LOG_TRANSPORTS is equal or contains the value "File"
```
LOG_TRANSPORTS=File
LOG_LEVEL=error
LOG_FILE=etc/dedi/winston.log
All winston's core configuration properties except format and levels can be set in a JSON configuration file. The property transports is set through the field log_transports which is detailed in the following part. default_meta
There are three more available properties:
* : javascript object containing metadata that should be displayed in the log messageexception_handlers
* : array containing transports which specify where uncaughtException events should be displayed (see winston documention)rejection_handlers
* : array containing transports which specify where uncaughtRejection events should be displayed (see winston documention)
These properties should be the value of a logging field
`json`
{
"logging": {
"log_level": "error",
"silent": false,
"log_transports": []
}
}
NB: Winston level property is named log_level
#### Format
This loggger has a predefined format which is:
LEVEL | Date in ISO format | log message
Between date and log message it is possible to add request details:
* ip: ip address where the request comes from
* matrixUserId: id of the user which sent the request
* requestURL: requested url
* endpointPath: requested API endpoint
* httpMethod: http method of the request
* status: response status
Any other detail can be added and it will be displayed after log message
Aditionnal details are displayed in the following order:
LEVEL | Date in ISO format | ip | matrixUserId | httpMethod | requestURL | endpointPath | status | log message | additionnal details
#### Transports
In this module, logger's log_transports field is set to an array of objects which contain two properties:
* type: winston transport that the logger should use. "Console" and "File" transports listed in this winston documentation are available. The field's value must be the transport name and must start with a capital letter ("Console", "File").
* options: object containing selected transport options, they are all detailed on this page.
NB: It is not specified in winston documentation but transport options can also contain the following properties:
* level: a string that specifies which logger level is associated with this transport
* silent: boolean flag indicating whether to suppress output
* handleExceptions: boolean flag indicating that transport should log uncaughtException events
* handleRejections: boolean flag indicating that transport should log uncaughtRejection events
`json`
{
"logging": {
"log_level": "error",
"log_transports": [{
"type": "Console",
"options": {
"level": "error",
"consoleWarnLevels": ["error", "info"]
}
}]
}
}
If no transports is specified in the configuration and description files, then the default value is [{"type":"Console"}]
NB: The LOG_TRANSPORTS environment property has the priority over the JSON configuration file/object log_transports field
#### Rotate File
This module enables to configure transports based on the winston-daily-rotate-file library. Almost all options listed in this documentation are available in this module, only stream option is missing.
`json`
{
"logging": {
"log_level": "error",
"log_transports": [{
"type":"DailyRotateFile",
"options": {
"zippedArchive": true,
"dirname":"./logs",
"filename": "dedi-%DATE%.log",
"maxSize": "10m",
"maxFiles": "5d"
}
}],
}
}
All default values are defined in the configuration description file src/config.json or your custom description object. null
For the following properties: log_level, silent, exit_on_error if they are or undefined both in configuration and description files then the default values will come from winston library. filename
Transports options default values will come from winston library too, except for option
#### Logs in file
When LOG_TRANSPORTS is set to "File" or contain "File"`sh`
LOG_TRANSPORTS=Fileor
LOG_TRANSPORTS=Console,FileLOG_FILE
If is not set then the default filename will be dedi.log
Same with JSON configuration file or conf object, if log_transports array contains a File transport and no options are specified`ts`
logging: {
...
log_transports: [
{type: 'File'}
]
...
}filename
If option and LOG_FILE are not set, when logTransports contains "File", then the default filename will be dedi.log
For rotate file, it will only check filename option and if it is not set the default filename will be dedi-%DATE%.log with %DATE% replaced by the day date.
To create a logger you have to use the getLogger method. It takes two optional parameters, conf and confDesc, two configuration objects as described before
First, the module checks if we have defined the path to a configuration file in the DEDI_LOGGER_CONF environment variable. Otherwise, it will check if the file etc/dedi/logger.conf exists and read the configuration from this file. Then, it will check if conf is not null or undefined, and use it if it is the case. If none of these options work, the configuration will come from src/config.json file or your custom description object.
You can define your own description object as the second parameter of getLogger method, but it is not recommended to set this property.
Example: conf parameter not null`ts
import { EFormatType, ETransportType, getLogger } from '@dediapp/logger'
const logger = getLogger({
logging: {
log_level: 'error',
log_transports: [
{
type: ETransportType.FILE,
options: {
dirname: 'etc/dedi/logs',
filename: 'dedi.log'
}
}
]
}
})
`
Example: set DEDI_LOGGER_CONF with configuration file path`ts
import { getLogger } from '@dediapp/logger'
process.env.DEDI_LOGGER_CONF = 'home/dwho/logger.conf'
const logger = getLogger()
/*
For example, home/dwho/logger.conf file content is:
{
logging: {
log_level: 'error',
log_transports: [
{
type: ETransportType.FILE,
options: {
dirname: 'etc/dedi/logs',
filename: 'dedi.log'
}
}
]
}
}
*/
`
Example: etc/dedi/logger.conf file exists`ts
import { getLogger } from '@dediapp/logger'
const logger = getLogger()
/*
For example, etc/dedi/logger.conf file content is:
{
logging: {
log_level: 'error',
log_transports: [
{
type: ETransportType.FILE,
options: {
dirname: 'etc/dedi/logs',
filename: 'dedi.log'
}
}
]
}
}
*/
`
Example: use default configuration from src/config.json`ts
import { getLogger } from '@dediapp/logger'
const logger = getLogger()
/*
src/config.json file content is:
{
"logging": {
"log_level": "info",
"log_transports": [{
"type":"Console"
}],
"silent": false,
"exit_on_error": false,
"default_meta": null,
"exception_handlers": [],
"rejection_handlers": []
}
}
*/
`
Example: use your own configuration description object
`ts
import { EFormatType, ETransportType, getLogger } from '@dediapp/logger'
const logger = getLogger(null, {
logging: {
log_level: 'error',
log_transports: [
{
type: ETransportType.FILE,
options: {
dirname: 'etc/dedi/logs',
filename: 'dedi.log'
}
}
]
}
})
`
The created logger can log messages thanks to the following methods:
* error(message: string, details?: Recordwarn(message: string, details?: Record
* info(message: string, details?: Record
* http(message: string, details?: Record
* verbose(message: string, details?: Record
* debug(message: string, details?: Record
* silly(message: string, details?: Record
*
`ts
import { getLogger } from '@dediapp/logger'
const logger = getLogger()
logger.error('This is an error message')
// Output: ERROR | 2028-12-08T21:36:22.011Z | This is an error message
logger.info(
'This is an info message',
{
ip: '127.0.0.1',
matrixUserId: '@dwho:example.com',
httpMethod: 'GET',
requestUrl: 'https://example.com/example/how/to/use/logger',
endpointPath: '/example/how/to/use/logger',
status: 200,
detail1: 'additionnal detail 1',
detail2: 'additionnal detail 2'
}
)
// Output: INFO | 2028-12-08T21:36:22.011Z | 127.0.0.1 | @dwho:example.com | GET | https://example.com/example/how/to/use/logger | /example/how/to/use/logger | This is an info message | additionnal detail 1 | additionnal detail 2
// Order of additionnal details does not matter
logger.debug(
'This is a debug message',
{
endpointPath: '/example/how/to/use/logger',
httpMethod: 'GET',
requestUrl: 'https://example.com/example/how/to/use/logger',
matrixUserId: '@dwho:example.com',
ip: '127.0.0.1',
status: 200,
detail1: 'additionnal detail 1',
detail2: 'additionnal detail 2'
}
)
// Output: DEBUG | 2028-12-08T21:36:22.011Z | 127.0.0.1 | @dwho:example.com | GET | https://example.com/example/how/to/use/logger | /example/how/to/use/logger | 200 | This is a debug message | additionnal detail 1 | additionnal detail 2
logger.silly(
'This is an info message',
{
endpointPath: '/example/how/to/use/logger',
httpMethod: 'GET',
matrixUserId: '@dwho:example.com',
status: 200
}
)
// Output: SILLY | 2028-12-08T21:36:22.011Z | @dwho:example.com | GET | /example/how/to/use/logger | 200 | This is a silly message
// Methods won't crash if they are called with unsupported additionnal detail
logger.debug(
'This is an debug message',
{
endpointPath: '/example/how/to/use/logger',
httpMethod: 'GET',
matrixUserId: '@dwho:example.com',
status: 200,
falsyDetail: 'falsy'
}
)
// Output: DEBUG | 2028-12-08T21:36:22.011Z | @dwho:example.com | GET | /example/how/to/use/logger | 200 | This is a debug message
`
The log method is also available and requires one more parameter to specify log level
`ts
import { getLogger } from '@dediapp/logger'
const logger = getLogger()
logger.log('error', 'This is an error message')
// Output: ERROR | 2028-12-08T21:36:22.011Z | This is an error message
logger.log(
'info',
'This is an info message',
{
ip: '127.0.0.1',
matrixUserId: '@dwho:example.com',
httpMethod: 'GET',
requestUrl: 'https://example.com/example/how/to/use/logger',
endpointPath: '/example/how/to/use/logger',
status: 200,
detail1: 'additionnal detail 1',
detail2: 'additionnal detail 2'
}
)
// Output: INFO | 2028-12-08T21:36:22.011Z | 127.0.0.1 | @dwho:example.com | GET | https://example.com/example/how/to/use/logger | /example/how/to/use/logger | This is an info message | additionnal detail 1 | additionnal detail 2
``
Copyright (c) 2023-present DediApp
License: GNU AFFERO GENERAL PUBLIC LICENSE