PROJECT ARCHIVED, PLEAUSE USE pino
This is simple logger for JavaScript programs.
- All logs going to stdout, because apps is not a place where logs should be processed.
- Output in JSON, because it's really easy to parse (except TTY, where output is human readable).
- Fields in JSON object in order:
timestamp
,level
,message
,...rest
. - By default, only 2 levels exported:
info
&error
. You can add own levels, but are we care about other levels in real life?
All this items makes this package perfect for applications in containers.
npm:
npm install https://github.com/fanatid/log2json2stdout
yarn:
yarn add https://github.com/fanatid/log2json2stdout
By default npm
/ yarn
will install code from master
branch. If you want specified version, just add some branch name / commit hash / tag and the end of URL. See Yarn add or npm install for details about installing package from git repo.
example.js
:
const logger = require('log2json2stdout')
logger.info('hello')
const err = new Error('world')
logger.error({ message: err.message, stack: err.stack })
$ # human-readable output, because output to TTY
$ node example.js
2019-07-10T19:51:23.824Z info: hello
2019-07-10T19:51:23.826Z error: world
Error: world
at Object.<anonymous> (/home/kirill/projects/log2json2stdout/example.js:5:13)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:643:32)
at Function.Module._load (internal/modules/cjs/loader.js:556:12)
at Function.Module.runMain (internal/modules/cjs/loader.js:839:10)
at internal/main/run_main_module.js:17:11
$ # hack for JSON output (STDOUT is not considered as TTY with tee)
$ node example.js | tee
{"timestamp":"2019-07-10T19:54:07.539Z","level":"info","message":"hello"}
{"timestamp":"2019-07-10T19:54:07.540Z","level":"error","message":"world","stack":"Error: world\n at Object.<anonymous> (/home/kirill/projects/log2json2stdout/example.js:5:13)\n at Module._compile (internal/modules/cjs/loader.js:776:30)\n at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)\n at Module.load (internal/modules/cjs/loader.js:643:32)\n at Function.Module._load (internal/modules/cjs/loader.js:556:12)\n at Function.Module.runMain (internal/modules/cjs/loader.js:839:10)\n at internal/main/run_main_module.js:17:11"}
$ # JSON output can be converted to human-readable with log2json2stdout2tty
$ node example.js | tee | log2json2stdout2tty
2019-07-10T19:51:23.824Z info: hello
2019-07-10T19:51:23.826Z error: world
Error: world
at Object.<anonymous> (/home/kirill/projects/log2json2stdout/example.js:5:13)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:643:32)
at Function.Module._load (internal/modules/cjs/loader.js:556:12)
at Function.Module.runMain (internal/modules/cjs/loader.js:839:10)
at internal/main/run_main_module.js:17:11
If you still want use more levels than info
/ error
, you can add new functional in your own project:
const logger = require('log2json2stdout')
logger.emerg = (obj) => logger('emerg', obj)
logger.alert = (obj) => logger('alert', obj)
logger.crit = (obj) => logger('crit', obj)
// logger.error = (obj) => logger('error', obj) <-- already exists
logger.warning = (obj) => logger('warning', obj)
logger.notice = (obj) => logger('notice', obj)
// logger.info = (obj) => logger('info', obj) <-- already exists
logger.debug = (obj) => logger('debug', obj)
and if you want add colors for levels in TTY, you should change ttyGetColoredLevel
:
logger.ttyGetColoredLevel = (level) => {
switch (level) {
case 'emerg':
case 'alert':
case 'crit':
return logger.chalk.red.bold(level)
case 'error':
return logger.chalk.red(level)
case 'warning':
case 'notice':
return logger.chalk.yellow(level)
case 'info':
return logger.chalk.green(level)
default: return level
}
}
In comprasion to more log levels (or to additional), we can use similar idea which used in debug. Logging will depends from CLI flags / ENV vars:
const logger = require('log2json2stdout')
const namespaces = Object.create(null)
logger.create = (namespace) => {
if (!namespaces[namespace]) {
function newLogger (...args) {
newLogger.log(...args)
}
Object.assign(newLogger, logger)
if (!process.env[`MYAPP_LOG_${namespace}`]) newLogger.log = () => {}
namespaces[namespace] = newLogger
}
return namespaces[namespace]
}
// and now create logger for specified namespace
const loggerThingX = logger.create('THING_X')
loggerThingX.info('log info event from thing x')
So, now if we defined ENV MYAPP_LOG_THING_X
we will see log message, if not defined there will no logs about THING_X
.
In addition, if we want see how much time has passed from last message we can modify logObj
function:
const prettyMs = require('pretty-ms')
function diffTime (time) {
if (time === undefined) return process.hrtime()
const diff = process.hrtime(time)
return prettyMs(diff[0] * 1e3 + diff[1] / 1e6)
}
// logObj = newLogger.logObj
// let ts = null
// newLogger.logObj = (obj) => {
// if (ts === null) ts = diffTime()
// else obj.message = `${obj.message} +${diffTime(time)}`
// logObj(obj)
// }
Sometimes errors do not have nice messages and it would be good log them in little different way than just string.
logger.errorObj = (message, err) => {
const obj = { message }
for (const name of Object.getOwnPropertyNames(err)) {
switch (name) {
// do not rewrite message
case 'message':
obj.errmsg = err.message
break
default:
obj[name] = err[name]
break
}
}
logger.error(obj)
}
// } catch (err) {
// logger.errorObj(`new error at block X: ${err.message.toUpperCase()}`, err)
// }