OpenTelemetryの計装をデバッグする
はじめに
この記事ではOpenTelemetryでの計装内容をローカルで確認するために、次の方法を紹介します。
- stdoutに出力する
- OpenTelemetry Collector からファイルに出力する
- ローカルでOpenTelemetryのサーバーを建てる
1. stdoutに出力する
一番簡単なのはstdoutに出力することです。
言語毎にnullの表現方法など微妙に違う部分がありますが、「計装できているか」「入れたいAttributeが入っているか」という確認には十分です。
多くの言語ではstdoutに出力する方法は公式で用意されています。
例えばgoではstdouttrace
を使います。
import(
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
...
)
func setOtel() {
// stdouttraceを使用
exporter := stdouttrace.New(stdouttrace.WithPrettyPrint())
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
}
このように、既存のExporterをstdouttrace
に入れ替えるだけでstdoutに出力できるようになります。
(ここではかなり簡略した実装でOpenTelemetryを使っています。細かい実装については別の記事を参照してください)
stdouttrace
を使って動かすと、stdoutに下のようなJSONが出力されます。
{
"Name": "gorm.Query",
"SpanContext": {
"TraceID": "8b50a436cba088a3c4e635c5b5cc733f",
"SpanID": "704b0c869eeeef22",
},
"Parent": {
"TraceID": "8b50a436cba088a3c4e635c5b5cc733f",
"SpanID": "9c7cdeaff0b82ec0",
},
"Attributes": [
{
"Key": "db.system",
"Value": {
"Type": "STRING",
"Value": "mysql"
}
},
{
"Key": "db.statement",
"Value": {
"Type": "STRING",
"Value": "SELECT * FROM `payment_methods` WHERE id = 1"
}
},
],
...
}
これをみて、gorm.Query
という名前から「gorm部分も計装できているな」と確認することができるというわけです。
2. OpenTelemetry Collector からファイルに出力する
stdoutで物足りない場合は、OpenTelemetry Collectorを使う方法もあります。
OpenTelemetry CollectorにはFile Exporter
というexporterが用意されています。
File Exporter
を使うには、OpenTelemetry Collectorに次の設定を追加します。
exporters:
file:
path: /data/trace
format: json
service:
pipelines:
traces:
...
exporters: [file]
これで、OpenTelemetryが外部に出力している内容が/data/trace
に書き込まれます。
(このyamlでは必要な部分しか書いていません。yamlの設定については別の記事を参照してください)
ファイルには下のようなJSONが書き込まれます。
{
"resourceSpans": [
{
"resource": {...},
"scopeSpans": [
{
"scope": {
"name": "gorm.io/plugin/opentelemetry"
},
"spans": [
{
"traceId": "8b50a436cba088a3c4e635c5b5cc733f",
"spanId": "704b0c869eeeef22",
"parentSpanId": "9c7cdeaff0b82ec0",
"name": "gorm.Query",
"attributes": [
{
"key": "db.system",
"value": {
"stringValue": "mysql"
}
},
{
"key": "db.statement",
"value": {
"stringValue": "SELECT * FROM `users` WHERE id = 1 ORDER BY `users`.`id` LIMIT 1"
}
},
...
],
},
]
},
],
"schemaUrl": "https://opentelemetry.io/schemas/1.21.0"
}
]
}
OpenTelemetryの出力はネストが深いですが、ネストの最深部にstdoutと同じくgorm.Query
の名前が確認できます。この部分が計装した内容です。
基本的な内容はstdoutと同じ内容ですが、省略が無くprotoと一対一で対応するので詳しく調査する場合に有益です。
ちなみに、ymlでformatをproto
に設定するとProtocol Buffersで出力することができますが、
each encoded object is preceded by 4 bytes (an unsigned 32 bit integer) which represent the number of bytes contained in the encoded object
とREADMEにあるように「先頭4バイトから長さを取って、その長さでファイルを分割して、その後にprotocにかけて・・・」とやらないと中身が見れないのでjsonの方が読みやすいです。
一応、一行だけなら下のようにすれば中身が見れます。
tail -c +5 /data/trace | head -c $(head -c 4 /data/trace | xxd -p | awk '{print "0x"$1}' |xargs printf "%d") | protoc --decode_raw
3. ローカルでOpenTelemetryのサーバーを建てる
ローカルにサーバーを建ててしまうという方法もあります。
Dockerの準備などがあるものの、グラフなどで可視化されるので直感的に確認できるのが利点です。
例えば、以下のOSSが使えます。
終わりに
以上、OpenTelemetryの計装内容を確認する方法でした。
Discussion