マイクロサービスをdatadogで分散トレースする

この記事は　[ZOZO #4 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/zozo) １7日目の記事になります。

今回はマイクロサービスのdatadogの分散トレースに挑戦したのでそれについて記載しようと思います。

構成は以下になります。

|![Untitled Diagram.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645177/c4d608de-cd9a-dbaf-c420-5a9036df169b.jpeg)|
|:-:|



処理の流れは以下になります。

 1. Classic ASPからJava API【A】の登録APIを呼ぶ
 1. Java API【A】はDynamoDBに処理開始のレコードを登録する
 1. Java API【A】はKinesis Data Streams(以下、KDS）に登録情報を渡す
 1. Java API【B】はKDSから情報を取り出し、処理を行う
 1. Java API【B】はDynamoDBにレコードを処理完了に更新する

これにdatadogを仕込み、一連の処理をトレースします。

はじめにJava API【A】の登録APIでTraceを作成します。

```Java
public record Trace(TraceId traceId, DdCarrier ddCarrier) {
  public static Trace convert(String traceId) {
    Tracer tracer = GlobalTracer.get();
    Map<String, String> ddMap = new HashMap<>();
    tracer.inject(
        tracer.activeSpan().context(),
        Format.Builtin.TEXT_MAP_INJECT,
        new TextMapInjectAdapter(ddMap));
    return new Trace(new TraceId(traceId), new DdCarrier(ddMap));
  }
}
```

作成されたTraceをKDSに渡し、Java API【A】の登録APIの返却値として以下を返却します。

 - x-datadog-trace-id
 - x-datadog-sampling-priority
 - x-datadog-parent-id

Java API【B】はKDSを経由してdatadogのパラメータを受け取ります。
受け取ったパラメータを元にSpanを作成し、登録APIのtraceと紐付けます。
また、Java API【A】の参照APIもClassic ASPを経由して同じ様に対応します。

```Java
Tracer tracer = GlobalTracer.get();
SpanBuilder spanBuilder =
    new SpanBuilder()
        .generate(
            tracer,
            "処理API",
            traceId,
            datadogTraceId,
            datadogParentId,
            datadogSamplingPriority);
Span span = spanBuilder.start();
try (Scope scope = tracer.scopeManager().activate(span)) {
  // 処理
} catch (Exception e) {
  span.setTag(DDTags.ERROR_MSG, e.getMessage());
  span.setTag(DDTags.ERROR_STACK, Arrays.toString(e.getStackTrace()));
  throw e;
} finally {
  span.finish();
}
```

```Java
public class SpanBuilder {
  public Tracer.SpanBuilder generate(
      Tracer tracer,
      String operationName,
      String traceId,
      String datadogTraceId,
      String datadogParentId,
      String datadogSamplingPriority) {
    Tracer.SpanBuilder spanBuilder =
        tracer
            .buildSpan(operationName)
            .withTag("trace-id", traceId)
            .withTag("datadog-trace-id", datadogTraceId)
            .withTag("datadog-sampling-priority", datadogSamplingPriority)
            .withTag("datadog-parent-id", datadogParentId);
    Map<String, String> ddCarrier = new HashMap<>();
    ddCarrier.put("x-datadog-trace-id", datadogTraceId);
    ddCarrier.put("x-datadog-sampling-priority", datadogSamplingPriority);
    ddCarrier.put("x-datadog-parent-id", datadogParentId);
    SpanContext spanContext =
        tracer.extract(Format.Builtin.TEXT_MAP_EXTRACT, new TextMapExtractAdapter(ddCarrier));
    return spanBuilder.asChildOf(spanContext);
  }
}
```

これにより、以下のように一気通貫で処理をトレースすることができます。
処理の流れが追いやすくなり、エラーなどが発生した際も原因の特定がしやすくなりました。

|![スクリーンショット 2021-12-14 9.18.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645177/537dc2f0-952c-cb3a-9192-3b73daef502f.png)|
|:-:|


明日は @EnKUMA の記事になります。
読んでいただきありがとうございました。

参考 : [datadog 分散トレースのコンテキストの挿入と抽出](https://docs.datadoghq.com/ja/tracing/custom_instrumentation/java/#%E5%88%86%E6%95%A3%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B9%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AE%E6%8C%BF%E5%85%A5%E3%81%A8%E6%8A%BD%E5%87%BA)