はじめに
ログ調査でよく使うLinuxコマンドをまとめました。
サーバーの状態確認 → ログの絞り込み → 中身を読む、という調査の順番に沿って並べています。
数十GBクラスの巨大なファイルを扱うときは、ディスクI/Oへの影響もあるので注意しましょう。
不正アクセスが世の中的に多発していますので、いつかあなたも使うときが来るかも...
調査の流れ
-
top/vmstat 1/free -h/df -hでサーバー自体の状態を確認 -
ls -lha/ls -lhtで対象ログのサイズと更新日時を確認、headでフォーマット確認 - 数GBを超えていたら、
tail -n 100000などで調べる範囲を切り出す -
grep -cでエラーの件数をざっくり把握 -
grep | sort | uniq -c | sort -rnでエラーの種類を集計 - 時間帯で絞り込み、
grep -n -Cで行番号を確認してless +行番号Gで前後の流れを掴む - ログの出力をリアルタイムで確認する場合は
tail -F | grep --line-bufferedで監視
早見表
| やりたいこと | コマンド | 注意 |
|---|---|---|
| CPU・メモリを見る |
top / free -h
|
|
| CPU・メモリ・I/Oの推移を見る | vmstat 1 |
|
| ディスクの空きを見る | df -h |
|
| 何が容量を食っているか | du -sh * | sort -h |
配下のファイルをすべて走査する。ファイル数が多いとI/O負荷・時間がかかる |
| ファイルサイズを見る | ls -lha |
|
| 文字列を含む行を探す | grep 'ERROR' app.log |
ファイル全量を読む。巨大ファイルではI/O負荷に注意 |
| 文字列を含む行を除外 | grep -v 'DEBUG' app.log |
同上 |
| 複数の文字列(OR) | grep -E 'ERROR|WARN' app.log |
同上 |
| 前後の行も見る | grep -C 5 'ERROR' app.log |
同上 |
| 長すぎる行を除外 | grep -vE '^.{500,}' app.log |
同上 |
| 圧縮ログを検索 | zgrep 'ERROR' app.log.1.gz |
解凍しながら全量を読む。I/OとCPUを使う |
| 末尾を見る | tail -n 100 app.log |
末尾から読むので巨大ファイルでも軽い |
| リアルタイムで追う | tail -F app.log |
|
| 先頭を見る | head -n 20 app.log |
先頭だけ読むので巨大ファイルでも軽い |
| grepの結果をlessで開く | grep 'ERROR' app.log | less |
grep部分はファイル全量を読む |
| 行番号付きで表示 |
grep -n / less -N / nl -ba
|
いずれもファイル全量を読む |
| 行数を数える | wc -l app.log |
ファイル全量を読む。巨大ファイルでは時間がかかる |
| 件数を数える | grep -c 'ERROR' app.log |
ファイル全量を読む |
| 巨大ログの末尾だけ切り出す | tail -n 100000 app.log > tmp.log |
切り出し先のディスク空き容量を先に確認 |
| 多い順に集計 | ... | sort | uniq -c | sort -rn |
件数が多いと sort がメモリ・一時ファイルを使う |
| 全体を見る | cat app.log |
巨大ファイルには使わない(ターミナルが固まる・全量を読む) |
0. サーバーの状態を確認する
ログを見る前に、サーバー自体に負荷がかかっていないかを確認。
top(CPU・メモリ・プロセス)
使いどころ
- どのプロセスがCPUやメモリを使っているかを確認する
- サーバー全体の負荷(load average)を確認する
top
top -b -n 1 | head -n 20 # 一回だけ出力して終わる(結果を残したいとき)
nproc # CPUのコア数を確認
オプション
| オプション | 説明 |
|---|---|
-b |
バッチモード。対話モード(画面を上書き更新し、キー操作を受け付ける)ではなく、結果をプレーンなテキストで順に出力する。ファイル保存やパイプで他のコマンドに渡すときに使う |
-n 回数 |
指定した回数だけ更新して終了 |
-d 秒 |
更新間隔を指定 |
実行中のキー操作
| キー | 説明 |
|---|---|
P |
CPU使用率順に並べ替え |
M |
メモリ使用率順に並べ替え |
1 |
CPUコアごとに表示 |
c |
コマンドのフルパスを表示 |
q |
終了 |
見るポイント
- 1行目の
load average(1分・5分・15分の平均)。CPUコア数を大きく超えていたら高負荷 -
%Cpu(s)行のwa(I/O待ち)が高い → ディスクが詰まっている可能性
vmstat(CPU・メモリ・I/Oの推移)
使いどころ
- 1秒ごとに1行ずつ追加されていくので、負荷の推移を時系列で見られる
- 「今まさに重くなっている」状況や、ある処理を実行したときの変化を確認する
vmstat 1 # 1秒ごとに表示(Ctrl+Cで終了)
vmstat 1 10 # 1秒ごとに10回表示して終了
vmstat -t 1 # 各行に時刻を付ける
オプション
| オプション | 説明 |
|---|---|
間隔 |
何秒ごとに表示するか(vmstat 1 の 1) |
回数 |
何回表示して終了するか(vmstat 1 10 の 10) |
-t |
各行に時刻を付ける |
出力の項目
| 項目 | 説明 |
|---|---|
r |
実行待ちのプロセス数(CPUコア数より多い状態が続くとCPU不足) |
b |
I/O待ちで止まっているプロセス数 |
free / cache
|
空きメモリ / キャッシュ |
si / so
|
スワップイン / スワップアウト(0以外が続くとメモリ不足) |
bi / bo
|
ディスクの読み込み / 書き込み量 |
us / sy / id / wa
|
CPUのユーザー / システム / アイドル / I/O待ちの割合 |
1行目は起動してからの平均値なので、2行目以降を見ます。
ディスクごとのI/Oを見たいときは iostat を使います(sysstat パッケージが必要)。
iostat -x 1 # ディスクごとの使用率(%util)などを1秒ごとに表示
free(メモリ)
使いどころ
- メモリの空きをさっと確認する
free -h
available 列が実際に使える量です。free 列が少なくても、キャッシュに使われているだけなら問題ないことが多いです。
df / du(ディスク)
使いどころ
- ディスクの空き容量を確認する(ログ肥大化でディスクが100%になり、アプリが落ちることがある)
- どのディレクトリが容量を使っているかを調べる
# ファイルシステムごとの使用率
df -h
# inodeの枯渇チェック(容量は空いているのに書き込めないとき)
df -i
# カレントディレクトリ配下で何が大きいか
du -sh * | sort -h
# /var/log 配下の大きいもの上位10件
du -ah /var/log 2>/dev/null | sort -rh | head -n 10
ls(ログファイルのサイズ・更新日時)
使いどころ
- ログを開く前にサイズを確認する。数GBを超えていたら「6. 巨大なログファイルを扱うとき」の方法を使う
- どのログが最近更新されているか(今書き込まれているか)を確認する
ls -lha /var/log/myapp/ # 隠しファイルも含め、サイズを読みやすい単位で表示
ls -lht /var/log/myapp/ # 更新日時の新しい順
ls -lhS /var/log/myapp/ # サイズの大きい順
オプション
| オプション | 説明 |
|---|---|
-l |
詳細表示(権限・サイズ・更新日時) |
-h |
サイズを K / M / G で表示 |
-a |
. で始まる隠しファイルも表示 |
-t |
更新日時順 |
-S |
サイズ順 |
その他の状況確認
uptime # 稼働時間とload average
1. grep(絞り込み)
使いどころ
- 特定の文字列(ERROR、ユーザーID、リクエストIDなど)を含む行を探す
- 不要な行(DEBUG、ヘルスチェックなど)を除外する
- エラーの件数を数える
grep は、ヒットするかどうかにかかわらずファイルを最初から最後まで全量読みます。数GBを超えるファイルに使う場合は「6. 巨大なログファイルを扱うとき」を参照。
基本
grep 'ERROR' app.log # ERRORを含む行
grep -i 'error' app.log # 大文字小文字を区別しない
grep -n 'ERROR' app.log # 行番号付き
grep -c 'ERROR' app.log # 件数だけ
grep -w 'id' app.log # 単語として一致("hidden" などは除く)
grep -F '[ERROR]' app.log # 正規表現を使わず文字列そのままで検索
grep -E 'ERROR|WARN' app.log # 拡張正規表現を使う
[や.などの記号を含む文字列を探すときは-Fを使います。
-Eは拡張正規表現を使うオプションです。|(OR)や{500,}(500回以上の繰り返し)、+、()をバックスラッシュなしで書けます。-Eなしだと'ERROR\|WARN'や'^.\{500,\}'のようにエスケープが必要です。
除外(-v)
# DEBUGを含む行を除外
grep -v 'DEBUG' app.log
# 複数除外
grep -v -e 'DEBUG' -e 'INFO' app.log
grep -vE 'DEBUG|INFO' app.log
# 「ERRORを含む」かつ「HealthCheckを含まない」
grep 'ERROR' app.log | grep -v 'HealthCheck'
複数条件
# OR(どれかを含む)
grep -e 'ERROR' -e 'WARN' app.log
grep -E 'ERROR|WARN' app.log
# AND(両方を含む)→ パイプでつなぐ
grep 'ERROR' app.log | grep 'userId=123'
# 検索ワードをファイルにまとめておく
grep -f patterns.txt app.log
文字数で除外・絞り込み
スタックトレースや巨大なJSONが1行に出ていて邪魔なときに使います。
# 500文字以上の行を除外
grep -vE '^.{500,}' app.log
# 500文字以上の行だけを表示
grep -E '^.{500,}' app.log
# awkでも同じことができる
awk 'length($0) < 500' app.log
# 各行を先頭200文字で切って表示
cut -c 1-200 app.log
前後の行も表示
grep -A 5 'ERROR' app.log # 後ろ5行(After)
grep -B 5 'ERROR' app.log # 前5行(Before)
grep -C 5 'ERROR' app.log # 前後5行(Context)
例外のスタックトレース全量を見るときは
-A 30くらいで十分かと思います。
一致した部分だけ抜き出す(-o)
# リクエストIDだけ抜き出す
grep -oE 'requestId=[a-z0-9-]+' app.log
# IPアドレスだけ抜き出す
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log
圧縮されたログ(ローテート後など)
zgrep 'ERROR' app.log.1.gz
zcat app.log.*.gz | grep 'ERROR'
zless app.log.1.gz
2. tail / tail -f(末尾を見る・追いかける)
使いどころ
- 直近のログだけを見る
- 巨大なログから末尾の範囲だけを切り出す(ファイル全体を読まないので速い)
-
-f/-Fで、今出ているログをリアルタイムで追いかける(再現確認・デプロイ直後の監視など)
tail app.log # 末尾10行
tail -n 100 app.log # 末尾100行
tail -n +1000 app.log # 1000行目から最後まで
tail -f app.log # 追記を表示し続ける(Ctrl+Cで終了)
tail -F app.log # ログローテートされても追い続ける
tail -F app.log error.log # 複数ファイルを同時に追いかける
-fはファイルそのものを追いかけるので、ローテートで新しいファイルに切り替わると何も表示されなくなります。-Fはファイル名で追いかけるので、ローテート後も続けて表示されます。
tail + grep
# ERRORだけリアルタイムで表示
tail -F app.log | grep --line-buffered 'ERROR'
# さらに絞り込む
tail -F app.log | grep --line-buffered 'ERROR' | grep -v 'HealthCheck'
--line-buffered は、見つけた行を1行ごとにすぐ出力させるオプションです。
grep は出力先によって出し方が変わります。
- 出力先が画面(ターミナル)のとき:1行見つけるたびにすぐ表示する
- 出力先がパイプやファイルのとき:結果をいったん溜め込み(バッファリング)、数KB分たまってからまとめて出力する
そのため、grep の後ろにさらにパイプが続く場合、--line-buffered がないと1つ目の grep が結果を溜め込み、画面になかなか表示されません。ログの出力が少ないと、数分間何も表示されないこともあります。
tail -F app.log | grep 'ERROR'のように grep が最後のコマンドで出力先が画面の場合は、付けなくてもすぐ表示されます。必要になるのは、grep の後ろにさらにパイプが続くときです。
3. head(先頭を見る)
使いどころ
- ログのフォーマット(日時の書式・項目の並び)を確認する
- ログの開始時刻を確認する
- 結果が多いときに最初だけ見る:
grep 'ERROR' app.log | head
head app.log # 先頭10行
head -n 20 app.log # 先頭20行
head -c 500 app.log # 先頭500バイト
# 100〜120行目だけ表示
sed -n '100,120p' app.log
head -n 120 app.log | tail -n 21
4. less(ファイルを開いてスクロール・検索)
使いどころ
- ログを上下にスクロールしながら読む
- ファイル内を検索して、前後の流れを確認する
- grepの結果が多いときや、ターミナルに大量の出力を流したくないときに受け取る
ファイル全体を読み込まずに開くので、巨大なファイルでも vi や cat より軽く開けます。
less app.log
less -N app.log # 行番号付き
less -S app.log # 長い行を折り返さない(←→で横スクロール)
less +G app.log # 末尾から開く
less +F app.log # tail -f のように追いかけるモード
オプション
| オプション | 説明 |
|---|---|
-N |
行番号を表示 |
-n |
行番号を計算しない(巨大ファイルで速くなる) |
-S |
長い行を折り返さない(←→で横スクロール) |
+G |
末尾から開く |
+F |
追いかけモードで開く |
+行番号G |
指定した行から開く |
実行中のキー操作
| キー | 説明 |
|---|---|
/文字列 |
下方向に検索 |
?文字列 |
上方向に検索 |
n / N
|
次 / 前の検索結果へ |
&文字列 |
一致する行だけ表示(grep的なフィルタ) |
&!文字列 |
一致する行を除外して表示 |
g / G
|
先頭 / 末尾へ |
F |
追いかけモード(Ctrl+Cで解除) |
-S |
折り返しのON/OFF切り替え |
q |
終了 |
grep → less
# 結果が多いとき・ターミナルを出力で埋めたくないときはlessで受ける
grep 'ERROR' app.log | less
# 前後の行を付けて、行番号も出して、lessで読む
grep -n -C 10 'ERROR' app.log | less
grepで行番号を確認してから、less +行番号G でその行に飛ぶこともできます。
less +12345G app.log
5. 行番号の表示・行数を数える
使いどころ
- 調査結果を共有するときに「何行目」と場所を示す
- grepで見つけた行の前後を、lessやsedで開き直す
- ログの規模感(何行あるか)を把握する
行番号を付けて表示する
grep -n 'ERROR' app.log # 一致した行を行番号付きで
less -N app.log # less で行番号付き(less 内で -N を押しても切り替え可)
nl -ba app.log | less # 空行も含めて行番号を付ける
cat -n app.log | head -n 50 # 先頭50行を行番号付きで
awk '{print NR": "$0}' app.log | tail -n 20 # awk で行番号を付ける
nlは何も指定しないと空行に番号を振りません。ログの行番号とずれないように-baを付けます。
tailには行番号を付けるオプションがないので、行番号が必要ならgrep -nやawkを通します。
特定の行番号だけ表示する
sed -n '12345p' app.log # 12345行目だけ
sed -n '12340,12360p' app.log # 12340〜12360行目
sed -n '12345{p;q}' app.log # 12345行目を表示したら読むのをやめる(巨大ファイル向け)
sed -n '12345p'は表示した後もファイルの最後まで読み続けます。巨大なファイルではqを付けて途中で止めます。
行数を数える
wc -l app.log # ファイル全体の行数
grep -c 'ERROR' app.log # ERRORを含む行数
grep 'ERROR' app.log | wc -l # 上と同じ(パイプの途中で数えたいとき)
wc -lもファイルを最後まで読むので、数十GBのファイルでは時間がかかります。
6. 巨大なログファイルを扱うとき(数GB〜数十GB)
本番サーバーで実行する場合は、まず ls -lha でファイルの容量を確認しましょう。
cat を使わない理由
cat app.log # 巨大なログには使わない
- ファイル全体を画面に流そうとする。数十GBがターミナル(SSH越しならネットワークも)に流れ続け、Ctrl+C も効きにくく、ターミナルが固まる
- ファイルを最初から最後まで全部読むので、ディスクI/Oを大量に使う(下で説明)
- バイナリ部分が混ざっていると、ターミナルの表示が崩れることがある
-
cat app.log | grep 'ERROR'はgrep 'ERROR' app.logと同じ結果になる。cat を挟むとプロセスとパイプのコピーが増えるだけ
grep も全体を読む
grep は、ヒットするかどうかにかかわらずファイルを最初から最後まで読みます。数十GBのファイルに対して実行すると、次のような影響があります。
- ディスクの読み込み帯域を使い切り、同じディスクに書き込んでいるアプリの処理が遅くなる(
topやvmstatのwaが上がる) - Linux は読み込んだデータをメモリ(ページキャッシュ)に残すので、アプリがよく使うデータがキャッシュから追い出されることがある
- 結果が返ってくるまで数分〜数十分かかる
tail / head は巨大なファイルでも速い
tail は、ファイルの末尾にシーク(ジャンプ)してから必要な分だけ読みます。head は必要な行数を読んだら止まります。どちらもファイル全体は読まないので、数十GBでもすぐに結果が出ます。
直近のログを調べたいときは、tail で必要な範囲だけ切り出してから grep します。
# 末尾10万行だけ切り出して、そこに grep をかける
tail -n 100000 app.log | grep 'ERROR'
# 何度も調べるなら一度ファイルに保存しておく
tail -n 100000 app.log > /tmp/app_tail.log
grep -n 'ERROR' /tmp/app_tail.log | less
# 行数ではなくサイズで切り出す(末尾500MB)
tail -c 500M app.log > /tmp/app_tail.log
切り出したファイルを置く場所の空き容量は
df -hで確認しておきます。ログと同じディスクがいっぱいになると、アプリが書き込めなくなります。
tail -cで切り出すと、先頭の1行が途中から始まることがあります。
less は全体を読まずに開ける
less はファイル全体を読み込まずに開くので、巨大なファイルでも開くのは速いです。ただし、次の操作では全体を読むので時間がかかります。
-
G(末尾へ移動)したときの行番号の計算 →less -n(小文字)で行番号の計算を止めると速くなる -
-Nでの行番号表示 -
/で検索して、なかなかヒットしないとき
less -n +G app.log # 行番号を計算せずに末尾から開く
7. パイプで組み合わせる
|(パイプ)で左のコマンドの出力を右のコマンドに渡せます。ログ調査は基本的にこれの組み合わせです。
使いどころ
- エラーの種類ごとの件数や、IPごとのアクセス数を集計する
- 時間帯や列で絞り込む
- 絞り込んだ結果をファイルに保存する
件数を集計する
# エラーメッセージの種類ごとに件数を数えて、多い順に
grep 'ERROR' app.log | sort | uniq -c | sort -rn | head
# アクセスログのIPごとのアクセス数(1列目がIPの場合)
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head
# ステータスコードごとの件数(9列目の場合)
awk '{print $9}' access.log | sort | uniq -c | sort -rn
uniqは隣り合った重複しかまとめないので、先にsortが必要です。
時間帯で絞り込む
# 10時台のエラーだけ
grep '2026-11-09 10:' app.log | grep 'ERROR'
# 10:00〜10:30 の範囲(開始と終了のパターンに一致する行で挟む)
sed -n '/2026-11-09 10:00/,/2026-11-09 10:30/p' app.log
# 1時間ごとのエラー件数(行頭が "2026-11-09 10:23:45" 形式の場合)
grep 'ERROR' app.log | cut -c 1-13 | sort | uniq -c
sedの範囲指定は、開始パターンに一致する行が存在しないと何も出ません。秒まで書かずに分単位くらいで指定します。
特定の列だけ取り出す
# スペース区切りの3列目
awk '{print $3}' app.log
# カンマ区切りの2列目
cut -d ',' -f 2 app.csv
結果を保存する
grep 'ERROR' app.log > error_only.log # 上書き保存
grep 'ERROR' app.log >> error_only.log # 追記
grep 'ERROR' app.log | tee error_only.log # 画面に表示しつつ保存
8. その他
find(ログファイルを探す)
使いどころ
- ログの置き場所がわからないときに探す
- 最近更新されたログや、大きなログを探す
# 直近1日以内に更新されたログ
find /var/log -name '*.log' -mtime -1
# 100MB以上のファイル
find /var/log -size +100M
journalctl(systemd のログ)
使いどころ
- systemd で動いているサービス(nginx など)のログを、期間や重要度を指定して見る
journalctl -u nginx # サービス指定
journalctl -u nginx -f # 追いかける
journalctl -u nginx --since '1 hour ago' # 1時間前から
journalctl -p err # エラー以上だけ
systemctl status nginxでもログが表示されますが、こちらはサービスの状態(起動中か・PIDなど)と直近の数行だけです。ログは同じものなので、さかのぼって見たいときや期間を指定したいときはjournalctlを使います。
watch(定期的に実行する)
使いどころ
- 件数やディスク使用量など、同じコマンドの結果を一定間隔で見続ける
# 2秒ごとにエラー件数を表示
watch -n 2 "grep -c 'ERROR' app.log"
# ディスク使用量を監視
watch -n 5 df -h
vmstatは結果が下に追加されていきますが、watchは同じ画面を上書きして更新します。
lsof(削除したのに容量が空かないとき)
ログを rm しても、プロセスがファイルを開いたままだと容量が解放されません。
lsof | grep deleted
本番で巨大ログの中身を消すときは、
rmではなく: > app.log(中身を空にする)を使います。
おわりに
随時追記していきます。