github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/observability/tracing/http/http.go (about) 1 // Copyright 2023 Gravitational, Inc 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package http 16 17 import ( 18 "net/http" 19 20 "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 21 "go.opentelemetry.io/otel" 22 metricnoop "go.opentelemetry.io/otel/metric/noop" 23 ) 24 25 func init() { 26 // There's a memory leak in otelhttp caused by the default global 27 // delegating meter provider. Here we set the no op meter provider to 28 // avoid this. 29 // 30 // See the upstream issue for more: 31 // https://github.com/open-telemetry/opentelemetry-go-contrib/issues/5190 32 otel.SetMeterProvider(metricnoop.MeterProvider{}) 33 } 34 35 // TransportFormatter is a span formatter that may be provided to 36 // [otelhttp.WithSpanNameFormatter] to include the url path in the span 37 // names generated by an [otelhttp.Transport]. 38 func TransportFormatter(_ string, r *http.Request) string { 39 return "HTTP " + r.Method + " " + r.URL.Path 40 } 41 42 // HandlerFormatter is a span formatter that may be provided to 43 // [otelhttp.WithSpanNameFormatter] to include the component and url path in the span 44 // names generated by [otelhttp.NewHandler]. 45 func HandlerFormatter(operation string, r *http.Request) string { 46 return operation + " " + r.Method + " " + r.URL.Path 47 } 48 49 // NewTransport wraps the provided [http.RoundTripper] with one 50 // that automatically adds spans for each http request. 51 // 52 // Note: special care has been taken to ensure that the returned 53 // [http.RoundTripper] has a CloseIdleConnections method because 54 // the [otelhttp.Transport] does not implement it: 55 // https://github.com/open-telemetry/opentelemetry-go-contrib/issues/3543. 56 // Once the issue is resolved the wrapper may be discarded. 57 func NewTransport(rt http.RoundTripper) http.RoundTripper { 58 return NewTransportWithInner(rt, rt) 59 } 60 61 // NewTransportWithInner wraps the provided [http.RoundTripper] with one 62 // that automatically adds spans for each http request. 63 // The inner round tripper is used to close idle connections when 64 // rt.CloseIdleConnections isn't implemented for the rt provided. 65 // 66 // Note: special care has been taken to ensure that the returned 67 // [http.RoundTripper] has a CloseIdleConnections method because 68 // the [otelhttp.Transport] does not implement it: 69 // https://github.com/open-telemetry/opentelemetry-go-contrib/issues/3543. 70 // Once the issue is resolved the wrapper may be discarded. 71 func NewTransportWithInner(rt http.RoundTripper, inner http.RoundTripper) http.RoundTripper { 72 return &roundTripWrapper{ 73 RoundTripper: otelhttp.NewTransport(rt, otelhttp.WithSpanNameFormatter(TransportFormatter)), 74 inner: inner, 75 } 76 } 77 78 type closeIdler interface { 79 CloseIdleConnections() 80 } 81 82 type roundTripWrapper struct { 83 http.RoundTripper 84 inner http.RoundTripper 85 } 86 87 // Unwrap returns the inner round tripper. 88 func (w *roundTripWrapper) Unwrap() http.RoundTripper { 89 return w.inner 90 } 91 92 // CloseIdleConnections ensures that the returned [http.RoundTripper] 93 // has a CloseIdleConnections method. Since [otelhttp.Transport] does not implement 94 // this any [http.Client.CloseIdleConnections] calls result in a noop instead 95 // of forwarding the request onto its wrapped [http.RoundTripper]. 96 // 97 // DELETE WHEN https://github.com/open-telemetry/opentelemetry-go-contrib/issues/3543 98 // has been resolved. 99 func (w *roundTripWrapper) CloseIdleConnections() { 100 if c, ok := w.RoundTripper.(closeIdler); ok { 101 c.CloseIdleConnections() 102 } else if c, ok := w.inner.(closeIdler); ok { 103 c.CloseIdleConnections() 104 } 105 }