ログシステムの構成:ログ出力・収集・ローテーション
ログ出力とログ収集の違い、ログを実現するシステム全体の構成、ログファイルのローテーションの仕組みをゼロから解説
TL;DR
- ログ出力はアプリが書き出す行為、ログ収集はそれを別の場所に集める行為。役割が異なる
- ログシステムは「アプリ → 出力 → 収集・転送 → 蓄積 → 検索・可視化」の流れで構成される
- ログローテーションとはログファイルが無限に大きくなるのを防ぐ仕組み。一定サイズ・日時で古いファイルを切り替える
- Fluentd はログ収集の代表ツール。ファイルやソケットからログを受け取り、変換して複数の転送先に送れる
- 本番環境では単体サーバーで完結させず、専用のログ基盤(ELK、CloudWatch など)に集約するのが一般的
目的
アプリ単体でログを出力するだけでは、現実の運用では不十分になる。
- サーバーが複数台あると、各サーバーのログが別々のファイルに分散する
- ログファイルを放置すると、ディスクが満杯になってサーバーが停止する
- ログがテキストファイルのままでは「特定ユーザーのエラーだけを素早く検索する」ができない
この記事では、ログを「出力するだけ」で終わらせず、実際の運用に耐えるシステムとして構築するための全体像を説明する。
ログ出力とログ収集の違い
この2つは別の役割を担う。
| ログ出力 | ログ収集 | |
|---|---|---|
| 誰が行うか | アプリケーション自身 | 収集ツール(Fluentd、Logstash など)またはクラウドサービス |
| 何をするか | ログをファイルや標準出力に書き出す | 出力されたログを読み取って別の場所へ転送する |
| 場所 | アプリが動くサーバー内 | アプリとは独立したプロセスやサービス |
ログ出力(アプリの仕事)
アプリはログライブラリを使って、設定した出力先にログを書き出すだけ。
「あとでどこかに送る」という責任はアプリは持たない。
アプリ → /var/log/app/app.log(ファイルに書き出す)
アプリ → 標準出力(コンソールに書き出す)ログ収集(インフラの仕事)
アプリが出力したログを、収集ツールが拾い上げて転送する。
これによってアプリは「書き出す」だけに集中できる。
/var/log/app/app.log → [Fluentd] → Elasticsearch(全サーバーのログを1か所に集める)
標準出力 → [CloudWatch Agent] → AWS CloudWatch LogsFluentdとは
Fluentd はログ収集の代表的なツール。「ログを受け取る → 加工する → 送り出す」の3ステップを担う。
基本的な仕組み
Fluentd はプラグインという拡張機能の組み合わせで動作する。
[Input プラグイン] → [Filter プラグイン] → [Output プラグイン]
ログを受け取る 内容を変換・加工する 転送先へ送り出す| プラグイン種別 | 役割 | 例 |
|---|---|---|
| Input | ログを読み込む | ファイルを監視 / HTTP で受信 / syslog 受信 |
| Filter | ログを変換・加工する | JSON にパース / フィールドを追加 / 不要な行を除外 |
| Output | ログを送り出す | Elasticsearch へ送信 / S3 へ保存 / 別のサーバーへ転送 |
設定ファイルの例
Fluentd の設定は fluent.conf という設定ファイルで記述する。
# ① Inputプラグイン:アプリのログファイルを監視する
<source>
@type tail # ファイルを末尾から追いかけて読む
path /var/log/app/app.log # 監視するファイル
pos_file /var/log/td-agent/app.pos # どこまで読んだかを記録するファイル
tag app.log # このログに付けるタグ名
<parse>
@type json # JSONフォーマットとして解析する
</parse>
</source>
# ② Filterプラグイン:フィールドを追加する
<filter app.log>
@type record_transformer
<record>
hostname "#{Socket.gethostname}" # サーバーのホスト名を自動で付加する
</record>
</filter>
# ③ Outputプラグイン:Elasticsearchへ転送する
<match app.log>
@type elasticsearch
host elasticsearch.example.com
port 9200
index_name app-logs
<buffer>
flush_interval 5s # 5秒ごとにまとめて送信(リアルタイムではなくバッファリング)
</buffer>
</match>上記の設定で行われることを順番に追うと:
/var/log/app/app.logに新しい行が追記されるたびに Fluentd が検知する- 各行を JSON としてパースし、
hostnameフィールドを追加する - 5秒ごとにまとめて Elasticsearch へ送信する
バッファリング(まとめて送る仕組み)
Fluentd は受け取ったログを1行ずつ即座に転送するのではなく、一定量・一定時間ためてからまとめて送信する(バッファリング)。
これにより:
- ネットワーク通信の回数を減らしてパフォーマンスを上げられる
- 転送先(Elasticsearch など)が一時的に落ちても、ログをバッファに保持して後から再送できる
複数の転送先に同時に送ることもできる
# 同じログを Elasticsearch と S3 の両方へ送る
<match app.log>
@type copy
<store>
@type elasticsearch
host elasticsearch.example.com
...
</store>
<store>
@type s3
s3_bucket my-log-bucket
...
</store>
</match>Fluentdとよく似たツールとの違い
| ツール | 特徴 |
|---|---|
| Fluentd | プラグインが豊富。設定の自由度が高い。Ruby 製 |
| Fluent Bit | Fluentd の軽量版。コンテナ・組み込み向け。C 製でメモリ消費が少ない |
| Filebeat | Elastic 社製の軽量エージェント。ELKスタックとの親和性が高い |
| Logstash | Elastic 社製。変換・加工の表現力が高いが、重い |
Kubernetes 環境では Fluent Bit を各ノードに DaemonSet として配置し、Fluentd に集約するという構成がよく使われる。
ログを実現するシステム全体の構成
全体像
graph TD
App["アプリ<br/>(NestJS / Spring Boot など)"]
File["ログファイル<br/>/var/log/app/app.log"]
Agent["収集エージェント<br/>Fluentd / Filebeat / CloudWatch Agent"]
Storage["ストレージ<br/>Elasticsearch / CloudWatch Logs / S3"]
UI["可視化 UI<br/>Kibana / Grafana / CloudWatch Insights"]
App -->|ログ出力| File
File -->|読み取り| Agent
Agent -->|転送| Storage
Storage -->|検索・可視化| UI
subgraph アプリサーバー
App
File
Agent
end
subgraph ログ基盤(別サーバー)
Storage
UI
end
各コンポーネントの役割
| 役割 | 具体例 | 説明 |
|---|---|---|
| アプリ | NestJS, Spring Boot, Django | ログを出力する |
| 収集エージェント | Fluentd, Filebeat, CloudWatch Agent | ログを読み取って転送する |
| ストレージ | Elasticsearch, CloudWatch Logs, BigQuery | ログを蓄積・検索できる形で保存する |
| 可視化 | Kibana, Grafana, CloudWatch Insights | ダッシュボードで確認・検索する |
代表的な構成パターン
ELKスタック(オンプレ・自前構築向け)
graph LR
App["アプリ"] --> FB["Filebeat / Logstash<br/>(収集・転送)"]
FB --> ES["Elasticsearch<br/>(蓄積・検索)"]
ES --> Kibana["Kibana<br/>(可視化)"]
- Elasticsearch: ログを全文検索できる形で蓄積するデータベース
- Logstash / Filebeat: 収集・転送エージェント
- Kibana: Elasticsearch のログを検索・グラフ表示する UI
AWSの場合(マネージドサービス)
graph LR
App["アプリ<br/>(ECS / Lambda)"] -->|標準出力| CWL["CloudWatch Logs<br/>(自動収集)"]
CWL --> Insights["CloudWatch Insights<br/>(検索・分析)"]
CWL -->|必要なら| S3["S3<br/>(長期保存)"]
ECSやLambdaの場合、標準出力に書くだけで CloudWatch Logs が自動でログを収集してくれる。
アプリ側に収集エージェントを置く必要がない。
ログファイルのローテーションとは
問題:ログファイルは放置すると巨大化する
ログを1つのファイルに書き続けると、時間とともにファイルが数GB・数十GBに膨れ上がる。
ディスクが満杯になるとサーバーが停止したり、新しいログが書けなくなったりする。
解決策:ローテーション(定期的にファイルを切り替える)
ログローテーションとは、古いログファイルを「切り替える」仕組み。
app.log ← 現在書き込み中のファイル
app.log.1 ← 昨日のログ(1世代前)
app.log.2 ← 一昨日のログ(2世代前)
app.log.3 ← 3世代前
...(設定した世代数を超えたら削除)ローテーションのトリガー
| トリガー | 説明 | 例 |
|---|---|---|
| 日時ベース | 毎日0時など、決まった時刻にファイルを切り替える | app-2026-06-16.log のように日付をファイル名に含める |
| サイズベース | ファイルが一定サイズを超えたら切り替える | 100MB を超えたら新しいファイルに切り替える |
| 組み合わせ | どちらかの条件を満たしたら切り替える | 多くの設定でよく使われる |
ローテーションを行うツール
| ツール | 説明 |
|---|---|
| logrotate | Linux の標準ツール。設定ファイルでローテーションルールを定義する |
| ログライブラリ内蔵機能 | Winston の DailyRotateFile など、ライブラリ自身がローテーションを担う |
| クラウドサービス | CloudWatch Logs などはストレージ上限や保持期間を設定するだけでよい |
linuxのlogrotate設定例
/var/log/app/app.log {
daily # 毎日ローテーション
rotate 14 # 14世代(14日分)保持
compress # 古いファイルを gzip 圧縮
missingok # ファイルがなくてもエラーにしない
notifempty # ファイルが空ならローテーションしない
create 0640 app app # 新しいファイルを作成するパーミッション設定
}Winston(Node.js)でのローテーション設定例
import DailyRotateFile from 'winston-daily-rotate-file';
const transport = new DailyRotateFile({
filename: '/var/log/app/app-%DATE%.log',
datePattern: 'YYYY-MM-DD', // 日付ごとにファイルを切り替え
maxSize: '100m', // 100MB を超えたら切り替え
maxFiles: '14d', // 14日分を保持(それ以前は削除)
zippedArchive: true, // 古いファイルを gzip 圧縮
});システム構成の選び方
アプリが1台の場合(小規模)
ローテーション付きのファイルに書き出すだけでも十分なケースがある。
アプリ → ファイル(logrotate でローテーション)ただし、検索はファイルを grep する形になるため、量が増えると辛くなる。
複数サーバー・本番環境(標準的)
各サーバーのログをクラウドや専用のログ基盤に集約する。
graph LR
A["サーバーA のアプリ"]
B["サーバーB のアプリ"]
C["サーバーC のアプリ"]
Log["ログ基盤<br/>CloudWatch Logs / Elasticsearch"]
Search["検索・分析<br/>(全サーバーを横断)"]
A --> Log
B --> Log
C --> Log
Log --> Search
複数サーバーのログが1か所に集まることで「全サーバーを横断して特定ユーザーのエラーを検索する」ができるようになる。
コンテナ環境(Docker / Kubernetes)
コンテナは「ファイルに書く」よりも「標準出力に書く」が基本。
コンテナ管理システムが標準出力を自動収集してくれる。
graph LR
App["アプリ(コンテナ)"] -->|標準出力| Driver["Docker / Kubernetes<br/>ログドライバー"]
Driver --> Log["ログ基盤"]
よくある落とし穴
ローテーションを設定せずにログを放置する
ディスク満杯でサーバーが止まる典型的な障害パターン。
アプリを本番環境に置くときは必ずローテーション設定を確認する。
ログファイルを直接アプリサーバー上だけで見ようとする
tail -f /var/log/app/app.log でリアルタイム確認はできるが、検索・フィルタリングには向かない。
また、サーバーが壊れるとログも失われる。本番では必ずログ基盤に転送して永続化する。
アプリが直接ログ基盤に書きに行く設計にする
ログ基盤が落ちたときにアプリの処理がブロックされるリスクがある。
アプリはローカルのファイルか標準出力に書き、転送は収集エージェントに任せる設計が安全。