package unitel import ( "fmt" "net/http" "github.com/getsentry/sentry-go" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) const httpClientTransportClientID = libBase + "#HTTPTansport" type HTTPTransport struct { telemetry *Telemetry forwardTrace bool tracedRequestHeaders []string tracedResponseHeaders []string Transport http.RoundTripper tracer trace.Tracer } func (t *Telemetry) NewTracedTransport(transport http.RoundTripper, forwardTrace bool, tracedRequestHeaders []string, tracedResponseHeaders []string) *HTTPTransport { return &HTTPTransport{ telemetry: t, forwardTrace: forwardTrace, tracedRequestHeaders: tracedRequestHeaders, tracedResponseHeaders: tracedResponseHeaders, Transport: transport, tracer: t.tracerProvider.Tracer(httpClientTransportClientID, trace.WithInstrumentationVersion(libVersion)), } } func (t *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) { s := t.telemetry. StartSpan(req.Context(), "http.client", fmt.Sprintf("%s %s", req.Method, req.URL), WithOtelOptions(trace.WithSpanKind(trace.SpanKindClient))). AddAttributes( semconv.HTTPRequestMethodKey.String(req.Method), semconv.URLFull(req.URL.String()), ) defer s.End() ctx := s.Context() req = req.WithContext(ctx) if t.forwardTrace { t.telemetry.InjectIntoHeaders(ctx, req.Header) } for _, header := range t.tracedRequestHeaders { vv := req.Header.Values(header) for i, v := range vv { s.AddAttributes(attribute.String(formatHeaderAttribute("http.request", header, vv, i), v)) } } resp, err := t.Transport.RoundTrip(req) if err != nil { s.CaptureBreadcrumb(SeverityError, BreadcrumbTypeHTTP, BreadcrumbCatagoryHTTP, fmt.Sprintf("Failed to send request to %s: %v", req.URL.String(), err), map[string]any{ "url": req.URL.String(), "method": req.Method, }) if hub := sentry.GetHubFromContext(ctx); hub != nil { hub.CaptureException(err) } return resp, err } { severity := SeverityDebug if resp.StatusCode < http.StatusBadRequest { severity = SeverityInfo } else if resp.StatusCode < http.StatusInternalServerError { severity = SeverityError } else { severity = SeverityError } s.CaptureBreadcrumb(severity, BreadcrumbTypeHTTP, BreadcrumbCatagoryHTTP, req.URL.String(), map[string]any{ "url": req.URL.String(), "method": req.Method, "status": resp.StatusCode, }) } s. AddAttributes(semconv.HTTPResponseStatusCode(resp.StatusCode)). SetStatus(httpStatusToSpanStatus(resp.StatusCode, false), "") for _, header := range t.tracedResponseHeaders { vv := resp.Header.Values(header) for i, v := range vv { s.AddAttributes(attribute.String(formatHeaderAttribute("http.response", header, vv, i), v)) } } return resp, err }