Go 向けの Application Real-Time Monitoring (ARMS) エージェントは、ソースコードを変更することなくカスタム機能を挿入できるカスタム拡張を提供します。これにより、リクエストパラメーターやリクエストボディからデータをキャプチャして問題のトラブルシューティングに役立てることができます。このトピックでは、Net/HTTP を例に、これらの拡張機能を使用してリクエストヘッダーとレスポンスヘッダーをキャプチャする方法を説明します。
前提条件
-
アプリケーションが Go 1.18 以降で実行されていること。
-
Go アプリケーションが Application Real-Time Monitoring (ARMS) と統合されていること。
重要統合ドキュメントで指定されているように、ビルドコマンドを
./instgo go build xxxに変更してください。
制限事項
-
カスタム拡張を使用して
package main内のユーザー定義関数を計装することはできません。 -
カスタム拡張を使用して、レシーバータイプが
anyである関数を計装することはできません。
コードの仕様
-
フックコードで
package mainを使用しないでください。 -
バージョン 2.0.0 以降のコードを使用する場合、次のように import 文に
"_ unsafe"を追加する必要があります。import ( "encoding/json" "fmt" "github.com/alibaba/loongsuite-go-agent/pkg/api" "net/http" _ "unsafe" ) -
OnEnter関数の最初のパラメーターは常にcall api.CallContextで、2 番目のパラメーターはReceiverType(存在する場合)、それ以降のパラメーターは挿入された関数のリクエストパラメーターです。説明ReceiverTypeに*が含まれる場合、文字をエスケープするには、JSON ファイルに\\プレフィックスを追加する必要があります。 -
OnExit関数の最初のパラメーターはcall api.CallContextとして固定されており、後続のパラメーターは、挿入された関数の戻り値が順番に指定されます。
例えば、net/http::(*Transport).RoundTrip 関数は以下の通りです。
func (t *Transport) RoundTrip(req *Request) (*Response, error) {
return t.roundTrip(req)
}
RoundTrip の ReceiverType は *Transport であるため、OnEnter 関数の 2 番目のパラメーターは *Transport、3 番目のパラメーターは req *Request になります。OnExit 関数の 2 番目と 3 番目のパラメーターは、RoundTrip の戻り値である *Response, error です。
ReceiverType がエクスポートされていない型である場合、_ interface{} を代わりとして使用できます。また、無視したいパラメーターがある場合も、_ interface{} を代わりとして使用できます。
操作手順
-
現在のプロジェクト外のディレクトリに、
rulesという名前のフォルダーを作成し、go mod init rulesコマンドを実行して初期化してから、そのフォルダーに以下のコードでrules.goという名前のファイルを作成します。このコードはカスタム拡張を定義します。
package rules import ( "encoding/json" "fmt" "github.com/alibaba/loongsuite-go-agent/pkg/api" "net/http" _ "unsafe" ) //go:linkname httpClientEnterHook1 net/http.httpClientEnterHook1 func httpClientEnterHook1(call api.CallContext, t *http.Transport, req *http.Request) { header, _ := json.Marshal(req.Header) fmt.Println("request header is ", string(header)) } //go:linkname httpClientExitHook1 net/http.httpClientExitHook1 func httpClientExitHook1(call api.CallContext, res *http.Response, err error) { header, _ := json.Marshal(res.Header) fmt.Println("response header is ", string(header)) } -
config.jsonファイルを修正し、以下の内容を追加してフックコードをnet/http::(*Transport).RoundTripにインジェクトします。[ { "ImportPath":"net/http", "Function":"RoundTrip", "OnEnter":"httpClientEnterHook1", "ReceiverType": "\\*Transport", "OnExit": "httpClientExitHook1", "Path": "/extension/rules" // rules フォルダーへの絶対パスに置き換えます。 } ] -
テストデモを作成します。
rulesフォルダー以外のディレクトリに、デモアプリケーション用のフォルダーを作成し、go mod init demoコマンドを使用して初期化します。次に、demoフォルダーに、以下のコードを含むnet_http.goファイルを作成します。package main import ( "context" "net/http" ) func main() { req, err := http.NewRequestWithContext(context.Background(), "GET", "http://www.baidu.com", nil) if err != nil { panic(err) } req.Header.Set("otelbuild", "true") client := &http.Client{} resp, err := client.Do(req) defer resp.Body.Close() } -
demoディレクトリに切り替えます。instgo ツールを使用してプログラムをビルドし、実行します。$ ./instgo set --rule=../config.json $ INSTGO_CACHE_DIR=./ ./instgo go build net_http.go # Linux システムで実行する必要がある場合 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 INSTGO_CACHE_DIR=./ ./instgo go build net_http.go $ ./net_http次の出力は、拡張機能が正常に挿入されたことを示しています。
request header is {"Otelbuild":["true"]} response header is {"Content-Type":["application/x-gzip"],"Date":["Mon, 04 Nov 2024 09:10:48 GMT"],"Server":["bfe"]}完全なコード例については、「nethttp」をご参照ください。
説明リクエストヘッダーとレスポンスヘッダーのキャプチャに加えて、SQL インジェクション検出、カスタムロギング、リクエストパラメーターとレスポンスパラメーターへのアクセスなど、他の目的にもカスタム拡張を使用できます。