From 280e837c9e7fe218e11723e1c9146c3867f7b838 Mon Sep 17 00:00:00 2001 From: zhoushuguang <15951703783@163.com> Date: Tue, 24 Aug 2021 10:04:12 +0800 Subject: [PATCH] rest otel support (#943) --- core/opentelemetry/agent.go | 11 +++++ core/opentelemetry/config.go | 4 +- core/service/serviceconf.go | 3 ++ rest/engine.go | 1 + rest/handler/otelhandler.go | 36 ++++++++++++++ rest/handler/otelhandler_test.go | 49 +++++++++++++++++++ .../opentracinginterceptor.go | 1 + .../opentracinginterceptor.go | 1 + 8 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 rest/handler/otelhandler.go create mode 100644 rest/handler/otelhandler_test.go diff --git a/core/opentelemetry/agent.go b/core/opentelemetry/agent.go index 1197bd62..3b230ea7 100644 --- a/core/opentelemetry/agent.go +++ b/core/opentelemetry/agent.go @@ -5,6 +5,7 @@ import ( "github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/syncx" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/propagation" @@ -53,7 +54,17 @@ func StartAgent(c Config) { otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) + otel.SetErrorHandler(otelErrHandler{}) enabled.Set(true) }) } + +// errHandler handing otel errors. +type otelErrHandler struct{} + +var _ otel.ErrorHandler = otelErrHandler{} + +func (o otelErrHandler) Handle(err error) { + logx.Errorf("[otel] error: %v", err) +} diff --git a/core/opentelemetry/config.go b/core/opentelemetry/config.go index 2d5dbef4..a34d79fc 100644 --- a/core/opentelemetry/config.go +++ b/core/opentelemetry/config.go @@ -1,8 +1,6 @@ package opentelemetry -const ( - TraceName = "go-zero" -) +const TraceName = "go-zero" // A Config is a opentelemetry config. type Config struct { diff --git a/core/service/serviceconf.go b/core/service/serviceconf.go index d31dba20..32a6bd94 100644 --- a/core/service/serviceconf.go +++ b/core/service/serviceconf.go @@ -45,6 +45,9 @@ func (sc ServiceConf) SetUp() error { if len(sc.Log.ServiceName) == 0 { sc.Log.ServiceName = sc.Name } + if len(sc.OpenTelemetry.Name) == 0 { + sc.OpenTelemetry.Name = sc.Name + } if err := logx.SetUp(sc.Log); err != nil { return err } diff --git a/rest/engine.go b/rest/engine.go index 40ee5c1b..5f92d99b 100644 --- a/rest/engine.go +++ b/rest/engine.go @@ -108,6 +108,7 @@ func (s *engine) bindRoute(fr featuredRoutes, router httpx.Router, metrics *stat route Route, verifier func(chain alice.Chain) alice.Chain) error { chain := alice.New( handler.TracingHandler, + handler.OtelHandler(route.Path), s.getLogHandler(), handler.PrometheusHandler(route.Path), handler.MaxConns(s.conf.MaxConns), diff --git a/rest/handler/otelhandler.go b/rest/handler/otelhandler.go new file mode 100644 index 00000000..01188b52 --- /dev/null +++ b/rest/handler/otelhandler.go @@ -0,0 +1,36 @@ +package handler + +import ( + "net/http" + + "github.com/tal-tech/go-zero/core/opentelemetry" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + oteltrace "go.opentelemetry.io/otel/trace" +) + +func OtelHandler(path string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + if !opentelemetry.Enabled() { + return next + } + + propagator := otel.GetTextMapPropagator() + tracer := otel.GetTracerProvider().Tracer(opentelemetry.TraceName) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header)) + spanCtx, span := tracer.Start( + ctx, + path, + oteltrace.WithSpanKind(oteltrace.SpanKindServer), + oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest("", path, r)...), + ) + defer span.End() + + next.ServeHTTP(w, r.WithContext(spanCtx)) + }) + } +} diff --git a/rest/handler/otelhandler_test.go b/rest/handler/otelhandler_test.go new file mode 100644 index 00000000..9d245096 --- /dev/null +++ b/rest/handler/otelhandler_test.go @@ -0,0 +1,49 @@ +package handler + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/core/opentelemetry" + "go.opentelemetry.io/otel/propagation" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" +) + +func TestOtelHandler(t *testing.T) { + opentelemetry.StartAgent(opentelemetry.Config{ + Name: "go-zero-test", + Endpoint: "http://localhost:14268/api/traces", + Batcher: "jaeger", + Sampler: 1.0, + }) + + ts := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)) + spanCtx := trace.SpanContextFromContext(ctx) + assert.Equal(t, true, spanCtx.IsValid()) + }), + ) + defer ts.Close() + + client := ts.Client() + err := func(ctx context.Context) error { + ctx, span := otel.Tracer("httptrace/client").Start(ctx, "test") + defer span.End() + + req, _ := http.NewRequest("GET", ts.URL, nil) + otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header)) + + res, err := client.Do(req) + assert.Equal(t, err, nil) + _ = res.Body.Close() + return nil + }(context.Background()) + + assert.Equal(t, err, nil) +} diff --git a/zrpc/internal/clientinterceptors/opentracinginterceptor.go b/zrpc/internal/clientinterceptors/opentracinginterceptor.go index 8311f799..aa767983 100644 --- a/zrpc/internal/clientinterceptors/opentracinginterceptor.go +++ b/zrpc/internal/clientinterceptors/opentracinginterceptor.go @@ -4,6 +4,7 @@ import ( "context" "github.com/tal-tech/go-zero/core/opentelemetry" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" diff --git a/zrpc/internal/serverinterceptors/opentracinginterceptor.go b/zrpc/internal/serverinterceptors/opentracinginterceptor.go index 76e0d921..c81946d3 100644 --- a/zrpc/internal/serverinterceptors/opentracinginterceptor.go +++ b/zrpc/internal/serverinterceptors/opentracinginterceptor.go @@ -4,6 +4,7 @@ import ( "context" "github.com/tal-tech/go-zero/core/opentelemetry" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/codes"