diff --git a/go.sum b/go.sum index 206988ab14312..e51cafaafefec 100644 --- a/go.sum +++ b/go.sum @@ -561,8 +561,6 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8= -golang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA= golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -641,7 +639,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32 h1:Js08h5hqB5xyWR789+QqueR6sDE8mk+YvpETZ+F6X9Y= golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -773,8 +770,6 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731 h1:nquqdM9+ps0JZcIiI70+tqoaIFS5Ql4ZuK8UXnz3HfE= -google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 h1:qRu95HZ148xXw+XeZ3dvqe85PxH4X8+jIo0iRPKcEnM= google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -793,8 +788,6 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= diff --git a/rest/httpc/requests.go b/rest/httpc/requests.go index b6c9e86196eb1..25dc71fe74f75 100644 --- a/rest/httpc/requests.go +++ b/rest/httpc/requests.go @@ -7,18 +7,30 @@ import ( "fmt" "io" "net/http" + "net/http/httptrace" nurl "net/url" "strings" "github.com/zeromicro/go-zero/core/lang" "github.com/zeromicro/go-zero/core/mapping" + "github.com/zeromicro/go-zero/core/trace" "github.com/zeromicro/go-zero/rest/httpc/internal" "github.com/zeromicro/go-zero/rest/internal/header" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/propagation" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + oteltrace "go.opentelemetry.io/otel/trace" ) -var interceptors = []internal.Interceptor{ - internal.LogInterceptor, -} +var ( + // WithClientHttpTrace is an alias for httptrace.WithClientTrace + WithClientHttpTrace = httptrace.WithClientTrace + + interceptors = []internal.Interceptor{ + internal.LogInterceptor, + } +) // Do sends an HTTP request with the given arguments and returns an HTTP response. // data is automatically marshal into a *httpRequest, typically it's defined in an API file. @@ -149,18 +161,48 @@ func fillPath(u *nurl.URL, val map[string]interface{}) error { return nil } -func request(r *http.Request, cli client) (*http.Response, error) { - var respHandlers []internal.ResponseHandler - for _, interceptor := range interceptors { +func request(r *http.Request, cli client) (resp *http.Response, err error) { + tracer := otel.GetTracerProvider().Tracer(trace.TraceName) + propagator := otel.GetTextMapPropagator() + + spanName := r.URL.Path + ctx, span := tracer.Start( + r.Context(), + spanName, + oteltrace.WithSpanKind(oteltrace.SpanKindClient), + oteltrace.WithAttributes(semconv.HTTPClientAttributesFromHTTPRequest(r)...), + ) + defer span.End() + + respHandlers := make([]internal.ResponseHandler, len(interceptors)) + for i, interceptor := range interceptors { var h internal.ResponseHandler r, h = interceptor(r) - respHandlers = append(respHandlers, h) + respHandlers[i] = h + } + + clientTrace := httptrace.ContextClientTrace(ctx) + if clientTrace != nil { + ctx = httptrace.WithClientTrace(ctx, clientTrace) } - resp, err := cli.do(r) + r = r.WithContext(ctx) + span.SetAttributes(semconv.HTTPClientAttributesFromHTTPRequest(r)...) + propagator.Inject(ctx, propagation.HeaderCarrier(r.Header)) + + resp, err = cli.do(r) for i := len(respHandlers) - 1; i >= 0; i-- { respHandlers[i](resp, err) } + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return resp, err + } + + span.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(resp.StatusCode)...) + span.SetStatus(semconv.SpanStatusFromHTTPStatusCode(resp.StatusCode)) + return resp, err } diff --git a/rest/httpc/requests_test.go b/rest/httpc/requests_test.go index c6160c4855f8a..55492fe68fcf3 100644 --- a/rest/httpc/requests_test.go +++ b/rest/httpc/requests_test.go @@ -4,15 +4,25 @@ import ( "context" "net/http" "net/http/httptest" + "net/http/httptrace" "testing" "github.com/stretchr/testify/assert" + ztrace "github.com/zeromicro/go-zero/core/trace" "github.com/zeromicro/go-zero/rest/httpx" "github.com/zeromicro/go-zero/rest/internal/header" "github.com/zeromicro/go-zero/rest/router" + "go.opentelemetry.io/otel/trace" ) func TestDoRequest(t *testing.T) { + ztrace.StartAgent(ztrace.Config{ + Name: "go-zero-test", + Endpoint: "http://localhost:14268/api/traces", + Batcher: "jaeger", + Sampler: 1.0, + }) + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { })) defer svr.Close() @@ -21,6 +31,8 @@ func TestDoRequest(t *testing.T) { resp, err := DoRequest(req) assert.Nil(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) + spanContext := trace.SpanContextFromContext(resp.Request.Context()) + assert.True(t, spanContext.IsValid()) } func TestDoRequest_NotFound(t *testing.T) { @@ -113,7 +125,7 @@ func TestDo_BadRequest(t *testing.T) { assert.NotNil(t, err) val1 := struct { - Value string `json:"value,options=[a,b]"` + Value string `json:"value,requestOptions=[a,b]"` }{ Value: "c", } @@ -187,3 +199,17 @@ func TestDo_Json(t *testing.T) { _, err = Do(context.Background(), http.MethodPost, svr.URL+"/nodes/:key", data) assert.NotNil(t, err) } + +func TestRequestWithClientHttpTrace(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + defer svr.Close() + + _, err := Do(WithClientHttpTrace(context.Background(), + &httptrace.ClientTrace{ + DNSStart: func(info httptrace.DNSStartInfo) { + assert.Equal(t, "localhost", info.Host) + }, + }), http.MethodGet, svr.URL, nil) + assert.Nil(t, err) + +} diff --git a/rest/httpc/service_test.go b/rest/httpc/service_test.go index f4a32efd0a231..1eb0738a58f15 100644 --- a/rest/httpc/service_test.go +++ b/rest/httpc/service_test.go @@ -75,7 +75,7 @@ func TestNamedService_Do(t *testing.T) { func TestNamedService_DoBadRequest(t *testing.T) { val := struct { - Value string `json:"value,options=[a,b]"` + Value string `json:"value,requestOptions=[a,b]"` }{ Value: "c", }