github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/gf.
     6  
     7  package ghttp
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"io"
    13  
    14  	"go.opentelemetry.io/otel"
    15  	"go.opentelemetry.io/otel/attribute"
    16  	"go.opentelemetry.io/otel/codes"
    17  	"go.opentelemetry.io/otel/propagation"
    18  	"go.opentelemetry.io/otel/trace"
    19  
    20  	"github.com/wangyougui/gf/v2"
    21  	"github.com/wangyougui/gf/v2/errors/gerror"
    22  	"github.com/wangyougui/gf/v2/internal/httputil"
    23  	"github.com/wangyougui/gf/v2/internal/utils"
    24  	"github.com/wangyougui/gf/v2/net/gtrace"
    25  	"github.com/wangyougui/gf/v2/os/gctx"
    26  	"github.com/wangyougui/gf/v2/util/gconv"
    27  )
    28  
    29  const (
    30  	tracingInstrumentName                       = "github.com/wangyougui/gf/v2/net/ghttp.Server"
    31  	tracingEventHttpRequest                     = "http.request"
    32  	tracingEventHttpRequestHeaders              = "http.request.headers"
    33  	tracingEventHttpRequestBaggage              = "http.request.baggage"
    34  	tracingEventHttpRequestBody                 = "http.request.body"
    35  	tracingEventHttpResponse                    = "http.response"
    36  	tracingEventHttpResponseHeaders             = "http.response.headers"
    37  	tracingEventHttpResponseBody                = "http.response.body"
    38  	tracingEventHttpRequestUrl                  = "http.request.url"
    39  	tracingMiddlewareHandled        gctx.StrKey = `MiddlewareServerTracingHandled`
    40  )
    41  
    42  // internalMiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry.
    43  func internalMiddlewareServerTracing(r *Request) {
    44  	var (
    45  		ctx = r.Context()
    46  	)
    47  	// Mark this request is handled by server tracing middleware,
    48  	// to avoid repeated handling by the same middleware.
    49  	if ctx.Value(tracingMiddlewareHandled) != nil {
    50  		r.Middleware.Next()
    51  		return
    52  	}
    53  
    54  	ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1)
    55  	var (
    56  		span trace.Span
    57  		tr   = otel.GetTracerProvider().Tracer(
    58  			tracingInstrumentName,
    59  			trace.WithInstrumentationVersion(gf.VERSION),
    60  		)
    61  	)
    62  	ctx, span = tr.Start(
    63  		otel.GetTextMapPropagator().Extract(
    64  			ctx,
    65  			propagation.HeaderCarrier(r.Header),
    66  		),
    67  		r.URL.Path,
    68  		trace.WithSpanKind(trace.SpanKindServer),
    69  	)
    70  	defer span.End()
    71  
    72  	span.SetAttributes(gtrace.CommonLabels()...)
    73  
    74  	// Inject tracing context.
    75  	r.SetCtx(ctx)
    76  
    77  	// If it is now using a default trace provider, it then does no complex tracing jobs.
    78  	if gtrace.IsUsingDefaultProvider() {
    79  		r.Middleware.Next()
    80  		return
    81  	}
    82  
    83  	// Request content logging.
    84  	reqBodyContentBytes, err := io.ReadAll(r.Body)
    85  	if err != nil {
    86  		r.SetError(gerror.Wrap(err, `read request body failed`))
    87  		span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
    88  		return
    89  	}
    90  	r.Body = utils.NewReadCloser(reqBodyContentBytes, false)
    91  	reqBodyContent, err := gtrace.SafeContentForHttp(reqBodyContentBytes, r.Header)
    92  	if err != nil {
    93  		span.SetStatus(codes.Error, fmt.Sprintf(`converting safe content failed: %s`, err.Error()))
    94  	}
    95  
    96  	span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
    97  		attribute.String(tracingEventHttpRequestUrl, r.URL.String()),
    98  		attribute.String(tracingEventHttpRequestHeaders, gconv.String(httputil.HeaderToMap(r.Header))),
    99  		attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx).String()),
   100  		attribute.String(tracingEventHttpRequestBody, reqBodyContent),
   101  	))
   102  
   103  	// Continue executing.
   104  	r.Middleware.Next()
   105  
   106  	// parse after set route as span name
   107  	if r.Router.Uri != defaultMiddlewarePattern || r.Router.RegNames != nil {
   108  		span.SetName(r.Router.Uri)
   109  	}
   110  
   111  	// Error logging.
   112  	if err = r.GetError(); err != nil {
   113  		span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
   114  	}
   115  
   116  	// Response content logging.
   117  	resBodyContent, err := gtrace.SafeContentForHttp(r.Response.Buffer(), r.Response.Header())
   118  	if err != nil {
   119  		span.SetStatus(codes.Error, fmt.Sprintf(`converting safe content failed: %s`, err.Error()))
   120  	}
   121  
   122  	span.AddEvent(tracingEventHttpResponse, trace.WithAttributes(
   123  		attribute.String(tracingEventHttpResponseHeaders, gconv.String(httputil.HeaderToMap(r.Response.Header()))),
   124  		attribute.String(tracingEventHttpResponseBody, resBodyContent),
   125  	))
   126  }