Top takeaways
本記事は Dude, Where’s My Error? の抄訳した記事となります。
開発に慣れている言語によっては、エラーとは何か、例外の構成要素とそれをどのように処理するかについて、一定の考えがあるかもしれません。 たとえば、Goには例外がありません。これは、プログラマーが通常のエラーを例外として分類するのを防ぐためです。 一方、JavaやPythonなどの言語では、例外をスローしたりキャッチしたりするためのサポートが組み込まれています。
エラーや例外とは何か、またそれらをどのように処理するかについてさまざまな言語で意見が一致していないところから始めます。これらの言語で書かれたマイクロサービス全体で標準化されたテレメトリーとエラー報告が必要な場合、何を使用しますか?以下などに対処するためのツールである、OpenTelemetryです。
- バックエンドでエラーがどのように視覚化されるかは、予想した場所や外観と異なる場合がある
- スパンの種類がエラー報告にどのように影響するか
- スパンとログによって報告されるエラー
OpenTelemetryとは?
OpenTelemetry(略してOTel)は、クラウドネイティブ・コンピューティング・ ファウンデーション(CNCF)のプロジェクトです。これは、テレメトリーデータの計装、生成、収集、エクスポートのためのオープンソースのベンダーニュートラルなオブザーバビリティフレームワークです。
エラーと例外
OTelがエラーと例外をどのように処理するかを説明する前に、それらが何であるか、それぞれがどのように違うのかを明確にしましょう。これらの用語の定義にはさまざまなバリエーションがありますが、このブログ記事では以下の定義を使用します。
エラーとは、プログラムの実行を妨げる予期しない問題です。 例としては、セミコロンの欠落やインデントの誤りなどの構文エラーや、ロジックのエラーによって生じる実行時エラーなどがあります。
例外とは、プログラムの通常の流れを中断するランタイムエラーの一種です。 ゼロによる除算や無効なメモリアドレスへのアクセスなどが例に挙げられます。
PythonやJavaScriptなどの一部の言語では、エラーと例外を同義語として扱います。PHPやJavaなどはそうではありません。 エラーと例外の区別を理解することは、アプリケーション障害の処理と回復について、より微妙な戦略を採用できるようになるため、効果的なエラー処理には非常に重要です。
OTelでのエラー処理
では、OTelは言語間のこうした概念的な違いにどのように対処するのでしょうか?ここで、specification(略して「spec」)が登場します。 この仕様は、プロジェクトのさまざまな部分に取り組む開発者にブループリントを提供し、すべての言語で実装を標準化します。
言語APIとSDKは仕様の実装であるため、仕様でカバーされていないものを実装することに対する一般的なルールがあります。 これは、プロジェクトへの貢献を整理するのに役立つ指針になります。実際には、例外がいくつかあります。たとえば、言語は、仕様への追加の一環として新しい機能のプロトタイプを作成する場合がありますが、対応する言語が追加される前に、その機能が (通常はアルファ版または実験版として)公開される場合があります。
もう1つの例外は、言語が仕様から逸脱することを決定した場合です。 一般的には推奨されませんが、言語に特有の強い理由により、異なる操作を行う必要がある場合もあります。 このように、この仕様では、各言語ができる限り慣用的に機能を実装できるようある程度の柔軟性が考慮されています。 たとえば、ほとんどの言語ではRecordException
が実装されていますが、Goでは同じことを行うRecordError
が実装されています。
この仕様のコンプライアンスマトリックスはすべての言語にわたって表示できますが、最新の情報は個別の言語リポジトリを確認することで入手できます。これで、OTelでエラーを処理する方法を理解するための出発点ができました。まずは、次の方法でエラーを報告する方法から始めます。
- スパン
- ログ
スパンのエラー
OTelでは、スパンは分散トレーシングの構成要素であり、分散システム内の個々の作業単位を表します。 スパンは、コンテキストを通じて相互に、またトレースと関連しています。 簡単に言えば、コンテキストは、データのパックを統一されたトレースに変える接着剤です。 コンテキスト伝搬を使用すると、複数のシステム間で情報を受け渡し、それらを結び付けることができます。 トレースは、メタデータとスパンイベントを通じて、アプリケーションに関するさまざまな情報を提供します。
メタデータによるスパンの強化
OTelを使用すると、キーと値のペアの形式でメタデータ(プロパティ)を使用してスパンを拡張できます。 ユーザーID、リクエスト、環境変数などの関連情報をスパンに添付することで、エラーを取り巻く状況をより深く洞察し、その根本原因を迅速に特定できます。 このメタデータを豊富に含むエラー処理アプローチにより、問題の診断と解決に必要な時間と労力が大幅に削減され、最終的にはアプリケーションの信頼性と保守性が向上します。
スパンにはスパンの種類フィールドもあります。これにより、開発者がエラーをトラブルシューティングするのに役立つ追加のメタデータが得られます。 OTelはいくつかのスパンの種類を定義しており、それぞれがエラー報告に対して独自の意味を持ちます。
- クライアント:発信同期リモート呼び出しの場合、発信HTTPリクエストやDB呼び出しなど
- サーバー:着信同期リモート呼び出しの場合、着信HTTPリクエストやリモート手順呼び出しなど
- インターナル:プロセス境界を越えない操作の場合、関数呼び出しをインストゥルメント化するなど
- プロデューサー:後で非同期に処理されるジョブを作成する場合、ジョブキューに挿入されるジョブなど
- コンシューマー:プロデューサーによって作成されたジョブを処理する場合、プロデューサーのスパンが終了してからかなり後に開始される可能性がある
スパンの種類は、使用するインストゥルメンテーションライブラリによって自動的に決定されます。
スパンは、スパンステータスを使用してさらに拡張できます。 デフォルトでは、特に指定がない限り、スパンステータスはUnset
としてマークされます。 結果のスパンにエラーが示されている場合はスパンステータスをError
としてマークし、結果のスパンにエラーがない場合はOk
をマークできます。
スパンイベントによるスパンの強化
スパンイベントは、スパン内に埋め込まれた構造化されたログメッセージです。 スパンイベントは、スパンに関するわかりやすい情報を提供することで、スパンの強化に役立ちます。 スパンイベントは独自の属性を持つこともできます。 New Relicは、SpanEvent
と呼ばれる独自のデータ型としてスパンイベントを合成します。
スパンのステータスがError
に設定されると、スパンイベントが自動的に作成され、スパンの結果のエラーメッセージとスタックトレースがそのスパンのイベントとしてキャプチャされます。 属性を追加することで、このスパンエラーをさらに強化できます。
先ほど、RecordException
というメソッドについて説明しました。 仕様によれば(当社独自のものを強調)、「言語が例外を使用する場合は、例外の記録を容易にするために、言語はRecordExceptionメソッドを提供する必要があります。…メソッドのシグネチャは言語ごとに決定され、必要に応じてオーバーロードできます。」
Goは例外の「従来の」概念をサポートしていないため、代わりにRecordErrorをサポートしています。これは本質的に同じことを慣用的に行います。 ただし、自動的には設定されないため、ステータスをError
に設定する必要がある場合は、追加の呼び出しを行う必要があります。同様に、RecordException
を使用すると、スパンステータスをError
に設定せずにスパンイベントを記録できます。つまり、これを使用してスパンに関する追加データを記録できます。
スパン例外の発生時にスパンステータスが自動的にError
に設定されることから切り離すことで、Ok
またはUnset
のステータスを持つ例外イベントを発生させることができるユースケースをサポートできます。 これにより、インストゥルメンテーションの作成者は最大限の柔軟性を得ることができます。
ログに示されるエラー
OTelでは、ログはサービスまたは他のコンポーネントによって発行される、構造化されたタイムスタンプ付きのメッセージです。 最近OTelにログが追加されたことで、エラーを報告する別の方法がさらに増えました。 従来、ログには、出力されるメッセージの種類を表すさまざまな重大度レベル(DEBUG
、INFO
、WARNING
、ERROR
、CRITICAL
など)がありました。
OTelでは、トレースへのログの相関が可能で、トレースコンテキスト相関を介して、ログメッセージをトレース内のスパンに関連付けることができます。 したがって、ログレベルがERROR
またはCRITICAL
のログメッセージを検索すると、関連するトレースを取得することで、そのエラーの原因に関する詳細情報が得られます。
ログにエラーを記録するには、exception.type
またはexception.message
のいずれかが必要ですが、exception.stacktrace
が推奨されます。 ログ例外のセマンティック規約の詳細については、こちらをご覧ください。
エラーをキャプチャするにはログまたはスパン?
ここまでのことを踏まえると、エラーをキャプチャするにはスパンとログのどちらの信号を使用すべきか迷うことでしょう。答えは「状況次第」です。 おそらくあなたのチームでは主にトレースを使用しているか、主にログを使用しているでしょう。
スパンはエラーを捕捉するのに最適です。操作でエラーが発生した場合、スパンをエラーとしてマークすると目立つため、見つけやすくなります。 一方、トレースのフィルタリングやテールサンプリングを行っておらず、システムが1分間に数千のスパンを生成している場合は、頻繁には発生していないものの、それでも処理する必要があるエラーを見逃す可能性があります。
ログではなくスパンイベントを使用する場合はどうでしょうか?繰り返しますが、これは状況次第です。スパンステータスがError
に設定されると、例外メッセージ(およびキャプチャする必要があるその他のメタデータ)を含むスパンイベントが自動的に作成されるため、スパンイベントを使用すると便利な場合があります。
幸いなことに、 New Relicではログとトレースの両方をレンダリングし、データをより見つけやすくするためのクエリ言語を提供し、ログとトレースの相関関係をサポートしています。
New Relicでのエラーの可視化
OTelはシステムから出力された生のテレメトリーデータを提供しますが、データの視覚化や解釈は提供しません。 これはオブザーバビリティバックエンドによって行われます。 OTelはベンダーニュートラルであるため、アプリケーションを再インストゥルメントすることなく、出力された同じ情報を異なるバックエンドで視覚化し、解釈できます。
弊社のエージェントのいずれかを使用してアプリケーションを監視しており、最近OTelに移行した場合は、APMエージェントでキャプチャされた同じエラーと比較して、OTelエラーが期待どおりに表現されない場合があることに気づくかもしれません。 これは、ベンダーがエラーをモデル化してきた方法とは異なる方法で、OTelがエラーをモデル化しているからです。
一例として、OTelのスパンの種類の概念は、OTelエラーの表示方法に影響を与える可能性があります。 インスタンスの場合、例外が1つあるトレースが内部スパン上にあり、ステータスがError
に設定されている場合、トレースにはエラーのマークが付けられますが、アプリのエラー率にはカウントされない可能性があります。 これは、 New Relic 、エントリーポイントスパン(サーバースパン)とコンシューマースパンでのエラーのみをエラー率にカウントすべきであるという意見を持っているためです。 詳細はこちらをご覧ください。
New Relicでは、send_requests
というトレースグループをクリックすると、エラーのあるスパンが含まれるトレースを確認できます。
エラートレースの1つを選択すると、関係するすべてのスパンのトレースウォーターフォールを表示できます。 do_roll
スパンにスパンイベントとして例外が含まれていることがわかります。 また、Attributes
をクリックして、追加したカスタムアトリビュートなど、関連するメタデータを表示することもできます。
スパンイベントをクリックすると、例外に関する詳細と、スパンイベントに追加したカスタムアトリビュートが表示されます。 この場合、ダミー属性の例を確認できます。 手動でスパンイベントを記録しているため、例外に加えて2番目のスパンイベントもキャプチャされます。
選択したトレースのLogs
をクリックすると、トレースから直接相関ログにアクセスできます。 ここでは、3つのログと、トレースのどの時点でそれらが生成されたかが表示されます。 チャートにはエラーログの数が表示されます。
まとめ
エラー処理は、ソフトウェア開発において困難ではありますが不可欠な側面であり、OTelはその複雑さを克服するための包括的なソリューションを提供します。 OTelの機能とNew Relicプラットフォーム機能を活用すると、アプリケーションの動作に関するより深いインサイトを得ることができ、問題のトラブルシューティングをより効果的に行うことができます。 今日の動的かつ要求の厳しい環境において、回復力、信頼性、パフォーマンスに優れたソフトウェアアプリケーションを構築および維持するための準備が整います。
The views expressed on this blog are those of the author and do not necessarily reflect the views of New Relic. Any solutions offered by the author are environment-specific and not part of the commercial solutions or support offered by New Relic. Please join us exclusively at the Explorers Hub (discuss.newrelic.com) for questions and support related to this blog post. This blog may contain links to content on third-party sites. By providing such links, New Relic does not adopt, guarantee, approve or endorse the information, views or products available on such sites.