ClassicASPでDatadogAPM (Trace)を使えるようにする

# はじめに
弊社では、システムのリプレースプロジェクトを進めています。
しかしながら、リプレースプロジェクトはまだ過渡期で、ClassicASPで構成されたレガシーシステムが残っており、リプレースが完了するまではまだ時間がかかります。

リプレース済みのシステムではDatadogAPMを導入し、アプリケーションパフォーマンスを可視化していますが、レガシーシステム部分ではAPMが利用できていませんでした。
DatadogAPMがClassicASPのAutoInstrumentに対応していないことが要因です。


そこで、ClassicASPでもDatadogAPMのTraceを利用できるようにするCOMを開発したので、その内容を簡単にお話しさせていただきます。

※本記事の内容は、2022年のきっかけとその開発時の内容を振り返って書いた記事になります。2023年9月現在。誰かの参考になればと思い書いております。


# 課題
ClassicASP環境でも、DatadogAPMを使ってレガシー環境とリプレース環境のトレースをつないで可視化したい。


# PoC
Datadog社の担当にきいたが、ClassicASP対応する予定は今後もないということ。
何か方法がないかを調査しました。

モダンな言語で利用できる、DatadogTracerAgentを用いたAuto Instrumentはできないが、Manual Instrumentであれば、ライブラリを作りさえすればトレースできそう。

実際に、WindowsのPowershellで以下のコードでトレースを送れることがわかりました
```
# TraceSpan開始時の処理

## service設定
$name = "web"
$service = "sample.service"
$resource = "/test"
$env = "dev"


## parent_id (int64)
## 実際にはリクエストヘッダから取得する部分
$parent_id = Get-Random -Maximum 9223372036854775807 -Minimum 1000000000000000000
echo $parent_id
 
## trace_id (int64)
## 実際にはリクエストヘッダから取得する部分。なければ生成する
# Get-Random [-Maximum 最大値] [-Minimum 最小値] [-Count 取得数]
$trace_id = Get-Random -Maximum 9223372036854775807 -Minimum 1000000000000000000
echo $trace_id
 
## span_id (int64)
$span_id = Get-Random -Maximum 9223372036854775807 -Minimum 1000000000000000000
echo $span_id
 
## start (UnixEpochからのナノ秒)
$now = (Get-Date)
$epoch = (Get-Date("1970/1/1 0:0:0 GMT"))
$start = [int64]((($now)) - ($epoch)).TotalMilliSeconds
$start = (( $start *1000 *1000))
echo $start
 
# ここは処理が実装されるので、今はダミーで1秒
# 途中でmicroservice-API呼ぶ場合は、trace_id、parent_idなどをheaderにつけてリクエストしてあげる
sleep 1
 
 
# TraceSpan終了時の処理
 
## end (UnixEpochからのナノ秒)
$now = (Get-Date)
$epoch = (Get-Date("1970/1/1 0:0:0 GMT"))
$end = [int64]((($now)) - ($epoch)).TotalMilliSeconds
$end = (( $end *1000 *1000))
echo $end
 
## duration
$duration = $end - $start
echo $duration
 
## body
$body = '[ [ {"error": 0, "duration":'+$duration+',"name":"'+$name+'","resource":"'+$resource+'","service":"'+$service+'","parent_id":'+$parent_id+', "span_id":'+$span_id+',"start":'+$start+',"trace_id":'+$trace_id+', "meta": {"env": "'+$env+'"}} ] ]'
echo $body
 
## ローカルのDatadogAgent-APIにTraceを打つ
Invoke-WebRequest `
-Headers @{"Content-Type"="application/json;charset=UTF-8"} `
-Body ([System.Text.Encoding]::UTF8.GetBytes("$body")) `
-Method PUT `
-Uri "http://localhost:8126/v0.3/traces"


```

Powershellでのテストトレースは、DatadogAPMで可視化することができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27112/8bb314b9-d7d7-778f-8276-06958e4b3be4.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27112/0f060d0e-6177-82ed-bcd2-9907dc3a384a.png)


# COM開発
ClassicASPは、.NetFrameworkのライブラリをCreateObjectして使うことができる機能があります。
そこで、DatadogTracerCOMというオリジナルのCOMライブラリを作り、CreateObjectして使うことでSpanの組み立てを行い、AgentAPIに送ることを考えました。

Datadog社の担当にも技術的な仕様をうかがって、DatadogTracerCOMという独自ライブラリを作成。
サーバーにCOMアセンブリとして登録し、ClassicASPでCreateObjectして使います。
※この辺の実装は、公開に関してセンシティブなので割愛します。

# 分散トレーシングでの課題
作成したCOMを使って、レガシー環境とリプレース環境のトレースを可視化しつなげることができました。
ただ、分散トレーシングの仕様上、どうしても解決できない問題があります。

それは、環境ごとのNTPのずれによる「Spanの開始位置のずれ」です。

イメージ的にこんな感じ。
子スパンが親スパンよりも先に始まっている＝子システムの時刻が親のシステムよりもちょっと早い
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27112/be422358-1733-b870-a65f-ea01205c7a25.png)


AWSのEC2やEKS環境は、AWSのNTPサーバーで時刻を合わせているので問題はないのですが、オンプレ環境は違うNTPサーバーを参照しているため、時刻が若干ずれてしまいます。その差分が、数msレベルのトレースでずれとなります。
これは、分散トレーシングでは仕方のないことです。
出来る対策は、全システムで同じNTPサーバーを利用して時刻同期することですが、これは現状難しいのでこの表示のずれは許容します。

大事なことは、レガシー環境での処理時間を可視化できること。


# まとめ
ZOZOTOWNでは、このような工夫をし、レガシー環境とリプレース環境でのTraceをつなぎDatadogAPMでトレースの可視化を行いました。

独自開発した「DatadogTracerCOM for ClassicASP」は、今のところ公開は予定しておりませんが、どのようにしてClassicASP(VBScript)でDatadogAPMを導入したかの簡単なお話させていただきました。

あまりないかもしれませんが、もし、「ClassicASP環境でDatadogAPMのTraceを導入したいが。。。」とお悩みの方がいらっしゃれば、参考になれば幸いです。