状況
Node.jsアプリケーションでWinstonを使用しており、
ログローテーションのためにLinuxのlogrotateを設定しています。
winstonの設定に関してはこちらの記事にまとめています。
logrotateの設定内容
/var/log/applog/*.log
/var/log/applog/*/*.log {
create 0640 nginx nginx
daily
rotate 120
missingok
notifempty
compress
delaycompress
}
次の日
logrotateが実行され新しいファイル(app-all.log)が新規作成
しかしlogrotateされた前のファイル(app-all.log-20250626)のログファイルにログが出続けました。
-rw-r-----. 1 nginx nginx 0 Jul 26 00:00 app-all.log
-rw-r--r--. 1 nginx nginx 62563 Jul 25 15:49 app-all.log-20250626
原因
logrotateでファイル名を変更しても、Node.jsプロセスが古いファイルディスクリプタを掴んだまま新しいファイルに書き込んでいないことに気づく。
Node.jsプロセスが古いファイルディスクリプタを使い続けることで、新しいローテーションファイルではなく、リネームされた古いファイルにログを書き込み続けてしまうとのことでNode.jsプロセスの再起動が必須だった。
解決策:winston-daily-rotate-fileの導入
アプリケーション側でローテーションできるライブラリwinston-daily-rotate-fileの導入
import winston from 'winston';
import 'winston-daily-rotate-file';
const logger = winston.createLogger({
level: 'info', // 必要に応じてログレベルを調整
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.json() // ファイル出力はJSON形式が推奨
),
transports: [
new winston.transports.DailyRotateFile({
filename: 'application-%DATE%.log', // 例: application-2025-07-14.log
dirname: '/var/log/app', // ログの出力ディレクトリ
datePattern: 'YYYY-MM-DD', // 毎日0時にローテーション
zippedArchive: true, // 古いログファイルをgzip圧縮するかどうか
maxSize: '20m', // 1つのファイルの最大サイズ (例: 20MB) - これを超えると日付に関わらずローテーション
maxFiles: '14d', // 保持する期間 (例: 14日分) - これを超えると古いファイルが削除
}),
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf(
(info) => `${info.timestamp} ${info.level}: ${info.message}`
)
),
}),
],
});
export default logger;
Node.jsプロセスの再起動という方法もありますが、再起動時にユーザーセッションが切断されたり、処理中のリクエストが中断されたりする可能性があるため今回はアプリケーションでローテーションする方法にしました。
まとめ
logrotateの定番のミスかもしれませんが、忘備録として残しておきます。