プログラミング入門者向け「ログ設計まとめ」

はじめに

システム開発をする際、ログ設計は非常に重要な工程です。
しかし、ログというものは実運用(実際にサービスが開始されている状態)に入って障害などがあってからようやく役に立ってくるもので、開発時には意識が向きにくいという部分があるのも事実です。
ログを適切に出力し、最大限に活用することで「障害時の調査コスト」「システム運用コスト」を抑えたり、よりよいサービスを提供する手掛かりやヒントにもなります。

ログってなに?なんのためにあるの?

ログとは「足跡」という意味で、目に見えないプログラムの処理が

「どんなユーザー」「どの順序」「どこまで」処理されているのか

という足跡を残すための機能です。なので、ログを適切に出力させる(残しておく)ことで

誰がどういう画面を見ていて、どんな処理がされて、どこでエラーになっちゃってるのか

が後からでも確認できるわけですね。

そうすることで、実際にユーザーから「ログインできない」といった何か不具合のお問い合わせが来た時の「原因調査」が行いやすくなります。

逆にログを残していなかった場合、「ほとんど何もわからない」という事になってしまい、原因を探るのにかなり頭を悩ませる事になります。

実際の運用では、「問い合わせが来る前」に気づかなければいけません。大規模になるほど「気づくのが遅れる=大損害」なわけです。

被害が拡大する前にいち早く気づくためにきちんと「どこでどういった内容のログを出力しておくか」を事前に決め、そのルールに沿って「ログ監視ツール」といったリアルタイムでエラーではないログなどもどんどん出力されている中で「エラーログ」を見つけ出してメールでお知らせをしてくれるツールを使って日々リアルタイムに監視を行なっています。

まとめると下記のような場合のためにログを利用します。

  1. 不具合の原因を特定するため
  2. 不正利用の検出
  3. 利用統計の取得

3のように「ユーザーがどんなページを踏んで、どこで離脱してしまっているか。何を買っているか。どのページを見てから買っているか。」といった「利用情報」を大量に集めて、そこから「何が売れているのか」「何を改善すれば売れるのか」といった行動解析を行うためにも利用されます。いわゆる「ビッグデータ」なんてのもその1つです。

ログの出し方は?

WEBサービス部でやっているようにPHP言語でのログ出力方法や他に各言語でのログの出力方法がありますので、それに沿って出力させます。

また、ほとんどの現場ではFW(フレームワーク)を使っていますので、FWにログを出力させる便利なクラス(FuelPHPであればLogクラス)が存在しているので、それを使います。

さらにログは「画面」に表示させることもできますが、実際の運用ではログは個人情報に近いものが含まれていたりハッカーがハッキングする手掛かりを与えてしまうものでもあるので、「ログファイル」といったファイルに出力させ、そのファイルは外部からは見れない様な設定にするか、専用のログを記録しておけるサーバー(ログ記録専用のデータベースのようなもの)に保存しておきます。

フロント側でのログ出力

また、ログは基本的にはサーバー側の処理で出力をします。

フロント側(JavaScript)でconsole.log()console.error()などを使ってログを出すこともできますが、ユーザーが見ているブラウザ画面内(しかも、開発ツールを開かなければ見れない)で表示されるだけのため、運用している企業やエンジニア側でユーザーがエラーになっている事に気づく事はできません。

大抵はフロント側では重要な処理は行なっていないため、ログを出力させることはあまりありませんが、ゲーム系であったりフロント側の処理がとても大事になってくるサービスなどでは、Ajaxなどの仕組みを使ってエラーになった場合にサーバー側へ通信を行なってログを残す。という方法が取られます。

(詳しくは解説しませんがimgタグをjsで自動生成させることでログを送る方法もあります。動画広告などミリ秒単位の処理スピードが求められる現場で一般的な手法です。)

出力先

大抵は以下のAmazonが運営するクラウド環境に保存しておくことが多いでしょう。(初心者の方は「こんなものがあるんだー」くらいに名前だけなんとなく覚えておけばいいです)

金融機関など企業によっては機密情報を他会社に起きたくないため、自分の会社でサーバーを立てて運用しています。

  • CloudWatch Log
  • S3 (Fluentdなどにより転送)

先ほどのように「ログファイルへの出力」といったディスクへの出力の場合には、どんどんファイルに書き込まれていきます。

毎月、何万人というユーザーが使うだけでも、1つのファイルが膨大になります。そんなコード量ともなればファイルを開くだけでも時間がかかりますし、その中から目的のログを見つけ出すのも大変です。

そのため、FWにも大抵そういった機能がありますが、「日付ごと」などでファイルそれぞれ作成してログを保存しておきます。

さらにログでディスクを圧迫(サーバーのハードディスク容量がどんどん圧迫されてしまいます)しないようログローテーション圧縮などの対応を実施しています。

ログローテーションとは、システムが残す記録(ログ)が際限なく増えることを防ぐために、一定の容量や期間ごとに古いログを削除したり新しいログで上書きすることを言います。大抵はそういった専用の運用ツールが無料や有料であるため、それを使用します。

ログレベル

ログは詳細であればあるほどいいですが、本番環境(ネット上にUPされて実際に動いているシステム)ともなれば、詳細に出力するほどログの出力コストが発生します。

ログを出力するということは「ログを出力させる処理に時間がかかる」ということです。何百万人も使うサービスともなればログを数個表示させるだけでも時間がかかってしまうということになります。

なので、PHP・MySQL部入門でもやってようにログレベルを分類し、環境によって適切にログを出し分けていきます。(デバッグ用ログは本番環境では出さずに開発環境でだけ出す様にするなど)

ログレベル ログレベル(日本語) 説明
ERROR エラー 本来発生せず調査が必要な事象について用いる。

サービス全体の提供が不可能な状態になる状態。DBに接続できないとか、syntaxエラーがある場合など。このレベルのログが出るとインフラ担当の人達にメールが飛んだりする。

WARN 警告 非推奨となったAPIの使用、APIの不適切な使用、エラーに近い事象など。即時調査が必要ではないが完全に正常とも異なる事象について用いる。

全体が止まるほどではないが、ユーザーの一部の処理が完了しないような場合が該当する。エラーページが表示されるような場合はこのレベル。

INFO 情報 本番環境で最低限必要なレベル。外部API呼び出しのパラメータ・復帰値など。具体例は後述。

言語自体のwarningや処理フロー上来ることがない値がかえって来た場合などはこのレベル。(例えばユーザーが不正にAPIを叩いてブロックされた時とか) 状況によってはerror相当なので場合によりけり。

DEBUG デバッグ 発行されるSQL文やデバッグ用ログ。
  • 開発環境ではDEBUG以上、ステージング環境ではDEBUGもしくはINFO以上、本番環境ではINFO以上を出力することが基本です。
  • ERRORレベルはログ出力する他、Slackやメール等で通知し、即時に異常を検知できるようにする。

上記のように何をどのログレベルで出力させるかは、現場によるので随時ヒアリングしましょう。

具体的な例

  • 例外がある言語の場合、予期せぬ例外が発生した場合、例外内容を出力する。 (ERRORレベル)
  • HTTPのリクエストパラメータは基本的に全て出力する。ただし、パスワードなどの機密情報や個人情報は出力しない。Ruby on Railsなどフレームワーク側でマスキング機能を提供してくれているものもあるが、そうでない場合はがんばる。 (INFOレベル)
  • 外部APIをコールする場合、障害時の原因切り分けを容易にするためエンドポイント・リクエスト内容・レスポンス内容を出力する。 (INFOレベル)
  • OSコマンドを実行する場合、障害時の原因切り分けを容易にするためコマンドをパラメータ含め全て出力、復帰値を出力する。 (INFOレベル)
  • 発行したSQLを全て出力する。 (DEBUGレベル)

上記は細かな実例ですが、この辺りは実際に実務で学んでいけばいいので「ふーん」くらいに思っておいてください。

ログフォーマット

ログをどういうフォーマットで出力するか?も決める必要があります。

  • 各ログには日時 (年月日日時分秒ミリ秒) を出力する。

また、「何をログとして残すのか?」はとても大事です。

日時もそうですし、セッションIDなどあるとセッションIDでログ内を検索すれば、同じセッションIDのユーザーのログを追うことができますね。

ユーザーごとの処理内容が追えなければログを残す意味がありませんからね。

ログに出力すべきでない情報について

ちょっと前にTwitter社でパスワードがログに平文で保存されていたというニュースが話題になりました。また、EU一般データ保護規則 (GDPR) も2018/05/25)から施行され、個人情報の保護の重要性は増しています。

以下はログに出すべきでない情報になります。

  • パスワードがログに残らないよう、パスワードはマスキングもしくはログ出力しない。
  • 個人情報もマスキングもしくはログに出力しないようにする。個人情報の定義は個人情報の定義について - 経済産業省を参照。なお、個人情報はSQL文含めて注意すること。

ログをどれだけの期間保存するか

ログローテーションのことになりますが、ログの重要度と書き込み頻度によって決めていきます。
例えば、
ログを一日ごとのログローテーションで記録し、30日間保存する場合、前日のログはすぐアクセスできるようにファイル圧縮して同じディレクトリにおき、それ以前はバックアップサーバーに送って30日前まで保存するようにするなどです。