OpenTelemetry形式の2つのスパンデータをNew Relicに投げ込んでみた

# はじめに
OpenTelemetryを使ってアプリケーションを計装する場合は、すでに用意されている言語ごとの[InstrumentationやSDK](https://opentelemetry.io/docs/instrumentation/)を使ってデータを転送することが一般的ですが、InstrumentationやSDKが使えない場合を想定して、APIで直接データを転送する方法を試してみました。

今回は２つのスパンデータを直接New RelicのOpentelemetry用のエンドポイントに送信して、２つのスパンがどのようにNew Relic上で可視化されるか確認しています。

# 検証内容
次の3ステップで検証しています。
- 1つ目のスパンをNew RelicのOpentelemetry用のエンドポイントに送信。
- 1つ目のスパンを親スパンとして、2つ目のスパンをNew RelicのOpentelemetry用のエンドポイントに送信。
- 2つのスパンがどのようにNew Relic上で可視化されるか確認。

## 1つ目のスパン

１つ目のスパンを作成します。

:::note warn
注意点
- app01はNew Relic上で表示されるアプリケーション名になります。
- traceIdは32桁の小文字英数字の16進数になります。1つ目と2つ目のスパンで同じTraceIdを使用することで、1つのトレースとして扱うことができます。
- spanIdは16桁の小文字英数字の16進数になります。スパンを特定するためのIDなので、1つ目と2つめのスパンで別のspanIdを指定します。
- startTimeUnixNanoとendTimeUnixNanoはスパンの開始時刻(unixナノ秒）になります。New Relicは48時間より前のデータは取り込むことができないので、echo $((`date +%s` * 1000000000))などでunixナノ秒を生成して設定してください。
:::

``` span1.json 
{"resourceSpans":[{
    "resource": {
      "attributes": [{
        "key":"service.name",
        "value":{"stringValue":"app01"}
      }]
    },
    "scopeSpans": [{
      "scope": {},
      "spans": [{
        "traceId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "spanId": "1111111111111111",
        "name": "app1",
        "startTimeUnixNano": "1685953468000000000",
        "endTimeUnixNano": "1685953469000000000"
      }]
    }]
  }]}
```

下記のcurlコマンドを実行します。

```
curl -i https://otlp.nr-data.net:4317/v1/traces -X POST \
-H "Content-Type: application/json" \
-H "api-key: お使いの環境のAPI KEY(INGEST-LICENSE)を入れてください" \
-d @span1.json
```

## ２つ目のスパン
2つ目のスパンを作成します。

:::note warn
注意点
- app02はNew Relic上で表示されるアプリケーション名になります。
- traceIdは32桁の小文字英数字の16進数になります。1つ目と2つ目のスパンで同じTraceIdを使用することで、1つのトレースとして扱うことができます。
- spanIdは16桁の小文字英数字の16進数になります。スパンを特定するためのIDなので、1つ目と2つめのスパンで別のspanIdを指定します。
- 2つ目のスパンは親スパンを指定する必要があるため、parentSpanIdが必要となります。こちらは1つ目のスパンのspanIdを設定してください。
- startTimeUnixNanoとendTimeUnixNanoはスパンの開始時刻(unixナノ秒）になります。New Relicは48時間より前のデータは取り込むことができないので、echo $((`date +%s` * 1000000000))などでunixナノ秒を生成して設定してください。
:::

``` span2.json 
{"resourceSpans":[{
    "resource": {
      "attributes": [{
        "key":"service.name",
        "value":{"stringValue":"app02"}
      }]
    },
    "scopeSpans": [{
      "scope": {},
      "spans": [{
        "traceId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "spanId": "2222222222222222",
        "parentSpanId": "1111111111111111",
        "name": "app2",
        "startTimeUnixNano": "1685953469000000000",
        "endTimeUnixNano": "1685953470000000000"
      }]
    }]
  }]}
```

下記のcurlコマンドを実行します。

```
curl -i https://otlp.nr-data.net:4317/v1/traces -X POST \
-H "Content-Type: application/json" \
-H "api-key: お使いの環境のAPI KEY(INGEST-LICENSE)を入れてください" \
-d @span2.json
```

## New Relic上で２つのスパンがつながっていることを確認

Query Your Data > Data explorer > EventsでSpanイベントを選択して、データがNew Relicに送付されていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227243/7e752b1b-b3d7-ec94-6387-72634f36465a.png)

APM&Serviceからapp01を選んで、Distributed Tracingを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227243/82dde626-c843-de92-244c-ef770515bc86.png)


Trace groupsからTraceを選ぶと、app01とapp02が繋がったマップが表示され、２つのスパンが繋がって表示されていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227243/23aff05b-747e-a442-7f82-83252dc6b72a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227243/7b3a966c-8ede-1451-80b5-cba341acac93.png)



# まとめ
2つのスパンをAPI形式でNew Relicに送信して、New Relic上で繋がって見えることを確認できました。スパンや分散トレースの仕組みの理解にも役立ちますのでぜひ試してみてください。

# 参考資料

https://opentelemetry.io/docs/specs/otel/protocol/file-exporter/#examples

https://docs.newrelic.com/jp/docs/more-integrations/open-source-telemetry-integrations/opentelemetry/opentelemetry-ref-architecture/

https://docs.newrelic.com/jp/docs/more-integrations/open-source-telemetry-integrations/opentelemetry/best-practices/opentelemetry-best-practices-traces/