O agente do Application Real-Time Monitoring (ARMS) para Go oferece extensões personalizadas que permitem injetar funcionalidades específicas sem alterar seu source code. Esse recurso facilita a solução de problemas ao capturar dados dos parâmetros e do corpo da requisição. Este tópico utiliza Net/HTTP como exemplo para demonstrar como capturar cabeçalhos de requisição e resposta com essas extensões.
Pré-requisitos
Verifique se sua aplicação executa no Go 1.18 ou superior.
-
Sua aplicação Go já está integrada ao Application Real-Time Monitoring (ARMS).
ImportanteConfirme se o comando de build foi modificado para
./instgo go build xxx, conforme especificado na documentação de integração.
Limitações
Não é possível usar extensões personalizadas para instrumentar funções definidas pelo usuário em
package main.Extensões personalizadas não suportam a instrumentação de funções cujo ReceiverType seja
any.
Especificações de código
Evite utilizar
package mainno seu código de hook.-
Para códigos da versão 2.0.0 ou posterior, adicione
"_ unsafe"à declaração de import, conforme abaixo:import ( "encoding/json" "fmt" "github.com/alibaba/loongsuite-go-agent/pkg/api" "net/http" _ "unsafe" ) -
O primeiro parâmetro da função OnEnter é sempre
call api.CallContext, o segundo é o ReceiverType (se houver) e os seguintes correspondem aos parâmetros de requisição da função inserida.NotaCaso o
ReceiverTypecontenha*, adicione o prefixo\\no arquivo JSON para escapar os caracteres. A função
OnExittem como primeiro parâmetro fixocall api.CallContext, enquanto os subsequentes representam os valores de retorno da função inserida, respeitando a ordem definida.
Por exemplo, a função net/http::(*Transport).RoundTrip apresenta a seguinte estrutura:
func (t *Transport) RoundTrip(req *Request) (*Response, error) {
return t.roundTrip(req)
}
Como o ReceiverType de RoundTrip é *Transport, o segundo parâmetro da função OnEnter será *Transport e o terceiro será req *Request. Já o segundo e o terceiro parâmetros da função OnExit correspondem aos valores de retorno de RoundTrip: *Response, error.
Se o ReceiverType for um tipo não exportado, utilize _ interface{} como substituto. Essa mesma abordagem vale para ignorar quaisquer parâmetros indesejados, podendo também usar _ interface{} como substituto.
Procedimento
-
Em um diretório fora do projeto atual, crie uma pasta chamada
rules, inicialize-a executando o comandogo mod init rulese, em seguida, crie um arquivo chamadorules.gonessa pasta com o código abaixo.Este código define a extensão personalizada.
package rules import ( "encoding/json" "fmt" "github.com/alibaba/opentelemetry-go-auto-instrumentation/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)) } -
Modifique o arquivo
config.jsone adicione o conteúdo a seguir para injetar o código de hook emnet/http::(*Transport).RoundTrip.[ { "ImportPath":"net/http", "Function":"RoundTrip", "OnEnter":"httpClientEnterHook1", "ReceiverType": "\\*Transport", "OnExit": "httpClientExitHook1", "Path": "/extension/rules" // Replace with the absolute path to the rules folder. } ] -
Crie uma demonstração de teste.
Fora da pasta
rules, crie um diretório para a aplicação de demonstração e use o comandogo mod init demopara inicializá-lo. Depois, crie um arquivonet_http.gocontendo o código abaixo dentro da pastademo.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() } -
Navegue até o diretório
demo. Utilize a ferramenta instgo para compilar e executar o programa.$ ./instgo set --rule=../config.json $ INSTGO_CACHE_DIR=./ ./instgo go build net_http.go # If you need to run on a Linux system CGO_ENABLED=0 GOOS=linux GOARCH=amd64 INSTGO_CACHE_DIR=./ ./instgo go build net_http.go $ ./net_httpA saída abaixo confirma que a extensão foi injetada com sucesso.
request header is {"Otelbuild":["true"]} response header is {"Content-Type":["application/x-gzip"],"Date":["Mon, 04 Nov 2024 09:10:48 GMT"],"Server":["bfe"]}Para consultar o exemplo de código completo, acesse nethttp.
NotaAlém de capturar cabeçalhos de requisição e resposta, as extensões personalizadas servem para outras finalidades, como detecção de injeção de SQL, criação de logs personalizados e acesso a parâmetros de requisição e resposta.