形式は事故が起きる前にしか直せない
一言でいうと
アクセスログは、オンとオフを切り替えるスイッチではなく、形式を設計する作業です。何を残すか(コマンド演算子)、どんな形で残すか(テキスト・JSON)、どこに残すか(シンク)、何だけを残すか(フィルター)の4つを決めるもので、決めておかないと、事故が起きた後に残っているのは200 GET /だけになります。
なぜ必要なのか
障害が起きたときに最初に尋ねることは、いつも同じです。誰が何をリクエストし、どこへ行き、どれだけかかり、なぜ失敗したのか。この4つをログ1行で答えられれば、原因を数分で絞り込めます。なければ、アプリケーションのログを探し、それもなければ推測が始まります。
問題は、この形式を事故が起きる前に決めなければならないことです。事故が起きた後で「アップストリームのアドレスも残しておけばよかった」と気づいても、その事故のログにはすでにありません。そのためアクセスログは、オブザーバビリティの設定の中で唯一、取り返しがつかない部分です。
どう動くのか
コマンド演算子。形式文字列の%...%は、値1つを意味します。よく使うものだけを挙げると、次のとおりです。
| 演算子 | 何か |
|---|---|
%RESPONSE_CODE% |
ステータスコード |
%RESPONSE_FLAGS% |
Envoyが付ける短い原因表示(UT・UF・NRなど) |
%UPSTREAM_HOST% |
実際に受け取ったアップストリームのアドレスとポート |
%DURATION% |
リクエスト全体にかかったミリ秒 |
%BYTES_SENT% |
送り出したバイト数 |
%REQ(이름)%・%RESP(이름)% |
リクエスト・レスポンスのヘッダー(プレースホルダーはヘッダー名です)。:METHODのようにコロンで始まるものは疑似ヘッダーです |
値がないとき。アップストリームへ行かなかった応答(direct_response)には、%UPSTREAM_HOST%に入れる値がなく、送らなかったヘッダーも同じです。Envoyはその位置を空にせず、決まった表示で埋めます。ログを機械で読むときに、この表示を本物の値と勘違いすると、集計が静かにずれます。
テキストかJSONか。区切り文字で切ったテキストは人間には読みやすいですが、値の中に区切り文字が入った瞬間にフィールドがずれます。コレクターに送ってクエリする予定なら、最初からjson_formatのほうが優れています。キーの名前は自由に決められ、値の位置には同じ演算子を使います。
シンクとフィルター。access_logはリストなので複数置け、それぞれが別の形式と別のフィルターを持ちます。フィルターは、ステータスコード・所要時間・レスポンスフラグ・ヘッダーの条件で付けられます。実務で最も一般的な構成は、全体のログは短く、エラーのログは詳しく、別々に置くことです。1つのファイルに混ぜると、保存期間を別々に決められないからです。ここでよく引っかかる構文の落とし穴が1つあります。filterはtyped_configの内側ではなく外側、nameと同じ階層に書きます。
現場での姿
「ログが残りません」という場合です。ファイルシンクは、リクエストごとにディスクへ書き込みません。バッファーにためて定期的に空にし、その間隔のデフォルトは10秒です(--file-flush-interval-msec)。リクエストの直後にcatして空なのは、正常です。コンテナがすぐに死ぬ場合は、本当にバッファーごと消えるので、短命なサイドカーでは、この値を小さくするか、標準出力へ送ります。
ログがディスクを埋める場合。アクセスログは、トラフィックに比例して増える唯一のオブザーバビリティのシグナルです。毎秒数千リクエストなら、1日で数十ギガになります。そのため、形式を短く保ち、詳しい形式はフィルターを付けたシンクにだけ使います。
ログを標準出力へ送る場合。コンテナ環境では、ファイルの代わりに/dev/stdoutをパスに指定する構成が一般的です。コレクターがすでにコンテナのログを収集しているので、その経路に乗せるのです。ただしこの場合は、アプリケーションのログとアクセスログが1本の流れに混ざるので、2つを区別する目印を形式に入れておかないと、後で分けて見ることができません。そして標準出力は、ファイルより先に詰まります。受け取る側が遅くなると、その圧力がプロキシまで上がってきます。
リクエストIDを信じて起きたこと。リクエストIDは、複数のサービスを通る1つのリクエストをつなぎ合わせる糸で、一番前のプロキシは、値がなければ作り、あればそのまま引き継ぎます。それは、外から任意の値を入れて送れるという意味でもあります。信頼境界の外から入ってくる場所では、このヘッダーを消して新しく作るほうが安全です。
公式ドキュメント: Access logging・Command line options
次のラボですること
ファイルシンクを有効にして、ログがすぐに見えない理由を確認した後、8フィールドの形式を設計して3種類のリクエストを残します。値がない位置に何が出力されるかを自分で見て、JSON形式に変えてjqで読み、500以上だけを残すシンクを別に置き、最後にリクエストIDをクライアントが渡すときと渡さないときで、どう違うかを確認します。