運用を支えるためのログを出すにはどうするか? #jjug_ccc #ccc_m3
by
Masaya Saito
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
運用を支えるための ログを出すには どうするか? 斎藤将也
Slide 2
Slide 2 text
自己紹介 ● 無職 ● 前職ではアプリケーションエンジニア ○ SaaSとして動くSpring Bootで書かれたサーバーサイドの開発と運用 ● 仕事で使ってたツール ○ Spring Boot/Spring MVC ○ Slf4j + Logback ○ AWS, CloudWatch Logs ○ Docker, AWS ECS 2
Slide 3
Slide 3 text
アジェンダ ● なぜログを出力するか ● どこでログを出力するか ● どのようなログを出力するか ● ログでなにを出力するか ● ログ以外の手段 ● まとめ 3
Slide 4
Slide 4 text
なぜログを出力するのか 4
Slide 5
Slide 5 text
運用では色々なことが起きる 5
Slide 6
Slide 6 text
運用では色々なことが起きる 6 ユーザからの問い合わせ エラー/バグ パフォーマンスダウン
Slide 7
Slide 7 text
運用では色々なことが起きる ● 色々なことが起きる ○ バグ ○ 問い合わせ ○ パフォーマンス劣化 ● 障害は必ず起きる ○ 100%の可用性はありえない ● でも出来る限りサービスを提供したい ○ 障害の調査時間・復旧時間を短くして機会損失を減らす 7
Slide 8
Slide 8 text
じゃあどうするか ● 高い信頼性を持つアプリケーションを作る ● モニタリングで調査・復旧時間を短くする 8
Slide 9
Slide 9 text
じゃあどうするか ● 高い信頼性を持つアプリケーションを作る ● モニタリングで調査・復旧時間を短くする 今日の話 ● 調査・復旧時間を減らすためにログにどんな工夫をするべきか 9
Slide 10
Slide 10 text
そもそもログってなんだっけ ● 「いつ」を示すタイムスタンプを持つ ● 「その時どういう動作を行ったか」を示す ○ 「17時、決済サービスとの通信は正常に終了した」 ○ 「18時、決済サービスがダウンしている」 ● 当時の動作からシステムがどういう状態だったかが分かる 10
Slide 11
Slide 11 text
どんな場面でログを読むのか ● 障害発生時 ● ユーザサポート ● 開発・テスト などなど 11
Slide 12
Slide 12 text
ログを出力するのは誰かに読んでもらうため ● ログを読む人はシステムによって様々 ○ 自分以外の開発者 ○ 運用してる人 ○ ユーザサポート ○ 半年後の自分 ● 意図の伝わるログを出力しよう 12
Slide 13
Slide 13 text
どんなログがいいか? ● 5W1Hがわかるログが良い ○ 「いつ」「だれが」「なぜ」「何が起きて」「何をすれば良いのか」 ○ 特にエラーログ ● アプリケーションの特性・要件・コンテキストに合わせて トラブルシューティングなどで必要なログを書こう ○ 「ユーザID 〇〇のユーザの注文を〇〇 Payで決済した」 ○ 「ユーザがログインに失敗した」 13
Slide 14
Slide 14 text
運用を支えるログとは ● 「運用を支える」 ○ 復旧時間や調査時間を出来るだけ短くする ● ログ ○ ある時点でのシステムの状態 運用を支えるログとは ➔ システムで起きた問題や状況を 運用している人間が 調査に掛かる時間を短くするログ 14
Slide 15
Slide 15 text
どこでログを出力するか 15
Slide 16
Slide 16 text
関数の最初でログを出力しない ● やりがち ● どうしてもほしいならDEBUGレベルで ● 関数の最後に、どういう結果になったかをログに吐く方がいい ○ 最初に吐くより情報量が多い ○ 関数の最初: 「ユーザを作成しようとしています。」 ○ 関数の最後:「○○というユーザIDのユーザを作成しました」 ● AOPで出力するログはオススメしない ○ ログ量が多くなってノイズになりがち ○ 業務的な情報がないログが出がち ○ 個人情報がログに出てしまったりも・・・ 16
Slide 17
Slide 17 text
関数の最初でログを出力しない おすすめしない例 17
Slide 18
Slide 18 text
関数の最初でログを出力しない おすすめしない例 18
Slide 19
Slide 19 text
関数の最初でログを出力しない まだマシ例 19
Slide 20
Slide 20 text
関数の最初でログを出力しない 良い例 20
Slide 21
Slide 21 text
AOPで出力するログって? ● Aspect指向プログラミング(AOP)で出力するログ ○ サンプルでよく見るやつ ● 出すなら何か見たいものだけに絞る必要がある 21
Slide 22
Slide 22 text
ログは多すぎてはいけない ● Ex. ○ 繰り返し回数の多いループ内でのログ ○ そもそも単純に吐いてるログが多い ● ログが多いと読むのに時間がかかる ● ディスクを食いつぶしたり・・・ ○ 別の障害に繋がる可能性も・・・? ● ログを保持するコストに跳ね返る 22
Slide 23
Slide 23 text
システム間通信の結果はログに なぜ ● システム間通信をする部分には障害が起きやすい ● どういう依存があるかもログから分かる ● 外部システム起因の障害の切り分けに使える 23
Slide 24
Slide 24 text
システム間通信の結果はログに ● INFOレベルで出力する ● 次のような内容が書かれていると良い ○ どういうAPIを呼び出したか? ○ どういうステータスコードが帰ってきたか? ○ どの程度処理時間がかかったか? 24
Slide 25
Slide 25 text
システム間通信の結果はログに ● 通信ライブラリのインターセプタなどで書く ○ Ex. okhttp-logging-interceptor 25
Slide 26
Slide 26 text
システム間通信の結果はログに ● 通信ライブラリのインターセプタなどで書く ○ Ex. okhttp-logging-interceptor ● 通信結果の記録にリクエストIDを出力しておくと便利 ○ リクエストIDについては後述 ○ どう便利なのかについても後述 26
Slide 27
Slide 27 text
通信の結果をログに吐くだけでいい? 通信を行ったビジネスロジック側にもログは必要 なぜ ● 通信ライブラリのログ出力部分では ERRORなのかINFOなのか判断できない ● ビジネスロジック側ならもっと情報が出力できる ○ 「○○というユーザのポイントの発行に失敗しました」 27
Slide 28
Slide 28 text
どのようなログを出力するか 28
Slide 29
Slide 29 text
ログレベルを使い分ける ログレベル周りでありがちなこと ● ログレベルと出力している内容が合ってない ○ すぐに対応が必要なログも見逃してしまう ○ 障害時の初期対応はエラーログだけ見たいのに・・・ ● フレームワークのDEBUGログが出ている 本当は状況に合わせて必要なログを読みたい 29
Slide 30
Slide 30 text
ログレベルを使い分ける ● 適切にログレベルを選ぶ ● 基本出力するログレベルはINFOレベル以上にする ○ DEBUG以下はフレームワークのログとかも出てしまう ● INFOで出力する例 ○ ビジネスロジックで発生したイベントの結果 ○ 外部通信の結果 ○ バッチ処理の周期的なサマリ ■ 「何件処理しました。そのうち何件は失敗しました」 ○ など 30
Slide 31
Slide 31 text
ログレベルを使い分ける ● ERROR ○ 運用の継続が困難な問題が発生した時 ○ 人の介入が必要な場合は出力する ■ Ex. データベースの名前解決に失敗 ● WARN ○ 一時的な問題が発生した時や予期せぬ状態 ■ Ex. リトライ可能な問題, 一時的なキャパシティ不足 ● INFO ○ 基本的なログのレベル ● DEBUG ○ デバッグの為に使われるログ 31
Slide 32
Slide 32 text
構造化されたログで出力する どうやるか ● JSONでログを出力しよう なぜ ● 次の用途で使いやすい ○ 分析 ○ アラート ○ 検索 32
Slide 33
Slide 33 text
構造化されたログで出力する ● JSONなど解析しやすい形でログを出力しよう ● JSONならネイティブにサポートされているサービス・ツールがある ○ CloudWatch Logsなど ● logback(slf4jの実装)でJSON出力するなら ○ logback-ext-json-classic ○ logstash/logstash-logback-encoder 33
Slide 34
Slide 34 text
構造化されたログで出力する ● JSONで出力したときの例 ○ logstash-logback-encoderを使用 34
Slide 35
Slide 35 text
JSONでログ出力すると何が嬉しいのか ● JSONのフィールドで検索できる 例: ● エラーログを検索 ● 特定のAPIのログだけ検索 ● 1トランザクションで発生したログを検索 ○ リクエストIDを使う(後述) ● 500エラーのトランザクションだけ検索 35
Slide 36
Slide 36 text
ログでなにを出力するか 36
Slide 37
Slide 37 text
ログを読もう なぜ ● 運用していると必ず読む機会が発生する ● ログを見直す機会になる ○ 分かりにくいログを直すチャンス ○ 足りないログを足すチャンス 37
Slide 38
Slide 38 text
ログを読もう ● 障害が起きた時はもちろん ● 開発中に問題が起きた時も ● テストが落ちた時も ○ デバッガを出来るだけ使わない ● ログから問題の原因の特定が出来ないなら ログを直すことを考えよう 38
Slide 39
Slide 39 text
ログにコンテキストを付与する コンテキストを付与すると ● JSONログにその時の状況(コンテキスト)を表すフィールドが追加される なぜ ● その時どういう状況であったかを把握しやすくするため ○ 「Who」や「Why」を追加する 39
Slide 40
Slide 40 text
ログにコンテキストを付与する ● どうやってコンテキストを付与するか ○ MDC(Mapped Diagnostic Context) ■ 診断コンテキスト from logback ○ logstash-logback-encoder(今回は話しません) ● 付与する内容例 ○ ユーザID ○ リクエストされたパス ○ リクエストID(後述) 40
Slide 41
Slide 41 text
MDCの使い方 ● 処理をtry-finallyで囲む形 put/removeする ● 必ずfinallyでremove 41
Slide 42
Slide 42 text
ログにコンテキストを付与する ● 例: MDCInsertingServletFilter ○ リクエストされたURL/URIやUserAgentなどが付与される 42
Slide 43
Slide 43 text
ログにコンテキストを付与する ● 例: MDCInsertingServletFilter ○ リクエストされたURL/URIやUserAgentなどが付与される 43 一部省略してます 追加される 追加される
Slide 44
Slide 44 text
トランザクションに紐づくIDをログに付与する どうやるか ● 1トランザクションに紐づくID(リクエストID)をログに紐付ける ● APIのレスポンスヘッダにも付与すると良い なぜ ● 付与したIDを使ってログを検索すれば1トランザクションの処理を追える ※AWS SDKでも使われている 44
Slide 45
Slide 45 text
トランザクションに紐づくIDをログに付与する ● 例: ServletFilterでMDCにリクエストIDを付与する ○ このFilterを最初に適用する 45
Slide 46
Slide 46 text
トランザクションに紐づくIDをログに付与する ● 例: ServletFilterでMDCにリクエストIDを付与する ○ このFilterを最初に適用する 46 追加される
Slide 47
Slide 47 text
リクエストIDをログに付与すると何が嬉しい? 47 1回のやりとりに リクエストIDを割当 クライアント サーバ
Slide 48
Slide 48 text
リクエストIDをログに付与すると何が嬉しい? 48 1回のやりとりに リクエストIDを割当 リクエストIDを レスポンスヘッダに クライアント サーバ
Slide 49
Slide 49 text
リクエストIDをログに付与すると何が嬉しい? 49 ログ
Slide 50
Slide 50 text
リクエストIDをログに付与すると何が嬉しい? 50
Slide 51
Slide 51 text
リクエストIDをログに付与すると何が嬉しい? 51
Slide 52
Slide 52 text
リクエストIDをログに付与すると何が嬉しい? 52 リクエストIDはHoge
Slide 53
Slide 53 text
リクエストIDをログに付与すると何が嬉しい? 53 リクエストIDはHoge リクエストIDはHoge
Slide 54
Slide 54 text
リクエストIDをログに付与すると何が嬉しい? 54 大量のログがあっても 特定トランザクションのログだけ調べられる
Slide 55
Slide 55 text
問題の原因や回復手段を書く ● ログ出力の良くない例 ○ 「予期せぬエラーが発生しました」 ○ 「エラーが発生しました」 ● どうすればよいのか対処しやすい 55
Slide 56
Slide 56 text
良くないログを直してみる 予期せぬエラーが発生した IOエラーが発生した IOエラーが発生し、決済に失敗した ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした。○○の接続先情報を確認してください 56
Slide 57
Slide 57 text
良くないログを直してみる 予期せぬエラーが発生した IOエラーが発生した IOエラーが発生し、決済に失敗した ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした。○○の接続先情報を確認してください 57
Slide 58
Slide 58 text
良くないログを直してみる 予期せぬエラーが発生した IOエラーが発生した IOエラーが発生し、決済に失敗した ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした。○○の接続先情報を確認してください 58
Slide 59
Slide 59 text
良くないログを直してみる 予期せぬエラーが発生した IOエラーが発生した IOエラーが発生し、決済に失敗した ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした。○○の接続先情報を確認してください 59
Slide 60
Slide 60 text
良くないログを直してみる 予期せぬエラーが発生した IOエラーが発生した IOエラーが発生し、決済に失敗した ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした。○○の接続先情報を確認してください 60
Slide 61
Slide 61 text
良くないログを直してみる 予期せぬエラーが発生した IOエラーが発生した IOエラーが発生し、決済に失敗した ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした ○○に通信を試みましたが接続に失敗したので 決済は行われませんでした。○○の接続先情報を確認してください 61
Slide 62
Slide 62 text
問題の原因や回復手段を書く ● Slf4jの引数の一番最後を例外にしておくと スタックトレースが出力される ○ SLF4JのFAQに書いてある 62
Slide 63
Slide 63 text
問題の原因や回復手段を書く ● Slf4jの引数の一番最後を例外にしておくと スタックトレースが出力される ○ SLF4JのFAQに書いてある 63
Slide 64
Slide 64 text
出力するログは自分の言葉で書こう 特にアプリケーションのドメインに関わる部分 64
Slide 65
Slide 65 text
出力するログは自分の言葉で書こう 特にアプリケーションのドメインに関わる部分 65
Slide 66
Slide 66 text
出力するログは自分の言葉で書こう ● アプリケーションのドメインに関わる単語でログを書こう ● 例外からメッセージが帰ってこない可能性がある ● スタックトレースが出力されれば十分 ○ 例外のメッセージも含まれている 66
Slide 67
Slide 67 text
長い・大きいログは出さない ● Ex. ○ 大きなHTTPリクエスト・レスポンス ○ ファイルの中身 ● 長い・大きいメッセ―ジは読むのが難しい ● ログの量やサイズはコストに跳ね返る 67
Slide 68
Slide 68 text
おわりに 68
Slide 69
Slide 69 text
ログ以外の手段 ● 運用を支えるための手段はログだけではない ○ メトリクス・分散トレーシングもある ● 耐障害性の高いアプリケーションを作ろう 69
Slide 70
Slide 70 text
まとめ ● 障害をゼロにすることは難しい ● 運用を支える手段は色々ある ● ログは調査時間を減らすためのツール ● より有益なログを出して「運用を支えよう」 ● もっとログのベストプラクティスが知りたいなら ○ Optimal Loggingをどうぞ 70