📑

Datadog🐶のAPM・TraceでGo(echo)×SQL(gorm)のトレースをする

2023/08/09に公開
3

詰まったこと

DatadogのAPMをGoで実装されたAPIに導入する際に、echo単体・SQL単体のTraceは取れるようにすることはできたけど、echoのHTTPリクエストのTraceにSQLTraceが紐付かない という状況に詰まっていました。

やりたいことのイメージ

この画像のように、HTTPリクエストのTraceの中にそのリクエストで実行SQLのTraceが紐付いた形で情報を取れるようにすること。

参考: https://docs.datadoghq.com/ja/tracing/trace_explorer/trace_view/?tab=spantags

試していたこと

  1. tracerをinitializeする実装をする
  2. echoのtracerを実装する
  3. gormtraceを実装する

1.と2.を実装した時点で、APMのTraceを見ると、echoのHTTPリクエストのデータしか取れておらず、 Traceの中にSQLのSpanがない状態でした。

https://pkg.go.dev/gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1#example-Open
https://qiita.com/behiron/items/e1bf6f870ebd7719f7e0

この辺りの記事を参考にSQLのTraceをするにはgormtrace(sqltrce)を実装する必要があると思い、導入しましたが、SQL単体のTraceが取れるようになったものの先程の画像のように、HTTPリクエストのTraceの中にSQLのSpanが紐付くようにはなりませんでした。

解決方法

Datadogのサポートに協力いただいたりした結果、

  • クエリ実行時にWithContext(ctx)でechoのContextをgormに渡す

ことでやりたいことが実現できることがわかりました。

実装イメージは以下です。

func GetProjects(c echo.Context) error {
	var people []Person
	if err := gormtrace.WithContext(c.Request().Context(), db).Find(&people).Error; err != nil {
		fmt.Println(err)
		return c.JSON(400, people)
	} else {
		return c.JSON(200, people)
	}
}

※ Railsなどではtracerをinitializeするだけで子SpanもTraceできるという先入観で諸々initializeすればイケるだろうという考えを持っていたので結構時間を溶かしてしまいました。

解決した後に気づいた参考になるブログ

  • echo と olivere/elastic の統合

なので若干技術スタックは異なりますが、DatadogのAPMに関することがとてもわかりやすくまとまっていて参考になります。

https://tech.every.tv/entry/2021/12/14/120000

まとめ

以上が、GoのAPI×SQLをDatadogのAPMでTraceする際にハマった点とその解決方法になります。
断片的に見ていてもなかなか解決できなかったりするという学びと、アウトプットをこのような簡単な形でもしていけるといいなあと感じたので継続していけたらと思います。

似たようなことにハマっている人の助けになれば幸いです。

Discussion

muttsu_623muttsu_623

同じように親のSpanに子(GORM)のSpanが表示されずに困っているのですが、記事内で gormtrace と表示されている部分は https://pkg.go.dev/gopkg.in/DataDog/dd-trace-go.v1@v1.62.0/contrib/gorm.io/gorm.v1 ではなく、 https://pkg.go.dev/gopkg.in/DataDog/dd-trace-go.v1@v1.62.0/contrib/jinzhu/gorm を利用されてますか?

tkg216tkg216

gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1
なので前者になると思います🙏

muttsu_623muttsu_623

こちらありがとうございます!
完全に自分がミスっておりまして、APIサーバからのContextを c.Request().Context() ではなく c をそのまま渡してしまっていたのがミスでした🙏