Datadog APMをGoで使う

# はじめに

Datadog APMの簡単な特徴とGoで使用するための方法をまとめました。

公式サイトのドキュメントを見ていただくことを勧めますが、
補足資料として見ていただければと思います。

[公式ドキュメント](https://docs.datadoghq.com/ja/tracing/)
[TraceパッケージのGoDoc](https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/ddtrace)

また、ここではDatadogのAPM以外の機能については割愛します。

# APMとは

Application Performance Managementの頭文字をとった略語で、一般的にはアプリケーションやシステムの性能を管理・監視するものです。

DatadogのAPMではマネージドクライド、オンプレミスに関わらず分散トレーシングに対応しています。
Flame GraphやSpan Listなど整理された形でトレース結果を取得できるようになっています。

- Flame Graph
<img width="926" alt="FlameGraph.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76076/be91fc3c-28c7-0698-52b4-3fef3f592259.png">
- Span List
<img width="927" alt="SpanList.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76076/a0a8152a-a43a-14c4-dae4-d1d7af300197.png">

# 使用するには

- Agentのインストールと起動
- APM用パッケージを取得


## Agentのインストール

下記のページから各環境に合わせてインストールとAgentの起動を実施します。
https://app.datadoghq.com/account/settings#agent

基本的には上記のページに従ってインストールを実施すればAgentプロセス起動も実施してくれるが、再起動等を行う場合や設定を変更する場合は下記を参照してくだしあ

- [Agentコマンド](#agentコマンド)
- [Agentのインストールの詳細](#agentのインストールの詳細)

## APM用パッケージを取得

```
go get gopkg.in/DataDog/dd-trace-go.v1/ddtrace
```

# トレースの仕方

## TraceとSpan

トレースを行うにあたり下記の要素が存在します。

|用語|内容|
|---|---|
|Trace|アプリケーションにおけるトレースの開始・実行の単位|
|Span|1ロジックの開始・終了の実行時間を計測する単位。Traceは1つ以上のSpanを持つ。Span同士は親子関係をもたせられる|
|Service|処理の名前。複数Spanを一つのService名にすることも、各々独自のService名にすることも可能|
|Resource|処理の中身を示す。DBにおけるSQLクエリやHTTPにおけるクエリなどを格納する|


### Span

トレースしたい処理の開始から終了までの計測単位を指します。
Spanはさらに細かいSpanを配下に持つことが可能です。

トレースの中における親子Spanの関係
<img width="653" alt="親子Span.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76076/2126836c-566a-5045-f46f-1233fee66419.png">

例えばWebの1リクエストにおけるトランザクションを親Spanとし
その配下にDBのSpan、キャッシュのSpan、外部通信のSpanやさらには特定のロジックなどを子Spanとすること可能です。

<img width="694" alt="APM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76076/55e438c8-afd8-ee33-3971-7f208324ed8c.png">

## ソース

Datadog APM用のパッケージは下記の通りです。

[GitHub](https://github.com/DataDog/dd-trace-go)
[説明](https://docs.datadoghq.com/ja/tracing/setup/go/)

こちらをトレース開始・終了、必要Spanの開始終了に伴い実行していく

### 1. トレース開始

```
tracer.Start()	
defer tracer.Stop() // Close	
```

https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/ddtrace


### 2. 汎用的なSpan

Spanの開始と終了

```
span, ctx := tracer.StartSpanFromContext(ctx, 
    "parent-op", // operation Name
    tracer.ServiceName("parent-service"), // Service Name(必要であれば入れる)
    tracer.ResourceName("parent-resource"), // Resource Name(必要であれば入れる)
)

defer span.Finish()
```

#### 子Spanの使用

先程作成したSpanの配下に子Spanを設ける場合
親が投入されたcontext.Contextを使用して子Spanの開始すれば親Span情報が継承されます。

子Spanの開始と終了

```
span, ctx := tracer.StartSpanFromContext(ctx, 
    "child-op", // operation Name
    tracer.ServiceName("child-service"), // Service Name(必要であれば入れる)
    tracer.ResourceName("child-resource"), // Resource Name(必要であれば入れる)
)

defer span.Finish()
```

### 専用Spanパッケージを使用する

[各パッケージのGoDoc一覧](https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/contrib)

#### Web(HTTP Routing)用Span

アプリケーションのHTTPのRoutingで下記のいずれかのパッケージを使用していた場合は専用のパッケージが存在します。

|もともと使用しているpackage|Datadog APM package|
|---|---|
|[net/http](https://github.com/golang/go/tree/master/src/net/http)|https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http|
|[gorilla/mux](https://github.com/gorilla/mux)|https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/contrib/gorilla/mux|
|[julienschmidt/httprouter](https://github.com/julienschmidt/httprouter)|https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter|


gorilla/mux使用の場合

```
router = muxtrace.NewRouter(	
	muxtrace.WithServiceName("web-service"),  // ServiceName設定
)

router.HandleFunc("/", handler)
http.ListenAndServe(":8080", router)
```

#### DB用Span

database/sql使用の場合

```
// 登録(DBはMySQLの場合、ServiceNameは省略可)
sqltrace.Register("mysql", &mysql.MySQLDriver{}, sqltrace.WithServiceName("db"))

// traceに登録済のdriverを使用してDBをオープン
db, err := sqltrace.Open("mysql", "user:password@/dbname")
if err != nil {
    log.Fatal(err)
}

// Span作成・開始
span, ctx := tracer.StartSpanFromContext(ctx, 
    "db", // operation Name(必要であれば入れる)
    tracer.SpanType(ext.SpanTypeSQL),
    tracer.ServiceName("db"), // DB用Service Name(必要であれば入れる)
    tracer.ResourceName("db-access"), // Resource Name(必要であれば入れる)
)

// contextを通じて親Spanを継承している
rows, err := db.QueryContext(ctx, "SELECT * FROM memo")
if err != nil {
    log.Fatal(err)
}
rows.Close()
span.Finish(tracer.WithError(err)) // Span終了
```

# その他

## Agent関連

### Agentのインストールの詳細

Agentのインストールを実施するとdatadog.yamlが指定の場所にダウンロードされます。Agentはそのyamlファイルを設定ファイルとして使用します。

datadog.yamlの設定サンプルは[こちら](https://github.com/DataDog/datadog-agent/blob/master/pkg/config/config_template.yaml)

ダウンロードしたデフォルト設定のままでAPMは使用可能ですが、
API Keyを変えたりAPM関連の設定を変えたりした場合はdatadog.yamlの中身をチェックする必要があります。

- API Keyが正しく指定されているか
  - 値を確認する際は[こちら](https://app.datadoghq.com/account/settings#api)
- APM関連の各種設定

設定変更後はAgentの再起動を行う
再起動コマンドは[こちら](https://docs.datadoghq.com/ja/agent/guide/agent-commands/?tab=agentv6v7)

### Agentコマンド

Agentの起動・終了・再起動コマンド
https://docs.datadoghq.com/ja/agent/guide/agent-commands/?tab=agentv6v7

## Grpcの場合

<img width="657" alt="datadog_sample_simple_image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76076/37dd8de2-e65d-d443-4434-f9758f76c52d.png">

[こちら](https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/contrib/google.golang.org/grpc)を参考にgRPC Client側/Server側それぞれにTraceとSpanを設けた場合


- [gRPC Client側ソース](https://github.com/muroon/datadog_sample/blob/master/httpserver/usecases/grpc.go#L32)
- [gRPC Server側ソース](https://github.com/muroon/datadog_sample/blob/master/grpcserver/main.go#L96)

下記のようにトレースされます。
![FlameGraph_grpc2_exp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76076/7f9f9870-7c13-be18-fa68-13a3d6aa738a.png)


## OpenTracing, OpenCensus, OpenTelemetry

分散トレーシング標準仕様で[OpenTracing](https://docs.datadoghq.com/ja/tracing/opentracing/?tab=go), [OpenCensus](https://opencensus.io/exporters/supported-exporters/go/datadog/)に対応済み

OpenTelemetryはまだ正式なRegistoryには入っていない模様(2020/04/10現在)
https://opentelemetry.io/registry/

この辺は今後も見ていきたいです。

# 自動軽装

こちらを自前で計装に必要なソースを書かなくても、コマンド一つで必要なソースを吐き出してくれます。

下記の記事を参照していただければと思います。
* https://docs.datadoghq.com/ja/tracing/trace_collection/automatic_instrumentation/?tab=singlestepinstrumentationpreview
* https://qiita.com/AoTo0330/items/f36eae6fb00061b6a0f7

手動実装と比べると
* 導入は楽です
* 使用しているpackageを鑑みて必要な軽装を入れてくれるので、一通り表示しれくれます
* 手動軽装に比べるとかゆいところに手がとどかないようなトレースになる
    * [こちら](#grpc%E3%81%AE%E5%A0%B4%E5%90%88)のように入れ子のGraph表現はしてくれない

# 最後に

以上のようにセットアップも非常に簡単です。
また、有料サービスだけあって専用サーバー管理がいらないのは楽で良いです。

ただ、もし実導入するようであればトレーシングによるアプリの負荷については注意深く見ておく必要があると思います。(Datadogに限った話ではないのですが)