Skip to content

Commit 1e12f6e

Browse files
authored
Merge pull request #1 from monksoftware/logger-default-export-and-refactoring
Refactor module
2 parents a94014e + 9137f5f commit 1e12f6e

File tree

5 files changed

+142
-97
lines changed

5 files changed

+142
-97
lines changed

README.md

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,80 @@
11
# Monk Log library
22

3-
This is a simple logging library for Nodejs projects depends on:
4-
- [loglevel](https://github.com/pimterry/loglevel)
5-
- [loglevel-plugin-prefix](https://github.com/kutuluk/loglevel-plugin-prefix)
6-
- [chalk](https://github.com/chalk/chalk#readme)
3+
Are you using `console.log` in your project? Are you tired of configuring
4+
loglevel / winston / whatever to add timestamp, colors to your log messages?
5+
Do you want a consistent log style?
6+
This is the library for you! You can now log with style -- no configuration
7+
required!
78

89
## Installation
910

10-
For add this as `npm` dependency
11+
Simply add this library as a dependency in your project.
12+
You can use the github URI:
1113

12-
```bash
13-
npm install git+ssh://[email protected]:node-libraries/monk-log.git
14+
```
15+
yarn add github:monksoftware/monk-log
1416
```
1517

1618
or add manually to your `package.json` file
1719

1820
```json
1921
{
2022
"dependencies": {
21-
... ,
22-
"monk-log": "git+ssh://[email protected]:node-libraries/monk-log.git"
23+
[...] ,
24+
"monk-log": "github:monksoftware/monk-log"
2325
}
2426
}
2527
```
2628

27-
## Setting up
29+
## How to use
30+
31+
Works pretty much the same as `loglevel`, the only difference is that
32+
the exported root logger is preconfigured with loglevel-plugin-prefix
33+
and custom formatting functions in order to output nice colorful log messages
34+
with timestamp, log level and logger name.
2835

2936
```javascript
30-
const { levels, monkLog } = require('monk-log')
31-
const log = monkLog(options)
37+
const log = require('monk-log')
3238

3339
log.trace(msg)
3440
log.debug(msg)
3541
log.info(msg)
3642
log.warn(msg)
3743
log.error(msg)
38-
39-
```
40-
41-
## Documentation
42-
43-
The monk log library accept optional parameters:
44-
```
45-
{
46-
name: // Choose a name for log instance, default is monk-log
47-
level: // Choose a log level, default il DEBUG, possible level are:
48-
// "TRACE" "DEBUG" "INFO" "WARN" "ERROR"
49-
wrap: // Wrap `name` with custom identifier, default is and array of square brackets ['[', ']']
50-
}
5144
```
5245

5346
## Examples
5447

55-
see more inside `examples folder`
48+
See more inside `examples folder`
5649

50+
### Ready-to-go logging
5751
```javascript
58-
const { levels, monkLog } = require('monk-log')
59-
const log = monkLog({
60-
name: 'MONK-LOG',
61-
level: levels.DEBUG,
62-
wrap: ['+', '+']
63-
})
52+
const log = require('monk-log')
53+
log.warn('You can have nice log messages in just one line!')
6454

65-
log.debug('this is a debug log')
66-
log.info('this is an info log')
67-
log.warn('this is a warning log')
68-
log.error('this is an error log')
55+
// Outputs
56+
// [2019-02-18T01:10:49.933] WARN [root]: You can have nice log messages in just one line!
57+
```
6958

70-
// Response
71-
// [2019-02-08T18:35:40.245] INFO monk-log: Set log level to DEBUG
72-
// [2019-02-08T18:35:40.245] DEBUG +MONK-LOG+: this is a debug log
73-
// [2019-02-08T18:35:40.245] INFO +MONK-LOG+: this is an info log
74-
// [2019-02-08T18:35:40.245] WARN +MONK-LOG+: this is a warning log
75-
// [2019-02-08T18:35:40.245] ERROR +MONK-LOG+: this is an error log
59+
### Child loggers with custom format
7660

61+
```javascript
62+
const log = require('monk-log')
63+
// Default level is WARN,
64+
log.setDefaultLevel('DEBUG')
65+
const childLogger = log.getLogger('CHILD', 'DEBUG', {
66+
template: 'When: %t, who: %n, why: %l, what: '
67+
})
68+
log.info('Child logger has been set up')
69+
childLogger.debug('this is a debug message')
70+
childLogger.info('This is a info message')
71+
childLogger.warn('this is a warning message')
72+
childLogger.error('this is an error message')
73+
74+
// Outputs
75+
// [2019-02-18T01:08:46.260] INFO [root]: Child logger has been set up
76+
// When: 2019-02-18T01:08:46.262, who: CHILD, why: DEBUG, what: this is a debug message
77+
// When: 2019-02-18T01:08:46.262, who: CHILD, why: INFO, what: This is a info message
78+
// When: 2019-02-18T01:08:46.262, who: CHILD, why: WARN, what: this is a warning message
79+
// When: 2019-02-18T01:08:46.263, who: CHILD, why: ERROR, what: this is an error message
7780
```

examples/full.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
'use strict'
22

3-
const { levels, monkLog } = require('../lib/log')
4-
5-
const logTrace = monkLog({
6-
name: 'MONK-LOG',
7-
level: levels.TRACE
8-
})
3+
const log = require('../lib/log')
94

5+
const logTrace = log.getLogger('MONK-LOG', 'TRACE')
106
logTrace.trace('this is a trace log')
117
logTrace.debug('this is a debug log')
128
logTrace.info('this is an info log')
139
logTrace.warn('this is a warning log')
1410
logTrace.error('this is an error log')
1511

16-
const log = monkLog({
17-
name: 'MONK-LOG2',
18-
level: levels.DEBUG,
19-
wrap: ['[', ']']
20-
})
12+
const advancedLogger = log.getLogger(
13+
'super-long-logger-name-maybe-too-much',
14+
'DEBUG',
15+
{
16+
// Only use first letter of level, e.g. W for waraning logs
17+
levelFormatter: (level) => level.toUpperCase()[0],
18+
// Numeric timestamp
19+
timestampFormatter: (date) => +date,
20+
// Truncate logger names to 5 letters
21+
nameFormatter: (name) => name.slice(0, 5),
22+
template: `#%l# logger: %n - timestamp: %t - message:`
23+
}
24+
)
2125

22-
log.debug('this is a debug log')
23-
log.info('this is an info log')
24-
log.warn('this is a warning log')
25-
log.error('this is an error log')
26+
log.info('This library is great!') // #I# logger: super - timestamp: 1550449434004 - message: This library is great!

examples/simple.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
1-
'use strict'
2-
3-
const { monkLog } = require('../lib/log')
4-
5-
const log = monkLog()
6-
log.debug('This is a log message')
1+
const log = require('../lib/log')
2+
log.debug('This is a debug log message. You probably wont see it because the default level is WARN')

lib/log.js

Lines changed: 78 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ const logLevel = require('loglevel')
44
const chalk = require('chalk')
55
const prefix = require('loglevel-plugin-prefix')
66

7-
const getKeyByValue = (object, value) => {
8-
return Object.keys(object).find(key => object[key] === value)
9-
}
10-
117
const colors = Object.freeze({
128
TRACE: chalk.magenta,
139
DEBUG: chalk.cyan,
@@ -16,42 +12,91 @@ const colors = Object.freeze({
1612
ERROR: chalk.red
1713
})
1814

19-
const logConfiguration = {
20-
// template: '[%t] %l %n:',
21-
format(level, name, timestamp) {
22-
return `${chalk.gray(`[${timestamp}]`)} ${colors[level.toUpperCase()](level)} ${chalk.green(`${name}:`)}`
23-
},
24-
timestampFormatter: () => {
25-
const tzOffset = (new Date()).getTimezoneOffset() * 60000 // offset in milliseconds
26-
const localISOTime = (new Date(Date.now() - tzOffset)).toISOString().slice(0, -1) // => '2015-01-26T06:40:36.181'
27-
return localISOTime
28-
}
15+
const defaultTimestampFormatter = (date) => {
16+
const tzOffset = date.getTimezoneOffset() * 60000 // offset in milliseconds
17+
const localISOTime = (new Date(date - tzOffset)).toISOString().slice(0, -1) // => '2015-01-26T06:40:36.181'
18+
return localISOTime
19+
}
20+
21+
const defaultNameFormatter = (name) => {
22+
return chalk.green(name)
23+
}
24+
25+
const defaultLevelFormatter = (level) => {
26+
return colors[level.toUpperCase()](level.toUpperCase())
2927
}
3028

31-
module.exports.levels = logLevel.levels
29+
const defaultTemplate = '[%t] %l [%n]:'
3230

33-
module.exports.monkLog = ({ name = '', level = logLevel.levels.DEBUG, wrap = ['[', ']'] } = {}) => {
34-
if (!Array.isArray(wrap)) {
35-
wrap = ['[', ']']
31+
// "Nice" default options, they make log lines like:
32+
// [2019-02-18T00:37:56.007] WARN [root]: This is a warning message
33+
// With colored log level, according to serverity.
34+
const defaultLogLevelOptions = {
35+
levelFormatter: defaultLevelFormatter,
36+
timestampFormatter: defaultTimestampFormatter,
37+
nameFormatter: defaultNameFormatter,
38+
template: defaultTemplate,
39+
}
40+
41+
const _loggersByName = {}
42+
43+
/**
44+
* Subclass of loglevel's Logger,
45+
* Preset to use loglevel-plugin-prefix with some nice default options.
46+
* Can obtain child loggers by calling .getLogger, they will inherit
47+
* parent's options.
48+
*/
49+
class MonkLogger extends logLevel.constructor {
50+
constructor (name, level, options) {
51+
super(name, level)
52+
this.options = {...defaultLogLevelOptions}
53+
this.configure(options)
3654
}
3755

38-
if (wrap.length !== 2) {
39-
throw new Error(`Wrong wrap params, expected 2 got ${wrap.length} elements`)
56+
/**
57+
* Configure the logger, accepts all loglevel-plugin-prefix options
58+
* and the loglevel (`level` key).
59+
*
60+
* @param {object} options - new option values to update
61+
* @param {boolean} [reset=false] - if false (the default), it will keep
62+
* and update current options (e.g. options inherited from parents or
63+
* a previous call to configure). When true, if will first reset options
64+
* to default ones, and then update with supplied ones.
65+
* Therefore, you can use .configure({}, true) to completely reset
66+
* options to defaults.
67+
*/
68+
configure (options, reset = false) {
69+
this.options = {
70+
...(reset ? defaultLogLevelOptions : this.options),
71+
...options,
72+
}
73+
prefix.apply(this, this.options)
4074
}
4175

42-
let logger
43-
if (name) {
44-
name = `${wrap[0]}${name}${wrap[1]}`
45-
logger = logLevel.getLogger(name)
46-
} else {
47-
// root name is nasty
48-
logger = logLevel.getLogger('monk-log')
76+
/**
77+
*
78+
* @param {string} name - child logger name
79+
* @param {string} level - only output log messages of at least this level
80+
* @param {object} options - loglevel-plugin-prefix options
81+
*/
82+
getLogger (name, level, options) {
83+
if (typeof name !== "string" || name === "") {
84+
throw new TypeError("You must supply a name when creating a logger.")
85+
}
86+
let logger = _loggersByName[name]
87+
if (!logger) {
88+
logger = _loggersByName[name] = new MonkLogger(
89+
name, level, {...this.options, ...options})
90+
}
91+
return logger
4992
}
93+
}
5094

51-
prefix.reg(logLevel)
52-
prefix.apply(logger, logConfiguration)
53-
logger.setLevel(level)
54-
logger.info(`Set log level to ${getKeyByValue(logger.levels, level)}`)
95+
prefix.reg(logLevel)
5596

56-
return logger
57-
}
97+
const defaultLogger = new MonkLogger('root')
98+
defaultLogger.getLoggers = () => _loggersByName
99+
100+
// Export default logger. Can be used directly, or can be used
101+
// to obtain child loggers.
102+
module.exports = defaultLogger

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "monk-log",
3-
"version": "1.0.0",
3+
"version": "2.0.0",
44
"private": true,
55
"engines": {
66
"node": ">=8.3.0"

0 commit comments

Comments
 (0)