1. はじめに
log4js-node
は、log4js を javascript 用に移植した log4js を node 用に書き直したものらしい。
Java の log4j とは大きく違うとのこと。
ロガーの使い方がいまいち分かっていなかったので、調査ついでにまとめた。
- log4js-node/log4js-node: A port of log4js to node.js
- Official-Docs: log4js-node by log4js-node
- GitHub-Docs: log4js-node by log4js-node
テスト環境
- Windows10, MINGW64, VSCode
- node: 10.14.2
- babel-node: 7.6.1
- log4js: 5.2.2
- ES6 形式で書きたかったので
babel-node
を使用 - ログファイルは VSCode + Log File Highlighter で表示 (ちゃんと設定してないです…)
console
$ npm install log4js
参考:
2. 基本的な使い方
ログの出力(logging)
test.js
import log4js from 'log4js'
const logger = log4js.getLogger()
logger.level = 'all'
logger.trace('Some trace messages')
logger.debug('Some debug messages')
logger.info('Some info messages')
logger.warn('Some warn messages')
logger.error('Some error messages')
logger.fatal('Some fatal messages')
log4js.shutdown((err) => {
if (err) throw err
process.exit(0)
})
-
logger.level
は ALL (最小) < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF (最大) - default は
OFF
(console へ出力しない) - level は独自のものを設定することも可能だが取り扱わない
-
log4js.shutdown(cb(err))
はログを確実に保存してから終了する際に使用する (flush 処理) - 定義されている level は以下を参照
参考:
ログのカテゴリ (category)
test.js
const logger = log4js.getLogger()
logger.level = 'all'
logger.info('Some info messages', 'append', 'more')
const cheseLogger = log4js.getLogger('cheese')
cheseLogger.level = 'all'
cheseLogger.info('Cheese is Comte.')
- ログを category ごとにグループ化して出力することができる
- category は
log4js.getLogger([category])
で指定する - default は
default
というグループ - category ごとに出力する level を指定できる
- 前項で触れなかったが、複数の引数を使用するとスペース区切りで出力される
参考:
3. 高度な使い方(Configure)
コンソールとファイルに出力 (stdout / file)
test.js
log4js.configure({
appenders: {
out: { type: 'stdout' },
app: { type: 'file', filename: 'application.log' }
},
categories: {
default: { appenders: ['out', 'app'], level: 'debug' }
}
})
- appender は渡されたログを出力する機械と考える
-
default
の category を、 stdout に出力するout
と file に出力するapp
という appender を定義した - appender の
name(key)
は自由に指定可能 - categories で category ごとに使用する appender と level などを定義できる
-
logger.level
は この level と同じ意味となる - ファイルに出力されるログは末尾に追記される
参考:
ログのローリング (file / fileSync)
test.js
log4js.configure({
appenders: {
app: { type: 'file', filename: 'application.log', maxLogSize: 100, backups: 1 }
},
categories: {
default: { appenders: ['app'], level: 'all' }
}
})
- file 系の appender は書き込むファイルをローテートすることが可能
- 上記は
application.log.2
が保持上限を超えて削除されている -
type: fileSync
で同期的に書き込むことも可能 (test などの用途) - options
- type:
file
/fileSync
- filename: string - ログファイルの名称 (xxx.ext)
- maxLogSize: integer - 1ファイルの最大サイズ (byte)、default は上限なし
- backups: integer - 保持するファイル数、default は
5
- compress: boolean -
true
で ローリングしたファイルを圧縮する(.gz) - keepFileExt: boolean -
true
で 拡張子を保持する (xxx.ext.1 を xxx.1.ext にする) - encoding: string - default は
utf-8
- type:
参考:
日付ごとのローリング(dateFile)
test.js
log4js.configure({
appenders: {
app: { type: 'dateFile', filename: 'application.log', pattern: '.yyyyMMdd-hhmmss' }
},
categories: {
default: { appenders: ['app'], level: 'all' }
}
})
-
pattern
で指定した日付の粒度でローテートすることが可能 - 上記は1秒ごとに切り替える設定で二回実行した結果
- file 系と違って保持数は指定できない?(要検証)
- 保持日数は
daysToKepp
で指定できる - option
- type:
dateFile
- filename: string - ログファイルの名称 (xxx.ext)
- pattern: string - ローテートするタイミング、default は
.YYYY-MM-dd
、参照:date-format - daysToKeep: integer - 保持する日数、 default は上限なし
- alwaysIncludePattern: boolean -
true
で現在のログファイル名にも日付を付与する - compress: boolean -
true
で ローリングしたファイルを圧縮する(.gz) - keepFileExt: boolean -
true
で 拡張子を保持する (xxx.ext.date を xxx.date.ext にする) - encoding: string - default は
utf-8
- type:
参考:
appendars のレベルフィルター (logLevelFilter)
test.js
log4js.configure({
appenders: {
out: { type: 'stdout' },
app: { type: 'file', filename: 'application.log' },
wrapErr: { type: 'logLevelFilter', appender: 'app', level: 'warn' }
},
categories: {
default: { appenders: ['out', 'wrapErr'], level: 'all' }
}
})
-
logLevelFilter
を用いると appender ごとに出力する level を指定可能 - 出力する appender にラップするように使用する
- これによりエラーログだけを別途で処理することも可能
- ※ appender は上から処理されるので logLevelFilter は最後に配置する必要がある
- options
- type:
logLevelFilter
- level: string - 出力する最小の level
- maxLevel: string - 出力する最大の level 、default は
FATAL
- type:
参考:
その他
- 他にも SMTP や Slack に出力するものなどがある
- log4js-node by log4js-node
4. 出力ログのレイアウト
標準レイアウト(Built-in Layout)
test.js
log4js.configure({
appenders: {
out: { type: 'stdout', layout: { type: 'basic' } }
},
categories: {
default: { appenders: ['out'], level: 'all' }
}
})
- 各種 appender に layout を指定することができる
- 必須ではないので必要に応じて…
-
※ file 系の appenders に
colores
を指定しないこと! (色付けは制御文字を使用しているため)
参考:
出力パターン (Pattern format)
test.js
log4js.configure({
appenders: {
out: { type: 'stdout', layout: { type: 'pattern', pattern: '%d %[%5p%] %c %m' } }
},
categories: {
default: { appenders: ['out'], level: 'all' }
}
})
- 出力する文字列のパターンを独自に設定できる
- field は
%[padding].[truncation][field]{[format]}
で定義されている- padding: integer - 空白埋め文字数、右詰め
- truncation: integer - 最大文字数、切り捨て
- field: string - フィールド名
- format: string(option) - フィールドのオプション (任意)
- 上記の場合、「日付 level(5文字埋め)を色付きで カテゴリ 本文」となる
- ※ file 系の appenders に
%[
%]
を指定しないこと! (色付けは制御文字を使用しているため) - pattern field (サンプル)
- %r: ローカル時刻 (02:19:11)
- %p: ログ level (INFO)
- %c: ログ category (default)
- %h: ホスト名 (PCの名前等)
- %m: メッセージ
- %d: 日時、{ISO8601 |
yyyy-MM-ddThh:mm:ss.SSS
} (2019-10-23T02:13:53.028)、参照:date-format - %%: %のエスケープ
- %n: 改行
- %z: プロセスID、process.pid (28076)
- %[: 色付け開始
- %]: 色付け終了
-
enableCallStack: true
時の field (後述)- %f: ログ出力元のファイル名、{depth |
0
} (C:\xxx\test.js) - %l: ログ出力元の行番号 (13)
- %o: ログ出力元の列位置 (8)
- %s: スタックトレース ( at Object.info (C:\xxx/test.js:13:8) \n ...)
- %f: ログ出力元のファイル名、{depth |
- トークンは取り扱わない
参考:
enableCallStack
test.js
log4js.configure({
appenders: {
out: { type: 'stdout', layout: { type: 'pattern', pattern: '%[[%d] %p %c -%] %m%n%f %l %o%n%s' } }
},
categories: {
default: { appenders: ['out'], level: 'all', enableCallStack: true }
}
})
const logger = log4js.getLogger()
logger.info('Some info messages')
- category で
enableCallStack: true
を設定すると出力箇所のスタックトレースが取得できる -
%s
の前に%n
で改行を入れないと出力が崩れるので注意 -
enableCallStack: false
の際、だった各種 field は出力されないが%n
は出力される点も注意 - (スルーできる field があれば便利だったかも)
- error 出力などの場面で使用するのが良い?
参考:
Tokens
- key - value の値を埋め込める仕組み
- token はハードコートせざるを得ないので、どの key を使用しているか定義しておく必要がある
- これを使うなら message に変数を結合させるのがベストのような?
- 発行ユーザーなどの用途なら悪くはなさそう
- ここでは取り扱わない
参考:
カスタムレイアウト (Custom Layout)
test.js
log4js.addLayout('json', function(config) {
return function(logEvent) {
return JSON.stringify(logEvent, null, 2) + config.separator
}
})
log4js.configure({
appenders: {
out: { type: 'stdout', layout: { type: 'json', separator: ',' } }
},
categories: {
default: { appenders: ['out'], level: 'all', enableCallStack: true }
}
})
const logger = log4js.getLogger()
logger.info('Some info messages')
- 独自のレイアウトを定義することも可能
- appender で指定した設定が config から取得できる
-
functionName
以下はenableCallStack: true
が必要 - callback の引数は
logEvent
の Object なので、これを加工して return に返す
参考:
5. サンプル
test.js
import os from 'os'
import log4js from 'log4js'
import dateFormat from 'date-format'
import chalk from 'chalk'
const LOG_LEVEL = 'ALL' // DEBUG: TRACE or DEBUG, PRODUCTION: INFO or OFF
const levelColors = {
TRACE: { meta: 'grey', body: 'grey', trace: null },
DEBUG: { meta: 'green', body: 'grey', trace: null },
INFO: { meta: 'cyan', body: 'white', trace: null },
WARN: { meta: 'yellow', body: 'yellow', trace: null },
ERROR: { meta: 'red', body: 'red', trace: 'white' },
FATAL: { meta: 'magenta', body: 'magenta', trace: 'white' }
}
const coloring = function(color, text) {
if (color) {
return chalk[color](text)
}
return text
}
log4js.addLayout('origin', function({ addColor }) {
return function(e) {
const date = new Date(e.startTime)
const level = e.level.levelStr.toUpperCase() // 大文字
const hasCallStack = e.hasOwnProperty('callStack') // callStack を持っているか
const dateStr = dateFormat('yyyy-MM-dd hh:mm:ss.SSS', date)
const message = e.data.join(' ') // データはスペース区切り
const levelStr = level.padEnd(5).slice(0, 5) // 5文字
const color = levelColors[level]
// メタ情報
const meta = `${levelStr} ${dateStr} [${e.categoryName}]`
const prefix = addColor ? coloring(color.meta, meta) : meta
// ログ本体
const body = addColor ? coloring(color.body, message) : message
// スタックトレース
let suffix = ''
if (hasCallStack && color.trace) {
const callStack = e.callStack
suffix += os.EOL
suffix += addColor ? coloring(color.trace, callStack) : callStack
}
return `${prefix} ${body}${suffix}`
}
})
log4js.configure({
appenders: {
out: { type: 'stdout', layout: { type: 'origin', addColor: true } },
logFile: { type: 'file', filename: 'logs/application.log', layout: { type: 'origin', addColor: false } },
errFile: { type: 'file', filename: 'logs/error.log', layout: { type: 'origin', addColor: false } },
log: { type: 'logLevelFilter', appender: 'logFile', level: 'info' },
err: { type: 'logLevelFilter', appender: 'errFile', level: 'warn' },
},
categories: {
default: { appenders: ['out', 'log', 'err'], level: LOG_LEVEL, enableCallStack: true }
}
})
const logger = log4js.getLogger()
logger.trace('Some trace messages')
logger.debug('Some debug messages')
logger.info('Some info messages')
logger.warn('Some warn messages')
logger.error('Some error messages')
logger.fatal('Some fatal messages')
log4js.shutdown(() => {})
- console のカラーリングと、2つのファイルに書き出す形式
- console への出力は
LOG_LEVEL
で制御する
- console への出力は
- ファイルは
INFO
以上とWARN
以上の二種類を用意 (error を把握しやすくする)- これにローリングを加えれば完成となる (見やすさのため省略)
- terminal が 256色 をサポートしていない場合、通常色出力となる
- 独自の level を定義することは可能だが、カラーリングはサポートしないらしい?
参考:
以上