npm package: @noveo/logger
Description: Lightweight, customizable and flexible logger module. :rocket: :zap:
Author: Andrey Dergaev
Latest version: 1.3.2

# logger
Lightweight, customizable and flexible logger module. :rocket: :zap:

# How to use
```
npm install @noveo/logger --save
```


```javascript
const logger = require('@noveo/logger');

logger.configure({
    transport: 'native',
    level: 'debug',
    useTimezone: true,
    applicationId: 'app-name',
    format: '{datetime} {pid} {ctx.requestId} [{level}] [{full-tag}] {message} {meta}',
    dateFormat: 'DD.MM.YYYY HH:mm:ss.SSS'
});

const log = logger.getLogger('logger-name');


log.info('Some fun message', { meta: 'object' });
// => 16.06.2018 21:00:22.521 3457 0 [info] [app-name:logger-name] Some fun message { "meta": "object" }


```


## Context of execute
Its object contains the executing environment, such as request ID, user data, request source object and other.


## API
### logger.configure(options)
Configure logger. Can be executed once on start system. Config options have different fields for different transports.

```javascript
const logger = require('@noveo/logger');
logger.configure({
    transport: 'native',
    level: 'debug',
    useTimezone: true,
    applicationId: 'app-name',
    format: '{datetime} {pid} {ctx.requestId} [{level}] [{full-tag}] {message} {meta}',
    dateFormat: 'DD.MM.YYYY HH:mm:ss.SSS'
})
```

### logger.getLogger(name)
Create logger instance by base config and with the custom name. Return instance of Logger class




#### Logger

#### log.<LEVEL>(message, [meta])
Create message with default context and write him to output stream with level "LEVEL"

```javascript
log.info('Info message');
// => 31.01.2017 16:44:55.599 3457 0 [INFO] [app-name::my-module-name] Info message
log.warn('Warning message', {
    userName: 'Cool'
});
// => 31.01.2017 16:44:55.599 3457 0 [WARN] [app-name::my-module-name] Warning message {"userName": "Cool"}
```

#### log.LEVEL(ctx, message, [meta])
Create message with context and write him to output stream with level "LEVEL"
```javascript
log.info({requestId: 45}, 'Info message');
// => 31.01.2017 16:44:55.599 3457 45 [INFO] [app-name::my-module-name] Info message
log.warn({requestId: 48}, 'Warning message', {
    userName: 'Cool'
});
// => 31.01.2017 16:44:55.599 3457 48 [WARN] [app-name::my-module-name] Warning message {"userName": "Cool"}
```


#### log.child(tag, [ctx])
Create child logger instance with extra context and predifined tag.
```javascript
const log = logger.getLogger('tag-name');
const childLog = log.child('child-tag');
log.info('I am child!');
// => 16.02.2017 09:59:27.821 3457 0 [info] [module-name:tag-name.child-tag] I am child!
```

```javascript
const log = logger.getLogger('tag-name');
const childLog = log.child('child-tag', {
    requestId: 75
});
log.info('I am child!');
// => 16.02.2017 09:59:27.821 3457 75 [info] [module-name:tag-name.child-tag] I am child!
```

#### log.startProfiling([tag])
Create Profile instance.
```javascript
const log = logger.getLogger('tag-name');
const profile = log.startProfiling('mainController');
// => 16.02.2017 09:59:27.821 3457 0 [debug] [module-name:tag-name.profile] Profiling "mainController" start
log.info(`Current value of profile ${profile}ms`);
// => 16.02.2017 09:59:27.821 3457 0 [info] [module-name:tag-name.profile] Current value of profile 34ms
profile.stop();
// => 16.02.2017 09:59:39.821 3457 0 [debug] [module-name:tag-name.profile] Profiling "mainController" finished in 12045ms
// ... some time later
log.info(`Profile value don\`t increase - ${profile}ms`);
// => 16.02.2017 09:59:49.023 3457 0 [info] [module-name:tag-name.profile] Profile value don`t increase - 12045ms
```

##### Profile
API for render profiling messages by logger instance

###### new Profile(log, \[tag\])
Create new instance

###### profile.valueOf() ⇒ [<code>Profile</code>](#Profile)
Get current value of profile

###### profile.toString() ⇒ <code>String</code>
Convert current value to string

###### profile.stop() ⇒ [<code>Profile</code>](#Profile)
Stop time counting


### Transports

#### native
Logging to STDOUT stream in one line format by template string.
Settings:
* metaSerializer {String|Function} - meta serialization function or module name (by default fast-safe-stringify)
* format {String} - format of output message

```javascript
const logger = require('@noveo/logger');
logger.configure({
    transport: 'native',
    format: '${datetime} ${ctx.requestId} [${level}] [${full-tag}] ${message} ${meta}'
})
```

```javascript
const logger = require('@noveo/logger');
logger.configure({
    transport: 'native',
    metaSerializer: 'json-stringify-safe',
    format: '{datetime} {pid} {ctx.requestId} [{level}] [{full-tag}] {message} {meta}'
})
```

Parameters available for formatter string:
 - **pid** - process ID
 - **ctx** - execution context object
 - **tags** - list of tags
 - **applicationId** - Application ID (set on init logger)
 - **name** - logger instance name (see getLogger)
 - **host** - host name
 - **level** - logging level name
 - **datetime** - Datetime if format, what was set in format propertyDatetime in the format, what was set in format property
 - **message** - Message
 - **full-tag** - Artificial parameter what rendered from applicationId (if set), logger name and list of tags


#### logstash
Logging to STDOUT stream in logstash format.
Settings:
* prettyPrint {Boolean} - flag to print json in pretty format
* outputStream {stream.Writable|String} - Definition of output stream.
Can be a string, if so, it'll use the string as a filename to append logs
to it
```javascript
const logger = require('./app/components/logger');
logger.configure({
    transport: 'logstash',
    prettyPrint: true
})
```


#### native-colorized (use only for development mode)
Logging to STDOUT stream in one line format by template string.
Settings:
* metaSerializer {String|Function} - meta serialization function or module name (by default JSON.stringify)
* format {String} - format of output message
* colors {Object} - list of levels with color values
* matches {Object} - list of colors with regexp

```javascript
const logger = require('@noveo/logger');
logger.configure({
    transport: 'native-colorized',
    level: 'silly',
    useTimezone: true,
    applicationId: 'app-name',
    format: '{datetime} {pid} {ctx.requestId} [{level}] [{full-tag}] {message} {meta}',
    dateFormat: 'DD.MM.YYYY HH:mm:ss.SSS',
    colors: {
        error: 'red',
        warn: 'yellow',
        silly: 'magenta'
    },
    matches: {
        green: 'request:',
        bgRed: /auth/i
    }
});

const log = logger.getLogger('colorized');


log.info('Info');
log.warn('Warning');
log.error('Error');
log.debug('Debug');
log.silly('Silly');
log.info('Start request: /user/login');
log.error('Some problem with AUTH man!');
```
![Colored screenshort](.extra/native-colorized.png)


#### mongo
Logging to mongodb collection.
Settings:
* db {String|Object} - MongoDB connection uri or preconnected db object (optional, dafaults to `localhost:27017`)
* options {Object} - MongoDB connection parameters (optional, defaults to `{poolSize: 2, autoReconnect: true}`)
* collection {String} - The name of the collection you want to store log messages in (optional, defaults to `logs`)
* capped {Boolean} - In case this property is true, try to create new log collection as capped
* cappedSize {Number} - Size of logs capped collection in bytes (optional, defaults to 10000000)
* cappedMax {Number} - Size of logs capped collection in number of documents (optional)
* expireAfterSeconds {Number} - Seconds before the entry is removed. Do not use if capped is set
```javascript
const logger = require('@noveo/logger');
logger.configure({
    transport: 'mongo',
    format: '{datetime} {pid} {ctx.requestId} [{level}] [{full-tag}] {message} {meta}',
    expireAfterSeconds: 5
})
```


# Benchmark
```
winston x 4,410 ops/sec ±3.48% (75 runs sampled)
bunyan x 4,090 ops/sec ±1.96% (77 runs sampled)
noveo x 4,684 ops/sec ±2.00% (61 runs sampled)
pino x 4,044 ops/sec ±7.33% (55 runs sampled)
Benchmark: ""noveo"" is the fastest.
```


# Convetions
## logger-1 and logger-2
The repo contains two special examples ```./examples/logger-1``` and ```./examples/logger-2```.
The ```./examples/logger-1``` contains sources example of noveo-logger 
usage approach based on Andrey Dergaev's opinion.
The ```./examples/logger-2``` contains sources example of noveo-logger 
usage approach based on my (Alexander Dobrodiy) opinion.

Run ```./examples/logger-1```
```
node ./examples/logger-1
```

Run ```./examples/logger-2```
```
node ./examples/logger-2
```


As Andrey is a tech lead, please, use his approach, not my! =)
Maybe anytime we would try my approach in any microservice.

## Log levels
Please use such log levels
- error
- warning
- info
- debug

## How to log
### Error level
- Log any error when you have catched it 
  and don't throw it on the next code layer

### Warning level
- When you deprecated a method, put warning on method usage
- So prefer you have an edge case that causes sending not trivial
  response to the user or requires any special processing way.
  Prefer you understand operations team want to catch such cases
  and big amount of this cases follows new tasks to reduce them.
  So that is to log it on warning level!
  
### Info level
- Starting a server
- Starting a process
- Starting a controller
- etc TODO Add points to the list

### Debug level
- If you are implementing a complicated algorithm you may log
  some values
- So prefer you had a lot of fluent bugs of a feature. 
  You need to catch it on any server. That is to log on debug level.
- If you want to log something, but you understand it would 
  generate a sheet of logs on every request, 
  that is to log on debug level
  
### Libraries
If you are developing a library, think it is better to use 
[debug](https://www.npmjs.com/package/debug) module or not?

## What to log
- It is necessary to log requestId (but we still don't have it).
- Anything else?