$30 off During Our Annual Pro Sale. View Details »

TypescriptでのContextualな構造化ロギングと社内全体への導入

 TypescriptでのContextualな構造化ロギングと社内全体への導入

# TSKaigi
2024/05/11 トラック 2 LT 12:10~13:10
TypescriptでのContextualな構造化ロギングと社内全体への導入!

# 概要
Pino+AsyncLocalStorageによってStructured LoggingとContextual Loggingを同時に達成し、レバテックのサーバーに全体導入した話と、それによる効果や導入時に気をつけておけばよかったことをお話します。

Tech Leverages

May 11, 2024
Tweet

More Decks by Tech Leverages

Other Decks in Technology

Transcript

  1. | © Leverages inc. 2 今日は何の話? レバテックの裏側。 Typescriptでのマイクロサービス運用。 • どうロギングすれば運用しやすくなるか •

    どうTypescriptで実現するか • 導入してどうだったか を考えて、改善に取り組んだことのお話!
  2. | © Leverages inc. 3 • 所属 ◦ レバテック開発部/案件ドメインユニット • 経歴

    ◦ 2022年07月〜 レバレジーズ株式会社 ◦ バックエンドエンジニア ◦ 技術広報(テックブログと勉強会の運営) • 最近 ◦ ユーフォ3期が生きがい ◦ プレーリーカード作った => 瀬尾 光希(せお こうき) 自己紹介 これは自分で描いた卵かけご飯 まあまあ美味そう
  3. | © Leverages inc. 7 目次 1. ロギングのベストプラクティス 2. Typescript での実現

    3. レバテックシステムへの全体導入 4. 導入してうれしかったこと 5. 導入する前に決めておきたかったこと
  4. | © Leverages inc. 8 目次 1. ロギングのベストプラクティス 2. Typescript での実現

    3. レバテックシステムへの全体導入 4. 導入してうれしかったこと 5. 導入する前に決めておきたかったこと
  5. | © Leverages inc. 9 1. ライブラリを使おう! ◦ ログに出すべき情報を自動で出してくれるようなものがあります 2. 正しいログレベルをつけよう!

    ◦ 用途ごとに適切な設定をすべし(INFO, WARN, ERROR, …) 3. 集中管理できる場所に集めよう! ◦ DatadogやNewRelicなど 4. 構造化されたフォーマットでロギングしよう! ◦ ログは膨大な量になるので、人間より機械が読みやすいことの方が優先 ◦ DatadogやNewRelicでは、JSONは自動でパースされる 5. ログメッセージと共に Context を記録しよう! ◦ ログを読むときの目的を考えて、それ応じた関連情報を含めておきたい 6. 実行ごとに一意な識別子(Request ID)を含めよう! ◦ リクエストを同時に処理するとログが混在してどれがどれだかわからんなる… ロギングのベストプラクティス(受け売り) ロギングのベストプラクティス
  6. | © Leverages inc. 10 もともとレバテックのシステムは… ロギングのベストプラクティス 1. ライブラリを使おう! ◦ 2.

    正しいログレベルをつけよう! ◦ 3. 集中管理できる場所に集めよう! ◦ 4. 構造化されたフォーマットでロギングしよう! ◦ 5. ログメッセージと共に Context を記録しよう! ◦ 6. 実行ごとに一意な識別子(Request ID)を含めよう! ◦ 微妙… そらこんな顔なるわ
  7. | © Leverages inc. 12 目次 1. ロギングのベストプラクティス 2. Typescript での実現

    3. レバテックシステムへの全体導入 4. 導入してうれしかったこと 5. 導入する前に決めておきたかったこと
  8. | © Leverages inc. 13 • Node.js が提供する API • (Deno

    や Bun にも互換の API がある) • 非同期処理においてスレッドローカル変数 のようなものを実現  AsyncLocalStorage Typescriptでの実現 公式ドキュメントより引用 実行ごとにストレージを作り、 使いまわしたい情報を格納しておける!
  9. | © Leverages inc. 14 AsyncLocalStorage の簡単な使い方 Typescriptでの実現 run() 中で実行される非同期関数内では、 getStore()

    によっていつでも ”TestText” を取得できる Controller から Usecase を呼び出す際に run() でラップして RequestID を実現
  10. | © Leverages inc. 15 ロギングのライブラリ Typescriptでの実現 がおすすめ • JSON形式に構造化されたログ出力できる •

    hostname, UTC などをデフォルトで出力できる • 任意のオブジェクトをJSONに要素追加できる • 軽いらしい • プロパティを指定して個人情報などを伏せ字にすること もできる • ロギング時にトリガーしてJSONに要素追加する mixin 関数を設定できる → getStore() をロギングごとに仕込める
  11. | © Leverages inc. 16 目次 1. ロギングのベストプラクティス 2. Typescript での実現

    3. レバテックシステムへの全体導入 4. 導入してうれしかったこと 5. 導入する前に決めておきたかったこと
  12. | © Leverages inc. 17 前提: • システム ◦ ロギングでは、社内ライブラリとして log4js

    のラッパーが浸透していた ◦ 大半のシステムは gRPC で通信している(そうじゃないのもある) ◦ gRPCアプリの Controller は 秘伝の社内ライブラリ によって proto から自動生成されていた • 人 ◦ 全員にログを改善していこうという意識があるわけではなかった 
 どうすれば皆が楽に恩恵を受けられるか: • log4js ラッパーからの Migration が楽であること • 特に使い方を意識せずとも、移行するだけで Contextual な構造化ロギングが実現されること 導入に向けて レバテックシステムへの全体導入
  13. | © Leverages inc. 18 1. 社内ライブラリとして Pino のラッパーを新調 ◦ 共通設定の初期化のみ行う(ログレベル,

    時間のフォーマットなど) 2. 秘伝ライブラリに AsyncLocalStorage の処理を追加 ◦ 自動生成される Controller が Usecase を呼び出す部分をラップする感じ ◦ Node.js v12 以降で使用する前提なので許される ◦ 皆が苦労せず恩恵を受けるには、今までの自動生成に紛れ込ますのがよいと考えた 3. 秘伝ライブラリで Controller と一緒に1のラッパーを生成 ◦ mixin 関数で、生成した AsyncLocalStorage から RequestID の値を要素に追加するラッパー ◦ 自動生成されるコードへの新たな依存(暗黙知)を作らないように気をつけた結果でした ◦ gRPC と秘伝ライブラリの利用者はここから import すれば自動で RequestID が付与される 結論どうしたか レバテックシステムへの全体導入
  14. | © Leverages inc. 19 目次 1. ロギングのベストプラクティス 2. Typescript での実現

    3. レバテックシステムへの全体導入 4. 導入してうれしかったこと 5. 導入する前に決めておきたかったこと
  15. | © Leverages inc. 20 ログが自動でパースされる 導入してうれしかったこと Request ID Massage Name

    パースを頑張らなくても(重要) ログの要素がDatadog側で全てパースされている状態 全てのログにRequest IDが含まれていて めっちゃ検索しやすい
  16. | © Leverages inc. 22 俺自身が best practice になる事だ めでたしめでたし 1.

    ライブラリを使おう! ◦ 2. 正しいログレベルをつけよう! ◦ 3. 集中管理できる場所に集めよう! ◦ 4. 構造化されたフォーマットでロギングしよう! ◦ 5. ログメッセージと共に Context を記録しよう! ◦ 6. 実行ごとに一意な識別子(Request ID)を含めよう! ◦ ログおじいさんとログおばあさん
  17. | © Leverages inc. 23 目次 1. ロギングのベストプラクティス 2. Typescript での実現

    3. レバテックシステムへの全体導入 4. 導入してうれしかったこと 5. 導入する前に決めておきたかったこと