From 3f2415963680631b43dbd6d455f63a331b49028b Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 10 Dec 2021 18:08:36 +0800 Subject: [PATCH] add tracing configuration switch for whole framework --- net/gclient/gclient.go | 15 ++++++-- net/gclient/gclient_tracing.go | 45 +++++++++++++++-------- net/ghttp/ghttp_middleware_tracing.go | 53 +++++++++++++++++---------- net/ghttp/ghttp_server.go | 5 +++ net/gtrace/gtrace.go | 27 ++++++++++---- 5 files changed, 99 insertions(+), 46 deletions(-) diff --git a/net/gclient/gclient.go b/net/gclient/gclient.go index 49a623253..5e9e411ae 100644 --- a/net/gclient/gclient.go +++ b/net/gclient/gclient.go @@ -16,9 +16,11 @@ import ( "net/http" "net/http/cookiejar" "net/url" + "os" "strings" "time" + "github.com/gogf/gf/v2/net/gtrace" "golang.org/x/net/proxy" "github.com/gogf/gf/v2" @@ -43,12 +45,13 @@ type Client struct { } var ( - defaultClientAgent = fmt.Sprintf(`GoFrameHTTPClient %s`, gf.VERSION) + host, _ = os.Hostname() + defaultClientAgent = fmt.Sprintf(`GClient %s at %s`, gf.VERSION, host) ) // New creates and returns a new HTTP client object. func New() *Client { - client := &Client{ + c := &Client{ Client: http.Client{ Transport: &http.Transport{ // No validation for https certification of the server in default. @@ -61,8 +64,12 @@ func New() *Client { header: make(map[string]string), cookies: make(map[string]string), } - client.header["User-Agent"] = defaultClientAgent - return client + c.header["User-Agent"] = defaultClientAgent + // It enables OpenTelemetry for client if tracing feature is enabled. + if gtrace.IsEnabled() { + c.Use(MiddlewareTracing) + } + return c } // Clone deeply clones current client and returns a new one. diff --git a/net/gclient/gclient_tracing.go b/net/gclient/gclient_tracing.go index 8dc3e4256..ae2cbcae3 100644 --- a/net/gclient/gclient_tracing.go +++ b/net/gclient/gclient_tracing.go @@ -7,11 +7,13 @@ package gclient import ( + "context" "fmt" "io/ioutil" "net/http" "net/http/httptrace" + "github.com/gogf/gf/v2/os/gctx" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -27,25 +29,38 @@ import ( ) const ( - tracingInstrumentName = "github.com/gogf/gf/v2/net/gclient.Client" - tracingAttrHttpAddressRemote = "http.address.remote" - tracingAttrHttpAddressLocal = "http.address.local" - tracingAttrHttpDnsStart = "http.dns.start" - tracingAttrHttpDnsDone = "http.dns.done" - tracingAttrHttpConnectStart = "http.connect.start" - tracingAttrHttpConnectDone = "http.connect.done" - tracingEventHttpRequest = "http.request" - tracingEventHttpRequestHeaders = "http.request.headers" - tracingEventHttpRequestBaggage = "http.request.baggage" - tracingEventHttpRequestBody = "http.request.body" - tracingEventHttpResponse = "http.response" - tracingEventHttpResponseHeaders = "http.response.headers" - tracingEventHttpResponseBody = "http.response.body" + tracingInstrumentName = "github.com/gogf/gf/v2/net/gclient.Client" + tracingAttrHttpAddressRemote = "http.address.remote" + tracingAttrHttpAddressLocal = "http.address.local" + tracingAttrHttpDnsStart = "http.dns.start" + tracingAttrHttpDnsDone = "http.dns.done" + tracingAttrHttpConnectStart = "http.connect.start" + tracingAttrHttpConnectDone = "http.connect.done" + tracingEventHttpRequest = "http.request" + tracingEventHttpRequestHeaders = "http.request.headers" + tracingEventHttpRequestBaggage = "http.request.baggage" + tracingEventHttpRequestBody = "http.request.body" + tracingEventHttpResponse = "http.response" + tracingEventHttpResponseHeaders = "http.response.headers" + tracingEventHttpResponseBody = "http.response.body" + tracingMiddlewareHandled gctx.StrKey = `MiddlewareClientTracingHandled` ) // MiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) { - tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) + var ( + ctx = r.Context() + ) + // Mark this request is handled by server tracing middleware. + if ctx.Value(tracingMiddlewareHandled) != nil { + return c.Next(r) + } + + ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1) + tr := otel.GetTracerProvider().Tracer( + tracingInstrumentName, + trace.WithInstrumentationVersion(gf.VERSION), + ) ctx, span := tr.Start(r.Context(), r.URL.String(), trace.WithSpanKind(trace.SpanKindClient)) defer span.End() diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index d74aea77a..6f2913447 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -7,10 +7,11 @@ package ghttp import ( + "context" "fmt" "io/ioutil" - "net/http" + "github.com/gogf/gf/v2/os/gctx" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -20,38 +21,50 @@ import ( "github.com/gogf/gf/v2" "github.com/gogf/gf/v2/internal/httputil" "github.com/gogf/gf/v2/internal/utils" - "github.com/gogf/gf/v2/net/gclient" "github.com/gogf/gf/v2/net/gtrace" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" ) const ( - tracingInstrumentName = "github.com/gogf/gf/v2/net/ghttp.Server" - tracingEventHttpRequest = "http.request" - tracingEventHttpRequestHeaders = "http.request.headers" - tracingEventHttpRequestBaggage = "http.request.baggage" - tracingEventHttpRequestBody = "http.request.body" - tracingEventHttpResponse = "http.response" - tracingEventHttpResponseHeaders = "http.response.headers" - tracingEventHttpResponseBody = "http.response.body" + tracingInstrumentName = "github.com/gogf/gf/v2/net/ghttp.Server" + tracingEventHttpRequest = "http.request" + tracingEventHttpRequestHeaders = "http.request.headers" + tracingEventHttpRequestBaggage = "http.request.baggage" + tracingEventHttpRequestBody = "http.request.body" + tracingEventHttpResponse = "http.response" + tracingEventHttpResponseHeaders = "http.response.headers" + tracingEventHttpResponseBody = "http.response.body" + tracingMiddlewareHandled gctx.StrKey = `MiddlewareServerTracingHandled` ) -// MiddlewareClientTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. -func MiddlewareClientTracing(c *gclient.Client, r *http.Request) (*gclient.Response, error) { - return gclient.MiddlewareTracing(c, r) -} - // MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareServerTracing(r *Request) { var ( - tr = otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) - ctx, span = tr.Start( - otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)), - r.URL.String(), - trace.WithSpanKind(trace.SpanKindServer), + ctx = r.Context() + ) + // Mark this request is handled by server tracing middleware. + if ctx.Value(tracingMiddlewareHandled) != nil { + r.Middleware.Next() + return + } + + ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1) + var ( + span trace.Span + tr = otel.GetTracerProvider().Tracer( + tracingInstrumentName, + trace.WithInstrumentationVersion(gf.VERSION), ) ) + ctx, span = tr.Start( + otel.GetTextMapPropagator().Extract( + ctx, + propagation.HeaderCarrier(r.Header), + ), + r.URL.String(), + trace.WithSpanKind(trace.SpanKindServer), + ) defer span.End() span.SetAttributes(gtrace.CommonLabels()...) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 7738d9a85..4887ba8d1 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -16,6 +16,7 @@ import ( "strings" "time" + "github.com/gogf/gf/v2/net/gtrace" "github.com/olekukonko/tablewriter" "github.com/gogf/gf/v2/container/garray" @@ -115,6 +116,10 @@ func GetServer(name ...interface{}) *Server { } // Record the server to internal server mapping by name. serverMapping.Set(serverName, s) + // It enables OpenTelemetry for server if tracing feature is enabled. + if gtrace.IsEnabled() { + s.Use(MiddlewareServerTracing) + } return s } diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index afcc505d3..443e4adbd 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -28,14 +28,16 @@ import ( const ( tracingCommonKeyIpIntranet = `ip.intranet` tracingCommonKeyIpHostname = `hostname` - commandEnvKeyForMaxContentLogSize = "gf.gtrace.maxcontentlogsize" - commandEnvKeyForTracingInternal = "gf.gtrace.tracinginternal" + commandEnvKeyForTraceEnabled = "gf.trace.enabled" // Main switch for tracing feature. + commandEnvKeyForMaxContentLogSize = "gf.gtrace.max.content.log.size" // To avoid too big tracing content. + commandEnvKeyForTracingInternal = "gf.gtrace.tracing.internal" // For detailed controlling for tracing content. ) var ( intranetIps, _ = gipv4.GetIntranetIpArray() intranetIpStr = strings.Join(intranetIps, ",") hostname, _ = os.Hostname() + traceEnabled = false // traceEnabled enables tracing feature for all. tracingInternal = true // tracingInternal enables tracing for internal type spans. tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body, especially for HTTP/RPC request. // defaultTextMapPropagator is the default propagator for context propagation between peers. @@ -46,6 +48,7 @@ var ( ) func init() { + traceEnabled = gconv.Bool(command.GetOptWithEnv(commandEnvKeyForTraceEnabled, "false")) tracingInternal = gconv.Bool(command.GetOptWithEnv(commandEnvKeyForTracingInternal, "true")) if maxContentLogSize := gconv.Int(command.GetOptWithEnv(commandEnvKeyForMaxContentLogSize)); maxContentLogSize > 0 { tracingMaxContentLogSize = maxContentLogSize @@ -53,6 +56,21 @@ func init() { CheckSetDefaultTextMapPropagator() } +// SetEnabled enables or disables the tracing feature. +func SetEnabled(enabled bool) { + traceEnabled = enabled +} + +// IsEnabled checks and returns if tracing feature is configured enabled. +func IsEnabled() bool { + return traceEnabled +} + +// IsActivated checks given context and returns if tracing feature is actually activated in this context. +func IsActivated(ctx context.Context) bool { + return GetTraceID(ctx) != "" +} + // IsTracingInternal returns whether tracing spans of internal components. func IsTracingInternal() bool { return tracingInternal @@ -73,11 +91,6 @@ func CommonLabels() []attribute.KeyValue { } } -// IsActivated checks and returns if tracing feature is activated. -func IsActivated(ctx context.Context) bool { - return GetTraceID(ctx) != "" -} - // CheckSetDefaultTextMapPropagator sets the default TextMapPropagator if it is not set previously. func CheckSetDefaultTextMapPropagator() { p := otel.GetTextMapPropagator()