> Source: https://www.ymotongpoo.com/works/otel-specs-ja/semconv/general/recording-errors/


# エラーの記録

**ステータス**: [Development][DocumentStatus]。

この文書は、セマンティック規約と計装の作成者に向けて、スパンとメトリクス上でエラーを記録する方法についての推奨事項を提供します。

個々のセマンティック規約は、追加のガイダンスを提供することが推奨されます。

## 何がエラーを構成するか

次のいずれかが真である場合、操作は失敗したものとして扱うべきです（SHOULD）。

- 計装対象の操作（API、コードブロック、その他の計装対象の単位）によって例外がスローされた場合。
- 計装対象の操作が別の方法（例えばエラーコード経由など）でエラーを返した場合。

  ドメイン固有のステータスコードを定義するセマンティック規約は、汎用的な計装がどのステータスコードをエラーとして報告すべきかを指定すべきです（SHOULD）。

> [!NOTE]
>
> ステータスコードをエラーとして分類するかどうかは、文脈に依存します。
> 例えば、アプリケーションがそのリソースが利用可能であることを期待していた場合、HTTP 404「Not Found」ステータスコードはエラーを示します。しかし、アプリケーションが単にそのリソースが存在するかどうかを確認しているだけの場合、これはエラーではありません。
>
> 特定のリクエストについて追加のコンテキストを持つ計装は、そのコンテキストを使って、スパンのステータスをより正確に設定してもかまいません（MAY）。

再試行または処理されたエラー（操作が正常に完了できるようにするもの）は、その操作を記述するスパンやメトリクスに記録すべきではありません（SHOULD NOT）。

## スパンにおけるエラーの記録

計装対象の操作がエラーなく終了した場合、[スパンステータスコード][SpanStatus]は未設定のままにしなければなりません（MUST）。

操作がエラーで終了した場合、計装は次のようにします。

- スパンステータスコードを `Error` に設定すべきです（SHOULD）。
- [`error.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/error/#error-type)属性を設定すべきです（SHOULD）。
- エラーについて機密な詳細を含まないと想定される追加情報があり、かつ[スパンステータスの説明][SpanStatus]の定義に沿う場合は、スパンステータスの説明を設定すべきです（SHOULD）。

  スパンステータスの説明の中で、ステータスコードや `error.type` を重複させることは推奨されません（NOT RECOMMENDED）。

  操作が例外により失敗した場合、スパンステータスの説明はその例外メッセージに設定すべきです（SHOULD）。

例外の詳細を捉える方法については、[例外の記録](#例外の記録)を参照してください。

## メトリクスにおけるエラーの記録

操作に関するセマンティック規約は、通常、操作の継続時間のヒストグラムメトリクスを定義します。このメトリクスは `error.type` 属性を含むべきです（SHOULD）。これにより、ユーザーはスループットとエラー率を導出できます。

正常に完了した操作は、`error.type` 属性を含むべきではありません（SHOULD NOT）。これにより、ユーザーはエラーを除外してフィルタリングできます。

セマンティック規約は、それが適用可能な場合、他のメトリクスにも `error.type` を含めるべきです（SHOULD）。例えば、`messaging.client.sent.messages` メトリクスはメッセージのスループットを測定し（1回のメッセージング操作で複数のメッセージの送信を伴うことがあります）、`error.type` を含みます。

操作のステータスに応じて2つ（以上）のメトリクスを報告するのではなく、成功と失敗を含む1つのメトリクスを報告することが推奨されます（RECOMMENDED）。

計装は、スパンとメトリクスの両方が報告される場合、`error.type` がそれらの間で一貫して適用されることを保証すべきです（SHOULD）。単一の操作についてのスパンと対応するメトリクスは、その操作が失敗した場合は同じ `error.type` の値を持つべきであり（SHOULD）、成功した場合はそれを含めるべきではありません（SHOULD NOT）。

## 例外の記録

計装対象の操作が例外によって失敗した場合:

- 計装は、この例外を[ログレコード](/works/otel-specs-ja/semconv/exceptions/exceptions-logs/)として記録すべきです（SHOULD）。
- 計装は、これらのシグナル上で例外の詳細を捉える際、[スパンにおけるエラーの記録](#スパンにおけるエラーの記録)と[メトリクスにおけるエラーの記録](#メトリクスにおけるエラーの記録)に従うべきです（SHOULD）。

同じ例外を2回以上記録することは推奨されません（NOT RECOMMENDED）。計装対象のライブラリによって処理された例外を記録することは推奨されません（NOT RECOMMENDED）。

例えば、次のコードスニペットでは、`ResourceAlreadyExistsException` は処理されており、対応するネイティブの計装はこれを記録すべきではありません。呼び出し元に伝播する例外は、一度だけ記録（またはログ出力）すべきです。

```java
public boolean createIfNotExists(String resourceId) throws IOException {
  Span span = startSpan();
  long startTime = System.nanoTime();
  try {
    create(resourceId);

    recordMetric("acme.resource.create.duration", System.nanoTime() - startTime);

    return true;
  } catch (ResourceAlreadyExistsException e) {
    // we do not set span status to error and the "error.type" attribute
    // as the exception is not an error,
    // but we still log and set attributes that capture additional details
    logger.withEventName("acme.resource.create.exception")
      .withAttribute("acme.resource.create.status", "already_exists")
      .withException(e)
      .debug();

    span.setAttribute(AttributeKey.stringKey("acme.resource.create.status"), "already_exists");

    recordMetric("acme.resource.create.duration", System.nanoTime() - startTime);

    return false;
  } catch (IOException e) {
    // this exception is expected to be handled by the caller
    // and could be a transient error
    logger.withEventName("acme.resource.create.exception")
      .withException(e)
      .warn();

    String errorType = e.getClass().getCanonicalName();

    span.setAttribute(AttributeKey.stringKey("error.type"), errorType);
    span.setStatus(StatusCode.ERROR, e.getMessage());

    recordMetric("acme.resource.create.duration", System.nanoTime() - startTime,
                 AttributeKey.stringKey("error.type"), errorType);
    throw e;
  }
}
```

[DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status
[SpanStatus]: /works/otel-specs-ja/spec/trace/api/#set-status

