curl -> OpenTelemetry Collector -> otel-tui で簡単 OTLP trace 入門
概要
本記事では、OpenTelemetry(OTel)のトレース入門として、curl -> OpenTelemetry Collector -> otel-tui を使った簡単な実装を紹介します。
OTel や OpenTelemetry Collector(OTel Collector)に興味があるものの、どのように試せば良いかわからない方の学習の一助になれば幸いです。
サンプルコードは以下のリポジトリで公開しています。一部のソースコードおよび OTel に送信するリクエストボディは AI を利用して作成しました。
実装
プロジェクトを作成
ディレクトリを作成してください。
mkdir otel-collector-curl-otel-tui
docker-compose.yml を作成
作成したディレクトリ内で、OTel Collector を起動するための docker-compose.yml を作成します。
services:
otel-collector:
image: otel/opentelemetry-collector:latest
container_name: otel-collector
ports:
- "4318:4318" # OTLP/HTTP
volumes:
- type: bind
source: ${PWD}/otelcol/config.yaml
target: /etc/otelcol/config.yaml
restart: unless-stopped
OpenTelemetry Collector の config.yaml を作成
続いて、OpenTelemetry Collector の設定ファイルである config.yaml を作成します。
設定におけるポイントは以下です。
- receiver が OTLP を受信するポートを 4318 に設定
- exporter が OTLP を送信する先を Docker を起動した host(
host.docker.internal)の 4317 ポートに設定 - トレースの pipeline を設定
receivers:
otlp:
protocols:
http: # OTLP over HTTP: 4318
endpoint: 0.0.0.0:4318
exporters:
debug: {} # 標準出力で debug ログを出力
otlp:
endpoint: host.docker.internal:4317 # docker を起動した host の 4317 ポートに送信
tls:
insecure: true
service:
pipelines:
# trace のパイプライン設定
traces:
receivers: [otlp]
exporters: [debug, otlp]
docker compose up を実行して、以下の画像のように動作したら成功です。本記事の解説ではこのまま起動したままで進めますが、中断したい場合や確認を終えたら、Ctrl + cで停止して、docker compose downでコンテナを削除してください。

otel-tui の用意
以下の記事を参考に、otel-tui をインストールしてください。
インストールが完了したら、otel-tui --http 8888 で起動してください。画像のように起動したら準備完了です。otel-tui も同様に起動したまま進めます。中断したい場合や確認を終えたら、停止するには Ctrl + c を実行します。

動作確認
これから動作確認を実施します。curl コマンドで実行した結果を otel-tui で確認します。
確認観点は以下です。
- 単一のトレースを送信する
- 親スパンをもつトレースを送信する
単一のトレースを送信する
curl コマンドで、ローカルの OTel Collector に送信するシェルスクリプトを作成します。
以下のファイルを作成してください。
#!/bin/bash
# ナノ秒単位の現在時刻を取得
# macOSの場合はgdate、Linuxの場合はdateコマンドを使用
if command -v gdate &> /dev/null; then
NOW=$(gdate +%s%N)
else
NOW=$(date +%s%N)
fi
# 32文字の16進数文字列を生成 (16バイト)
TRACE_ID=$(openssl rand -hex 16)
# 16文字の16進数文字列を生成 (8バイト)
SPAN_ID=$(openssl rand -hex 8)
START_TIME=$NOW
END_TIME=$((NOW + 1000000000))
curl -sS -X POST http://localhost:4318/v1/traces \
-H 'Content-Type: application/json' \
--data-binary @- <<JSON
{
"resourceSpans": [{
"resource": {
"attributes": [
{"key":"service.name","value":{"stringValue":"curl-client"}},
{"key":"service.host","value":{"stringValue":"mac"}}
]
},
"scopeSpans": [{
"spans": [{
"traceId": "$TRACE_ID",
"spanId": "$SPAN_ID",
"name": "example-span",
"kind": 1,
"startTimeUnixNano": "$START_TIME",
"endTimeUnixNano": "$END_TIME",
"attributes": [
{"key":"http.method","value":{"stringValue":"GET"}},
{"key":"http.url","value":{"stringValue":"http://example.com"}}
],
"status": {
"code": 1
}
}]
}]
}]
}
JSON
echo "Trace sent with ID: $TRACE_ID"
作成したら、bash otlp-curl-trace.sh で実行してみましょう。
実行すると、以下のようなログが出力されます。
> bash otlp-curl-trace.sh
{"partialSuccess":{}}Trace sent with ID: 13b02495491cd4df8a5d0bbed050a42a
otel-tui にトレースが表示されていれば成功です。このように、Collector に送信した内容が、オブザーバビリティバックエンドである otel-tui に転送・表示されます。


親スパンをもつトレースを送信する
続いて、親子関係を持つスパンを含むトレースを送信するシェルスクリプトを作成します。
長いため折りたたんでいます。
parentSpanId を持つスパンが、親子関係にあることを示しています。
実際にリクエストを送って確認してみましょう。
親スパンをもつトレースを送信するシェルスクリプト
#!/bin/bash
# ナノ秒単位の現在時刻を取得
# macOSの場合はgdate、Linuxの場合はdateコマンドを使用
if command -v gdate &> /dev/null; then
NOW=$(gdate +%s%N)
else
NOW=$(date +%s%N)
fi
# 32文字の16進数文字列を生成 (16バイト)
TRACE_ID=$(openssl rand -hex 16)
# 16文字の16進数文字列を生成 (8バイト)
PARENT_SPAN_ID=$(openssl rand -hex 8)
# 16文字の16進数文字列を生成 (8バイト)
CHILD_SPAN_ID=$(openssl rand -hex 8)
# 16文字の16進数文字列を生成 (8バイト)
CHILD_SPAN_ID_2=$(openssl rand -hex 8)
PARENT_START_TIME=$NOW
PARENT_END_TIME=$((NOW + 2000000000))
CHILD_START_TIME=$((NOW + 100000000))
CHILD_END_TIME=$((NOW + 1500000000))
CHILD_START_TIME_2=$((NOW + 200000000))
CHILD_END_TIME_2=$((NOW + 1800000000))
curl -sS -X POST http://localhost:4318/v1/traces \
-H 'Content-Type: application/json' \
--data-binary @- <<JSON
{
"resourceSpans": [{
"resource": {
"attributes": [
{"key":"service.name","value":{"stringValue":"parent-child-service"}},
{"key":"service.version","value":{"stringValue":"1.0.0"}},
{"key":"service.host","value":{"stringValue":"mac"}}
]
},
"scopeSpans": [{
"spans": [
{
"traceId": "$TRACE_ID",
"spanId": "$PARENT_SPAN_ID",
"name": "parent-operation",
"kind": 2,
"startTimeUnixNano": "$PARENT_START_TIME",
"endTimeUnixNano": "$PARENT_END_TIME",
"attributes": [
{"key":"http.method","value":{"stringValue":"POST"}},
{"key":"http.url","value":{"stringValue":"http://api.example.com/process"}},
{"key":"http.status_code","value":{"intValue":"200"}},
{"key":"operation.type","value":{"stringValue":"parent"}}
],
"status": {
"code": 1,
"message": "Success"
}
},
{
"traceId": "$TRACE_ID",
"spanId": "$CHILD_SPAN_ID",
"parentSpanId": "$PARENT_SPAN_ID",
"name": "child-operation-database-query",
"kind": 3,
"startTimeUnixNano": "$CHILD_START_TIME",
"endTimeUnixNano": "$CHILD_END_TIME",
"attributes": [
{"key":"db.type","value":{"stringValue":"postgresql"}},
{"key":"db.statement","value":{"stringValue":"SELECT * FROM users WHERE id = ?"}},
{"key":"db.operation","value":{"stringValue":"SELECT"}},
{"key":"operation.type","value":{"stringValue":"child"}}
],
"status": {
"code": 1,
"message": "Success"
}
},
{
"traceId": "$TRACE_ID",
"spanId": "$CHILD_SPAN_ID_2",
"parentSpanId": "$PARENT_SPAN_ID",
"name": "child-operation-cache-lookup",
"kind": 3,
"startTimeUnixNano": "$CHILD_START_TIME_2",
"endTimeUnixNano": "$CHILD_END_TIME_2",
"attributes": [
{"key":"cache.type","value":{"stringValue":"redis"}},
{"key":"cache.operation","value":{"stringValue":"GET"}},
{"key":"cache.key","value":{"stringValue":"user:session:12345"}},
{"key":"cache.hit","value":{"boolValue":true}},
{"key":"operation.type","value":{"stringValue":"child"}}
],
"status": {
"code": 1,
"message": "Cache hit"
}
}
]
}]
}]
}
JSON
echo "Trace sent with:"
echo " Trace ID: $TRACE_ID"
echo " Parent Span ID: $PARENT_SPAN_ID"
echo " Child Span ID 1: $CHILD_SPAN_ID"
echo " Child Span ID 2: $CHILD_SPAN_ID_2"
作成したら、bash otlp-curl-trace-with-parent-child.sh で実行してみましょう。
実行すると、以下のようなログが出力されます。
> bash ./otlp-curl-trace-with-parent-child.sh
{"partialSuccess":{}}Trace sent with:
Trace ID: 22aaa3bfa06fb4dbb0725973c8af512e
Parent Span ID: c6ff9719c672de56
Child Span ID 1: 58f281e985dfa5c9
Child Span ID 2: 038af19e2dc54bbe
otel-tui にトレースが表示され、スパンに ▼ と └ の記号が付いています。これは親子関係を持つスパンであることを示しています。
curl で任意のトレースや属性を送信することで、親子関係を持つスパンの動作を確認できました。


まとめ
本記事では、curl、OTel Collector、otel-tui を組み合わせて OTLP 形式のトレース送受信を確認しました。
この構成により、OpenTelemetry の動作や仕様を手軽に検証できます。
OpenTelemetry に興味がある方の学習の一助になれば幸いです。
Discussion