{ “expert_role”: “SRE/DevOps Engineer”, “topic”: “Advanced awk techniques for multi-line log parsing”, “focus”: “RS and getline for structured data extraction”, “tools”: [“gawk”, “jq”, “systemd”, “bash”] } 本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
awkのRSとgetlineを用いた複数行ログパターンの抽出とJSON構造化の自動化
【導入と前提】
スタックトレース等の複数行に渡る非構造化ログを、awkのレコード制御技術を用いて解析し、後続の監視基盤が処理可能なJSON形式へ変換・自動転送します。
OS: GNU/Linux (gawk 4.0以降推奨)
ツール: awk (gawk), jq, systemd, curl
前提: 対象ログが空行または特定のデリミタで区切られていること
【処理フローと設計】
graph TD
A["Raw Log File"] --> B{"awk Record Separator"}
B -->|RSでブロック分割| C["getlineで詳細行をスキャン"]
C --> D["一時配列にバッファリング"]
D --> E["JSON文字列として出力"]
E --> F["jqによるバリデーション"]
F --> G["監視エンドポイントへ転送"]
awkのRS(Record Separator)を空文字("")に設定することで、連続した空行をレコードの区切りとして扱い、getlineで各レコード内の特定行を動的に抽出します。
【実装:堅牢な自動化スクリプト】
#!/bin/bash
# ==============================================================================
# Multi-line Log Processor to JSON
# ==============================================================================
set -euo pipefail
IFS=$'\n\t'
# 設定
LOG_FILE="/var/log/app/error.log"
OUTPUT_JSON="/tmp/log_processed.json"
ENDPOINT_URL="http://localhost:9091/metrics/job/log_check"
# クリーンアップ処理
trap 'rm -f "$OUTPUT_JSON"' EXIT
# メイン処理:awkによる抽出と整形
# ------------------------------------------------------------------------------
# RS="": 空行をレコード区切りとする
# getline: 現在のレコード内でさらに行を進めて読み込む
# ------------------------------------------------------------------------------
process_logs() {
gawk '
BEGIN {
RS = ""; # 空行をレコード区切りに設定
FS = "\n"; # 行内フィールドは改行で区切る
}
{
# 1行目はヘッダー情報(タイムスタンプとレベル)と仮定
header = $1;
# 2行目以降(スタックトレース等)を結合
body = "";
for(i=2; i<=NF; i++) {
body = body $i " ";
}
# 特定のキーワードが含まれる場合のみ抽出
if (header ~ /ERROR/) {
printf "{\"timestamp\":\"%s\", \"level\":\"ERROR\", \"message\":\"%s\"}\n", $1, body;
}
}' "$LOG_FILE" | \
jq -c '.' > "$OUTPUT_JSON" # -c: 出力をコンパクトな1行JSONにする
}
# 転送処理
upload_logs() {
if [[ -s "$OUTPUT_JSON" ]]; then
curl -X POST -H "Content-Type: application/json" \
--data-binary "@$OUTPUT_JSON" \
-L # リダイレクトに従う \
-s # 進捗バーを非表示にする \
-o /dev/null \
"$ENDPOINT_URL"
fi
}
process_logs
upload_logs
systemdによる定期実行の設定例
/etc/systemd/system/log-parser.timer
[Unit] Description=Run multi-line log parser every 5 minutes [Timer] OnCalendar=*:0/5 Persistent=true [Install] WantedBy=timers.target
【検証と運用】
正常系の確認: スクリプトを手動実行し、
jqがエラーを吐かずにJSONを出力するか確認します。bash log_processor.sh
エラーログの確認:
systemd経由で実行している場合、標準出力とエラー出力はjournalctlで追跡可能です。journalctl -u log-parser.service -f
awk動作テスト: 特定のレコードのみが抽出されているか、
RSの動作をテストファイルで確認します。
【トラブルシューティングと落とし穴】
awkのバリアント:
mawk(Debian/Ubuntuのデフォルトの一部)はRSに正規表現を使用する際の挙動がgawkと異なる場合があります。ポータビリティが必要な場合はgawkを明示的に指定してください。メモリ制限:
RS=""で巨大なログファイルを処理する場合、1つのレコード(空行間のデータ)が極端に大きいとメモリを消費します。レコードサイズに上限があることを確認してください。権限問題:
/var/log配下のファイルを読み取る際、実行ユーザーがロググループ(adm等)に属している必要があります。sudoを多用せず、適切なグループ権限で実行してください。JSONのエスケープ: 上記のawk内での手動JSON構築は、メッセージ内にダブルクォートが含まれると破壊されます。実戦では
jq --argを使って変数を渡す構成を推奨します。
【まとめ】
運用の冪等性を維持するためのポイント:
状態管理: 処理済みログのオフセット(inodeとバイト数)を記録し、二重処理を防止する。
パイプラインの保護:
set -o pipefailにより、awkが成功してもjqやcurlが失敗した場合にスクリプト全体を異常終了させる。構造化の徹底: awkは抽出に徹し、複雑なエスケープが必要なJSON整形は
jq等の専用ツールへ委ねる。

