awesome-hacks
Docs

NestJS Winstonでロギングを整える

nest-winston を使って構造化ログ(JSON)を出力し、ログレベル・フォーマットを環境ごとに切り替える

最終更新:2026/06/05

NestJSの基礎まで終えている前提。
この記事では、NestJS 標準の LoggerWinston に差し替えて、開発環境では見やすいカラーログ、本番環境では JSON ログを出力する最小構成を実装する。

この記事でやること

  • winston / nest-winston を導入する
  • AppModuleWinstonModule を設定し、標準 Logger を差し替える
  • 開発環境:カラー付きテキストログ、本番環境:JSON ログを切り替える
  • UsersService にロガーを注入してログを出力する
  • 実際に動かしてログを確認する

触るファイル一覧

api-sample/
└─ src/
   ├─ app.module.ts          # WinstonModule を設定・NestJS Logger を差し替え
   ├─ main.ts                # app.useLogger() を追加
   └─ users/
      └─ users.service.ts    # Logger を注入してログ出力

1. パッケージを追加する

pnpm の場合。

pnpm add winston nest-winston

npm の場合。

npm install winston nest-winston

2. AppModuleWinstonModule を設定する

編集するファイルは api-sample/src/app.module.ts

import { Module } from "@nestjs/common";
import { WinstonModule } from "nest-winston";
import * as winston from "winston";
import { UsersModule } from "./users/users.module";

const isDev = process.env.NODE_ENV !== "production";

@Module({
  imports: [
    WinstonModule.forRoot({
      level: isDev ? "debug" : "info",
      transports: [
        new winston.transports.Console({
          format: isDev
            ? winston.format.combine(
                winston.format.colorize(),
                winston.format.timestamp({ format: "HH:mm:ss" }),
                winston.format.printf(
                  ({ level, message, timestamp, context }) =>
                    `${timestamp} [${context ?? "App"}] ${level}: ${message}`
                )
              )
            : winston.format.combine(
                winston.format.timestamp(),
                winston.format.json()
              ),
        }),
      ],
    }),
    UsersModule,
  ],
})
export class AppModule {}

ポイント

  • NODE_ENVproduction 以外の場合は開発向けフォーマット(カラー + 短いタイムスタンプ)を使う。
  • 本番では winston.format.json() で1行JSONを出力する。CloudWatch / Datadog などのログ収集ツールが自動パースできる形式。
  • level: "debug" にすると debug 以上すべてを出力する。本番で "info" にすると debug ログが出ない。

3. main.ts でアプリのロガーを差し替える

編集するファイルは api-sample/src/main.ts

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { WINSTON_MODULE_NEST_PROVIDER } from "nest-winston";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // NestJS 内部ログ(起動ログ等)も Winston に差し替える
  app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER));

  await app.listen(3000);
}
bootstrap();

WINSTON_MODULE_NEST_PROVIDERnest-winston が提供するトークン。
これを useLogger に渡すことで、フレームワーク自身のログ(モジュール起動ログ等)も Winston 経由で出力される。

4. UsersService にロガーを注入する

編集するファイルは api-sample/src/users/users.service.ts

import { Injectable, Inject, NotFoundException } from "@nestjs/common";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";
import { Logger } from "winston";

@Injectable()
export class UsersService {
  constructor(
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
  ) {}

  findAll() {
    this.logger.info("全ユーザーを取得", { context: "UsersService" });
    return [{ id: 1, name: "Alice" }];
  }

  findOne(id: number) {
    this.logger.debug(`ユーザーを取得: id=${id}`, { context: "UsersService" });
    const user = { id, name: "Alice" };
    if (!user) {
      this.logger.warn(`ユーザーが見つからない: id=${id}`, { context: "UsersService" });
      throw new NotFoundException(`User ${id} not found`);
    }
    return user;
  }
}

5. 動かして確認する

サーバーを起動する。

pnpm start:dev

開発環境の出力例

12:34:56 [NestFactory] info: Starting Nest application...
12:34:56 [InstanceLoader] info: AppModule dependencies initialized
12:34:56 [RoutesResolver] info: UsersController {/users}:
12:34:56 [NestApplication] info: Nest application successfully started

GET /users を叩く。

curl http://localhost:3000/users
12:34:57 [UsersService] info: 全ユーザーを取得

本番環境の出力例(JSON)

NODE_ENV=production で起動すると1行JSONになる。

{"level":"info","message":"全ユーザーを取得","context":"UsersService","timestamp":"2026-06-05T03:34:57.123Z"}

ログレベルの使い分け

レベル用途の目安
error予期しないエラー、例外キャッチ
warn異常ではないが注意が必要な状態(リソース不足など)
info正常な操作の記録(リクエスト受付、処理完了など)
debug開発時のデバッグ情報(変数の中身など)

本番では level: "info" にして debug ログを出力しないのが一般的。

ファイルへの出力を追加する(オプション)

コンソールに加えてファイルにも出力したい場合は transports に追加する。

transports: [
  new winston.transports.Console({ ... }),
  new winston.transports.File({
    filename: "logs/error.log",
    level: "error",  // error ログのみ記録
    format: winston.format.combine(
      winston.format.timestamp(),
      winston.format.json()
    ),
  }),
  new winston.transports.File({
    filename: "logs/combined.log",  // 全レベルを記録
    format: winston.format.combine(
      winston.format.timestamp(),
      winston.format.json()
    ),
  }),
],

logs/ ディレクトリは事前に作成するか、.gitignore に追加しておく。

mkdir -p logs
echo "logs/" >> .gitignore