github.com/gogf/gf/v2@v2.7.4/net/ghttp/ghttp_middleware_tracing.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package ghttp
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  
    13  	"go.opentelemetry.io/otel"
    14  	"go.opentelemetry.io/otel/attribute"
    15  	"go.opentelemetry.io/otel/codes"
    16  	"go.opentelemetry.io/otel/propagation"
    17  	"go.opentelemetry.io/otel/trace"
    18  
    19  	"github.com/gogf/gf/v2"
    20  	"github.com/gogf/gf/v2/internal/httputil"
    21  	"github.com/gogf/gf/v2/net/gtrace"
    22  	"github.com/gogf/gf/v2/os/gctx"
    23  	"github.com/gogf/gf/v2/util/gconv"
    24  )
    25  
    26  const (
    27  	instrumentName                              = "github.com/gogf/gf/v2/net/ghttp.Server"
    28  	tracingEventHttpRequest                     = "http.request"
    29  	tracingEventHttpRequestHeaders              = "http.request.headers"
    30  	tracingEventHttpRequestBaggage              = "http.request.baggage"
    31  	tracingEventHttpResponse                    = "http.response"
    32  	tracingEventHttpResponseHeaders             = "http.response.headers"
    33  	tracingEventHttpRequestUrl                  = "http.request.url"
    34  	tracingMiddlewareHandled        gctx.StrKey = `MiddlewareServerTracingHandled`
    35  )
    36  
    37  // internalMiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry.
    38  func internalMiddlewareServerTracing(r *Request) {
    39  	var (
    40  		ctx = r.Context()
    41  	)
    42  	// Mark this request is handled by server tracing middleware,
    43  	// to avoid repeated handling by the same middleware.
    44  	if ctx.Value(tracingMiddlewareHandled) != nil {
    45  		r.Middleware.Next()
    46  		return
    47  	}
    48  
    49  	ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1)
    50  	var (
    51  		span trace.Span
    52  		tr   = otel.GetTracerProvider().Tracer(
    53  			instrumentName,
    54  			trace.WithInstrumentationVersion(gf.VERSION),
    55  		)
    56  	)
    57  	ctx, span = tr.Start(
    58  		otel.GetTextMapPropagator().Extract(
    59  			ctx,
    60  			propagation.HeaderCarrier(r.Header),
    61  		),
    62  		r.URL.Path,
    63  		trace.WithSpanKind(trace.SpanKindServer),
    64  	)
    65  	defer span.End()
    66  
    67  	span.SetAttributes(gtrace.CommonLabels()...)
    68  
    69  	// Inject tracing context.
    70  	r.SetCtx(ctx)
    71  
    72  	// If it is now using a default trace provider, it then does no complex tracing jobs.
    73  	if gtrace.IsUsingDefaultProvider() {
    74  		r.Middleware.Next()
    75  		return
    76  	}
    77  
    78  	span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
    79  		attribute.String(tracingEventHttpRequestUrl, r.URL.String()),
    80  		attribute.String(tracingEventHttpRequestHeaders, gconv.String(httputil.HeaderToMap(r.Header))),
    81  		attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx).String()),
    82  	))
    83  
    84  	// Continue executing.
    85  	r.Middleware.Next()
    86  
    87  	// parse after set route as span name
    88  	if handler := r.GetServeHandler(); handler != nil && handler.Handler.Router != nil {
    89  		span.SetName(handler.Handler.Router.Uri)
    90  	}
    91  
    92  	// Error logging.
    93  	if err := r.GetError(); err != nil {
    94  		span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
    95  	}
    96  
    97  	span.AddEvent(tracingEventHttpResponse, trace.WithAttributes(
    98  		attribute.String(
    99  			tracingEventHttpResponseHeaders,
   100  			gconv.String(httputil.HeaderToMap(r.Response.Header())),
   101  		),
   102  	))
   103  }