Datadog Go APM の Compile-time instrumentation

## はじめに
こんにちは、Datadog Japan で Sales Engineer をしている [**AoTo**](https://qiita.com/AoTo0330) です。

この投稿は **[<font color="#6495ED">AoTo Advent Calendar 2023</font>](https://qiita.com/advent-calendar/2023/aoto) 12日目**の記事です。

**Orchestrion** はご存知でしょうか。

Golang はコンパイル型の言語のため、Java, Python のように実行時にコードを追加することができません。アプリケーションの計装を行うには手動計装か、最近では eBPF を利用した自動計装が知られています。

これに対し Datadog は [**Orchestrion**](https://github.com/DataDog/orchestrion) と呼ばれる自動的計装のツールを公開しています。

:::note info
Orchestrion による ~~automatic instrumentation~~ Compile-time instrumentation(正式名称がつきました) は現在　~~Private beta版です。~~ **GA(一般公開)**　されました！(2024年12月)

**GA(一般公開)** に伴い、[Datadog 公式ドキュメント](https://docs.datadoghq.com/ja/tracing/trace_collection/automatic_instrumentation/dd_libraries/go/?tab=compiletimeinstrumentation) にも **Compile-time instrumentation** としてガイドが用意されています。
:::

## Orchestrion とは
[**Orchestrion**](https://github.com/DataDog/orchestrion) は Go によって書かれたコードを**自動的に計装**できるツールです。これはいわゆる「自動計装」とは違い、コンパイル前に自動的に「手動計装」を行なうことができるということです。

現在サポートされているライブラリは `net/http`, `database/sql`, `google.golang.org/grpc`, `github.com/gin-gonic/gin`, `github.com/labstack/echo/v4`, `github.com/go-chi/chi/v5`, `github.com/gorilla/mux`の7つです。この対象は順次拡大予定です🐶

この**自動的な計装(automatic instrumentation)** は、コードへの`instrument`ライブラリのインポート・初期化を行う内容を追加し、アプリケーションのラップ処理を追加を行ってくれます。これにより、アプリケーションの処理単位でのスパンの生成と、トレースコンテキストの伝搬、トレース情報の収集が可能になります。

追加されたコードの前後にはコメントが付与されるため、その内容を見ることで差分を把握することができます。

```diff: automatic-instrumentation.go
  package automaticinstrumentation

  import {
    "net/http"
+ 	"github.com/datadog/orchestrion/instrument"
  }

  func main() {
+  	//dd:startinstrument
+	defer instrument.Init()()
+	//dd:endinstrument
 	 mux := http.NewServeMux()
- 	 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- 		 w.Write([]byte("Hello World!"))
- 	 })
+   //dd:startwrap
+	mux.HandleFunc("/", instrument.WrapHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte("Hello World!"))
+	}))
+	//dd:endwrap
 	 http.ListenAndServe(":8080", mux)
  }
```

まず、`import`に`"github.com/datadog/orchestrion/instrument"`が追加されているのが分かります。計装する対象によって、自動的に追加のインポートを行うことがあります。

続いて、`//dd:startinstrument`から`//dd:endinstrument`までに`defer instrument.Init()()`という形で、インポートした`instrument`ライブラリを初期化しているのが分かります。

その後、`//dd:startwrap`から`//dd:endwrap`までに処理を`instrument`ライブラリでラップしています。これによって対象の処理に準じたスパンが作成されます。

## 導入方法
Orchestrion の導入は、たったの2ステップで簡単に行えます。
#### 1. Orchestrion のインストール
`go install`を利用して GitHub に公開されている `datadog/orchestrion` をインストールします。

```zsh
$ go install github.com/datadog/orchestrion@latest
```

#### 2. Orchestrion の実行
 計装対象のコードがあるディレクトリで、`orchestrion`コマンドを実行します。
 コマンドの通り、対象のパスを指定しても実行可能です。
 
 ```zsh
 $ orchestrion -w ./
 ```
 
:::note info
検証の際は`orchestrion`コマンドを実行できるよう`export PATH=$PATH:`go env GOPATH`/bin`などで PATH を通しておきましょう。
:::
 
完了です。問題がなければ `overwriting` のメッセージまでが表示されます

```zsh
Scanning Package ~/<my_go_project>
overwriting ~/<my_go_project>/main.go:
```

たったのこれだけで、Orchestrion がコードを上書きして自動的に計装されたコードが準備されます。その内容を`git diff`などで確認した後、`go build`で Go アプリケーションを開始すれば、計装された状態で Go アプリケーションを実行することができます。

:::note warn
Orchestrion を利用することでコードの行番号が変わるため、ビルド前に確認する必要があります。
:::

## オプション
単に Orchetrion に任せて自動的に計装するだけではなく、特定のコメントを付与することで追加の設定を反映することができます。

### `//dd:span`コメント
`//dd:span`コメントを用いることで、スパンタグの設定や処理名の指定が可能です。
このコメントでアノテーションされる関数やメソッドは`context.Context`を最初のパラメーターとして持つか、`*http.Request`をパラメーターとして持つ必要があります。

`//dd:span`コメントの書き方は以下のように、`key:value`形式で複数のタグを設定でき`name`の後に処理名を定義できます。

```go
//dd:span <key1>:<value1> <key2>:<value2> name <CustomMethodName>
```

### 計装の削除
計装の削除も簡単に行うことができます。`-rm`オプションを指定することで、対象のディレクトリの内容をスキャンして Orchestrion によって計装されたコードを削除します。

 ```zsh
 $ orchestrion -rm -w .
 ```
 
### オプションの設定
Datadog Go APM の`dd-trace-go`で利用できる[すべての構成オプション](https://docs.datadoghq.com/ja/tracing/trace_collection/library_config/go/)を使用できます。これにより、環境変数を利用して[統合サービスタグ付け](https://docs.datadoghq.com/ja/getting_started/tagging/unified_service_tagging)やサンプリングの設定を行えます。

## おわりに
今回は、**Orchestrion** と呼ばれる、Go の計装を自動的に実行できるツールを紹介してみました。~~現在はプライベートベータの状態ですが~~ **GA(一般公開)** されたため、誰でも簡単に試すことが可能です。

Go の手動計装をコードに記載する手間に懸念があったり、Go の eBPF による自動計装でのセキュリティやパフォーマンスの懸念がある際は試してみてはいかがでしょうか🐶