Merge pull request #9480 from dmcgowan/fix-otel-http
Fix otel version incompatibility
This commit is contained in:
commit
148d21b1ae
2
go.mod
2
go.mod
@ -58,6 +58,7 @@ require (
|
|||||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||||
go.etcd.io/bbolt v1.3.7
|
go.etcd.io/bbolt v1.3.7
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0
|
||||||
go.opentelemetry.io/otel v1.19.0
|
go.opentelemetry.io/otel v1.19.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
|
||||||
@ -87,6 +88,7 @@ require (
|
|||||||
github.com/containerd/containerd v1.7.8 // indirect
|
github.com/containerd/containerd v1.7.8 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
|
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||||
github.com/go-logr/logr v1.3.0 // indirect
|
github.com/go-logr/logr v1.3.0 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -253,6 +253,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||||||
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
|
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
|
||||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||||
|
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
@ -695,6 +697,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
|||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q=
|
||||||
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
|
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
|
||||||
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
|
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||||
|
@ -595,18 +595,13 @@ func (r *request) do(ctx context.Context) (*http.Response, error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, httpSpan := tracing.StartSpan(
|
|
||||||
ctx,
|
tracing.UpdateHTTPClient(client, tracing.Name("remotes.docker.resolver", "HTTPRequest"))
|
||||||
tracing.Name("remotes.docker.resolver", "HTTPRequest"),
|
|
||||||
tracing.WithHTTPRequest(req),
|
|
||||||
)
|
|
||||||
defer httpSpan.End()
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpSpan.SetStatus(err)
|
|
||||||
return nil, fmt.Errorf("failed to do request: %w", err)
|
return nil, fmt.Errorf("failed to do request: %w", err)
|
||||||
}
|
}
|
||||||
httpSpan.SetAttributes(tracing.HTTPStatusCodeAttributes(resp.StatusCode)...)
|
|
||||||
log.G(ctx).WithFields(responseFields(resp)).Debug("fetch response received")
|
log.G(ctx).WithFields(responseFields(resp)).Debug("fetch response received")
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ import (
|
|||||||
"go.opentelemetry.io/otel/propagation"
|
"go.opentelemetry.io/otel/propagation"
|
||||||
"go.opentelemetry.io/otel/sdk/resource"
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
"go.opentelemetry.io/otel/sdk/trace"
|
"go.opentelemetry.io/otel/sdk/trace"
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
const exporterPlugin = "otlp"
|
const exporterPlugin = "otlp"
|
||||||
|
@ -20,11 +20,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||||
httpconv "go.opentelemetry.io/otel/semconv/v1.17.0/httpconv"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,15 +35,14 @@ type StartConfig struct {
|
|||||||
|
|
||||||
type SpanOpt func(config *StartConfig)
|
type SpanOpt func(config *StartConfig)
|
||||||
|
|
||||||
// WithHTTPRequest marks span as a HTTP request operation from client to server.
|
// UpdateHTTPClient updates the http client with the necessary otel transport
|
||||||
// It'll append attributes from the HTTP request object and mark it with `SpanKindClient` type.
|
func UpdateHTTPClient(client *http.Client, name string) {
|
||||||
func WithHTTPRequest(request *http.Request) SpanOpt {
|
client.Transport = otelhttp.NewTransport(
|
||||||
return func(config *StartConfig) {
|
client.Transport,
|
||||||
config.spanOpts = append(config.spanOpts,
|
otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
|
||||||
trace.WithSpanKind(trace.SpanKindClient), // A client making a request to a server
|
return name
|
||||||
trace.WithAttributes(httpconv.ClientRequest(request)...), // Add HTTP attributes
|
}),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartSpan starts child span in a context.
|
// StartSpan starts child span in a context.
|
||||||
|
0
vendor/github.com/felixge/httpsnoop/.gitignore
generated
vendored
Normal file
0
vendor/github.com/felixge/httpsnoop/.gitignore
generated
vendored
Normal file
6
vendor/github.com/felixge/httpsnoop/.travis.yml
generated
vendored
Normal file
6
vendor/github.com/felixge/httpsnoop/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.6
|
||||||
|
- 1.7
|
||||||
|
- 1.8
|
19
vendor/github.com/felixge/httpsnoop/LICENSE.txt
generated
vendored
Normal file
19
vendor/github.com/felixge/httpsnoop/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2016 Felix Geisendörfer (felix@debuggable.com)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
10
vendor/github.com/felixge/httpsnoop/Makefile
generated
vendored
Normal file
10
vendor/github.com/felixge/httpsnoop/Makefile
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.PHONY: ci generate clean
|
||||||
|
|
||||||
|
ci: clean generate
|
||||||
|
go test -v ./...
|
||||||
|
|
||||||
|
generate:
|
||||||
|
go generate .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *_generated*.go
|
95
vendor/github.com/felixge/httpsnoop/README.md
generated
vendored
Normal file
95
vendor/github.com/felixge/httpsnoop/README.md
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# httpsnoop
|
||||||
|
|
||||||
|
Package httpsnoop provides an easy way to capture http related metrics (i.e.
|
||||||
|
response time, bytes written, and http status code) from your application's
|
||||||
|
http.Handlers.
|
||||||
|
|
||||||
|
Doing this requires non-trivial wrapping of the http.ResponseWriter interface,
|
||||||
|
which is also exposed for users interested in a more low-level API.
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/felixge/httpsnoop)
|
||||||
|
[](https://travis-ci.org/felixge/httpsnoop)
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
// myH is your app's http handler, perhaps a http.ServeMux or similar.
|
||||||
|
var myH http.Handler
|
||||||
|
// wrappedH wraps myH in order to log every request.
|
||||||
|
wrappedH := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
m := httpsnoop.CaptureMetrics(myH, w, r)
|
||||||
|
log.Printf(
|
||||||
|
"%s %s (code=%d dt=%s written=%d)",
|
||||||
|
r.Method,
|
||||||
|
r.URL,
|
||||||
|
m.Code,
|
||||||
|
m.Duration,
|
||||||
|
m.Written,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
http.ListenAndServe(":8080", wrappedH)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why this package exists
|
||||||
|
|
||||||
|
Instrumenting an application's http.Handler is surprisingly difficult.
|
||||||
|
|
||||||
|
However if you google for e.g. "capture ResponseWriter status code" you'll find
|
||||||
|
lots of advise and code examples that suggest it to be a fairly trivial
|
||||||
|
undertaking. Unfortunately everything I've seen so far has a high chance of
|
||||||
|
breaking your application.
|
||||||
|
|
||||||
|
The main problem is that a `http.ResponseWriter` often implements additional
|
||||||
|
interfaces such as `http.Flusher`, `http.CloseNotifier`, `http.Hijacker`, `http.Pusher`, and
|
||||||
|
`io.ReaderFrom`. So the naive approach of just wrapping `http.ResponseWriter`
|
||||||
|
in your own struct that also implements the `http.ResponseWriter` interface
|
||||||
|
will hide the additional interfaces mentioned above. This has a high change of
|
||||||
|
introducing subtle bugs into any non-trivial application.
|
||||||
|
|
||||||
|
Another approach I've seen people take is to return a struct that implements
|
||||||
|
all of the interfaces above. However, that's also problematic, because it's
|
||||||
|
difficult to fake some of these interfaces behaviors when the underlying
|
||||||
|
`http.ResponseWriter` doesn't have an implementation. It's also dangerous,
|
||||||
|
because an application may choose to operate differently, merely because it
|
||||||
|
detects the presence of these additional interfaces.
|
||||||
|
|
||||||
|
This package solves this problem by checking which additional interfaces a
|
||||||
|
`http.ResponseWriter` implements, returning a wrapped version implementing the
|
||||||
|
exact same set of interfaces.
|
||||||
|
|
||||||
|
Additionally this package properly handles edge cases such as `WriteHeader` not
|
||||||
|
being called, or called more than once, as well as concurrent calls to
|
||||||
|
`http.ResponseWriter` methods, and even calls happening after the wrapped
|
||||||
|
`ServeHTTP` has already returned.
|
||||||
|
|
||||||
|
Unfortunately this package is not perfect either. It's possible that it is
|
||||||
|
still missing some interfaces provided by the go core (let me know if you find
|
||||||
|
one), and it won't work for applications adding their own interfaces into the
|
||||||
|
mix. You can however use `httpsnoop.Unwrap(w)` to access the underlying
|
||||||
|
`http.ResponseWriter` and type-assert the result to its other interfaces.
|
||||||
|
|
||||||
|
However, hopefully the explanation above has sufficiently scared you of rolling
|
||||||
|
your own solution to this problem. httpsnoop may still break your application,
|
||||||
|
but at least it tries to avoid it as much as possible.
|
||||||
|
|
||||||
|
Anyway, the real problem here is that smuggling additional interfaces inside
|
||||||
|
`http.ResponseWriter` is a problematic design choice, but it probably goes as
|
||||||
|
deep as the Go language specification itself. But that's okay, I still prefer
|
||||||
|
Go over the alternatives ;).
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkBaseline-8 20000 94912 ns/op
|
||||||
|
BenchmarkCaptureMetrics-8 20000 95461 ns/op
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, using `CaptureMetrics` on a vanilla http.Handler introduces an
|
||||||
|
overhead of ~500 ns per http request on my machine. However, the margin of
|
||||||
|
error appears to be larger than that, therefor it should be reasonable to
|
||||||
|
assume that the overhead introduced by `CaptureMetrics` is absolutely
|
||||||
|
negligible.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
86
vendor/github.com/felixge/httpsnoop/capture_metrics.go
generated
vendored
Normal file
86
vendor/github.com/felixge/httpsnoop/capture_metrics.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package httpsnoop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Metrics holds metrics captured from CaptureMetrics.
|
||||||
|
type Metrics struct {
|
||||||
|
// Code is the first http response code passed to the WriteHeader func of
|
||||||
|
// the ResponseWriter. If no such call is made, a default code of 200 is
|
||||||
|
// assumed instead.
|
||||||
|
Code int
|
||||||
|
// Duration is the time it took to execute the handler.
|
||||||
|
Duration time.Duration
|
||||||
|
// Written is the number of bytes successfully written by the Write or
|
||||||
|
// ReadFrom function of the ResponseWriter. ResponseWriters may also write
|
||||||
|
// data to their underlaying connection directly (e.g. headers), but those
|
||||||
|
// are not tracked. Therefor the number of Written bytes will usually match
|
||||||
|
// the size of the response body.
|
||||||
|
Written int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaptureMetrics wraps the given hnd, executes it with the given w and r, and
|
||||||
|
// returns the metrics it captured from it.
|
||||||
|
func CaptureMetrics(hnd http.Handler, w http.ResponseWriter, r *http.Request) Metrics {
|
||||||
|
return CaptureMetricsFn(w, func(ww http.ResponseWriter) {
|
||||||
|
hnd.ServeHTTP(ww, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaptureMetricsFn wraps w and calls fn with the wrapped w and returns the
|
||||||
|
// resulting metrics. This is very similar to CaptureMetrics (which is just
|
||||||
|
// sugar on top of this func), but is a more usable interface if your
|
||||||
|
// application doesn't use the Go http.Handler interface.
|
||||||
|
func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metrics {
|
||||||
|
m := Metrics{Code: http.StatusOK}
|
||||||
|
m.CaptureMetrics(w, fn)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaptureMetrics wraps w and calls fn with the wrapped w and updates
|
||||||
|
// Metrics m with the resulting metrics. This is similar to CaptureMetricsFn,
|
||||||
|
// but allows one to customize starting Metrics object.
|
||||||
|
func (m *Metrics) CaptureMetrics(w http.ResponseWriter, fn func(http.ResponseWriter)) {
|
||||||
|
var (
|
||||||
|
start = time.Now()
|
||||||
|
headerWritten bool
|
||||||
|
hooks = Hooks{
|
||||||
|
WriteHeader: func(next WriteHeaderFunc) WriteHeaderFunc {
|
||||||
|
return func(code int) {
|
||||||
|
next(code)
|
||||||
|
|
||||||
|
if !headerWritten {
|
||||||
|
m.Code = code
|
||||||
|
headerWritten = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Write: func(next WriteFunc) WriteFunc {
|
||||||
|
return func(p []byte) (int, error) {
|
||||||
|
n, err := next(p)
|
||||||
|
|
||||||
|
m.Written += int64(n)
|
||||||
|
headerWritten = true
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ReadFrom: func(next ReadFromFunc) ReadFromFunc {
|
||||||
|
return func(src io.Reader) (int64, error) {
|
||||||
|
n, err := next(src)
|
||||||
|
|
||||||
|
headerWritten = true
|
||||||
|
m.Written += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
fn(Wrap(w, hooks))
|
||||||
|
m.Duration += time.Since(start)
|
||||||
|
}
|
10
vendor/github.com/felixge/httpsnoop/docs.go
generated
vendored
Normal file
10
vendor/github.com/felixge/httpsnoop/docs.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// Package httpsnoop provides an easy way to capture http related metrics (i.e.
|
||||||
|
// response time, bytes written, and http status code) from your application's
|
||||||
|
// http.Handlers.
|
||||||
|
//
|
||||||
|
// Doing this requires non-trivial wrapping of the http.ResponseWriter
|
||||||
|
// interface, which is also exposed for users interested in a more low-level
|
||||||
|
// API.
|
||||||
|
package httpsnoop
|
||||||
|
|
||||||
|
//go:generate go run codegen/main.go
|
436
vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go
generated
vendored
Normal file
436
vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go
generated
vendored
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
// +build go1.8
|
||||||
|
// Code generated by "httpsnoop/codegen"; DO NOT EDIT
|
||||||
|
|
||||||
|
package httpsnoop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HeaderFunc is part of the http.ResponseWriter interface.
|
||||||
|
type HeaderFunc func() http.Header
|
||||||
|
|
||||||
|
// WriteHeaderFunc is part of the http.ResponseWriter interface.
|
||||||
|
type WriteHeaderFunc func(code int)
|
||||||
|
|
||||||
|
// WriteFunc is part of the http.ResponseWriter interface.
|
||||||
|
type WriteFunc func(b []byte) (int, error)
|
||||||
|
|
||||||
|
// FlushFunc is part of the http.Flusher interface.
|
||||||
|
type FlushFunc func()
|
||||||
|
|
||||||
|
// CloseNotifyFunc is part of the http.CloseNotifier interface.
|
||||||
|
type CloseNotifyFunc func() <-chan bool
|
||||||
|
|
||||||
|
// HijackFunc is part of the http.Hijacker interface.
|
||||||
|
type HijackFunc func() (net.Conn, *bufio.ReadWriter, error)
|
||||||
|
|
||||||
|
// ReadFromFunc is part of the io.ReaderFrom interface.
|
||||||
|
type ReadFromFunc func(src io.Reader) (int64, error)
|
||||||
|
|
||||||
|
// PushFunc is part of the http.Pusher interface.
|
||||||
|
type PushFunc func(target string, opts *http.PushOptions) error
|
||||||
|
|
||||||
|
// Hooks defines a set of method interceptors for methods included in
|
||||||
|
// http.ResponseWriter as well as some others. You can think of them as
|
||||||
|
// middleware for the function calls they target. See Wrap for more details.
|
||||||
|
type Hooks struct {
|
||||||
|
Header func(HeaderFunc) HeaderFunc
|
||||||
|
WriteHeader func(WriteHeaderFunc) WriteHeaderFunc
|
||||||
|
Write func(WriteFunc) WriteFunc
|
||||||
|
Flush func(FlushFunc) FlushFunc
|
||||||
|
CloseNotify func(CloseNotifyFunc) CloseNotifyFunc
|
||||||
|
Hijack func(HijackFunc) HijackFunc
|
||||||
|
ReadFrom func(ReadFromFunc) ReadFromFunc
|
||||||
|
Push func(PushFunc) PushFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap returns a wrapped version of w that provides the exact same interface
|
||||||
|
// as w. Specifically if w implements any combination of:
|
||||||
|
//
|
||||||
|
// - http.Flusher
|
||||||
|
// - http.CloseNotifier
|
||||||
|
// - http.Hijacker
|
||||||
|
// - io.ReaderFrom
|
||||||
|
// - http.Pusher
|
||||||
|
//
|
||||||
|
// The wrapped version will implement the exact same combination. If no hooks
|
||||||
|
// are set, the wrapped version also behaves exactly as w. Hooks targeting
|
||||||
|
// methods not supported by w are ignored. Any other hooks will intercept the
|
||||||
|
// method they target and may modify the call's arguments and/or return values.
|
||||||
|
// The CaptureMetrics implementation serves as a working example for how the
|
||||||
|
// hooks can be used.
|
||||||
|
func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter {
|
||||||
|
rw := &rw{w: w, h: hooks}
|
||||||
|
_, i0 := w.(http.Flusher)
|
||||||
|
_, i1 := w.(http.CloseNotifier)
|
||||||
|
_, i2 := w.(http.Hijacker)
|
||||||
|
_, i3 := w.(io.ReaderFrom)
|
||||||
|
_, i4 := w.(http.Pusher)
|
||||||
|
switch {
|
||||||
|
// combination 1/32
|
||||||
|
case !i0 && !i1 && !i2 && !i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
}{rw, rw}
|
||||||
|
// combination 2/32
|
||||||
|
case !i0 && !i1 && !i2 && !i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 3/32
|
||||||
|
case !i0 && !i1 && !i2 && i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 4/32
|
||||||
|
case !i0 && !i1 && !i2 && i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 5/32
|
||||||
|
case !i0 && !i1 && i2 && !i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Hijacker
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 6/32
|
||||||
|
case !i0 && !i1 && i2 && !i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Hijacker
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 7/32
|
||||||
|
case !i0 && !i1 && i2 && i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 8/32
|
||||||
|
case !i0 && !i1 && i2 && i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 9/32
|
||||||
|
case !i0 && i1 && !i2 && !i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 10/32
|
||||||
|
case !i0 && i1 && !i2 && !i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 11/32
|
||||||
|
case !i0 && i1 && !i2 && i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 12/32
|
||||||
|
case !i0 && i1 && !i2 && i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 13/32
|
||||||
|
case !i0 && i1 && i2 && !i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 14/32
|
||||||
|
case !i0 && i1 && i2 && !i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 15/32
|
||||||
|
case !i0 && i1 && i2 && i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 16/32
|
||||||
|
case !i0 && i1 && i2 && i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw, rw}
|
||||||
|
// combination 17/32
|
||||||
|
case i0 && !i1 && !i2 && !i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 18/32
|
||||||
|
case i0 && !i1 && !i2 && !i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 19/32
|
||||||
|
case i0 && !i1 && !i2 && i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 20/32
|
||||||
|
case i0 && !i1 && !i2 && i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 21/32
|
||||||
|
case i0 && !i1 && i2 && !i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.Hijacker
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 22/32
|
||||||
|
case i0 && !i1 && i2 && !i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.Hijacker
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 23/32
|
||||||
|
case i0 && !i1 && i2 && i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 24/32
|
||||||
|
case i0 && !i1 && i2 && i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw, rw}
|
||||||
|
// combination 25/32
|
||||||
|
case i0 && i1 && !i2 && !i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 26/32
|
||||||
|
case i0 && i1 && !i2 && !i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 27/32
|
||||||
|
case i0 && i1 && !i2 && i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 28/32
|
||||||
|
case i0 && i1 && !i2 && i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw, rw}
|
||||||
|
// combination 29/32
|
||||||
|
case i0 && i1 && i2 && !i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 30/32
|
||||||
|
case i0 && i1 && i2 && !i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw, rw}
|
||||||
|
// combination 31/32
|
||||||
|
case i0 && i1 && i2 && i3 && !i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw, rw, rw}
|
||||||
|
// combination 32/32
|
||||||
|
case i0 && i1 && i2 && i3 && i4:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
http.Pusher
|
||||||
|
}{rw, rw, rw, rw, rw, rw, rw}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
type rw struct {
|
||||||
|
w http.ResponseWriter
|
||||||
|
h Hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Unwrap() http.ResponseWriter {
|
||||||
|
return w.w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Header() http.Header {
|
||||||
|
f := w.w.(http.ResponseWriter).Header
|
||||||
|
if w.h.Header != nil {
|
||||||
|
f = w.h.Header(f)
|
||||||
|
}
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) WriteHeader(code int) {
|
||||||
|
f := w.w.(http.ResponseWriter).WriteHeader
|
||||||
|
if w.h.WriteHeader != nil {
|
||||||
|
f = w.h.WriteHeader(f)
|
||||||
|
}
|
||||||
|
f(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Write(b []byte) (int, error) {
|
||||||
|
f := w.w.(http.ResponseWriter).Write
|
||||||
|
if w.h.Write != nil {
|
||||||
|
f = w.h.Write(f)
|
||||||
|
}
|
||||||
|
return f(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Flush() {
|
||||||
|
f := w.w.(http.Flusher).Flush
|
||||||
|
if w.h.Flush != nil {
|
||||||
|
f = w.h.Flush(f)
|
||||||
|
}
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) CloseNotify() <-chan bool {
|
||||||
|
f := w.w.(http.CloseNotifier).CloseNotify
|
||||||
|
if w.h.CloseNotify != nil {
|
||||||
|
f = w.h.CloseNotify(f)
|
||||||
|
}
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
f := w.w.(http.Hijacker).Hijack
|
||||||
|
if w.h.Hijack != nil {
|
||||||
|
f = w.h.Hijack(f)
|
||||||
|
}
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) ReadFrom(src io.Reader) (int64, error) {
|
||||||
|
f := w.w.(io.ReaderFrom).ReadFrom
|
||||||
|
if w.h.ReadFrom != nil {
|
||||||
|
f = w.h.ReadFrom(f)
|
||||||
|
}
|
||||||
|
return f(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Push(target string, opts *http.PushOptions) error {
|
||||||
|
f := w.w.(http.Pusher).Push
|
||||||
|
if w.h.Push != nil {
|
||||||
|
f = w.h.Push(f)
|
||||||
|
}
|
||||||
|
return f(target, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Unwrapper interface {
|
||||||
|
Unwrap() http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns the underlying http.ResponseWriter from within zero or more
|
||||||
|
// layers of httpsnoop wrappers.
|
||||||
|
func Unwrap(w http.ResponseWriter) http.ResponseWriter {
|
||||||
|
if rw, ok := w.(Unwrapper); ok {
|
||||||
|
// recurse until rw.Unwrap() returns a non-Unwrapper
|
||||||
|
return Unwrap(rw.Unwrap())
|
||||||
|
} else {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
}
|
278
vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go
generated
vendored
Normal file
278
vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go
generated
vendored
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
// +build !go1.8
|
||||||
|
// Code generated by "httpsnoop/codegen"; DO NOT EDIT
|
||||||
|
|
||||||
|
package httpsnoop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HeaderFunc is part of the http.ResponseWriter interface.
|
||||||
|
type HeaderFunc func() http.Header
|
||||||
|
|
||||||
|
// WriteHeaderFunc is part of the http.ResponseWriter interface.
|
||||||
|
type WriteHeaderFunc func(code int)
|
||||||
|
|
||||||
|
// WriteFunc is part of the http.ResponseWriter interface.
|
||||||
|
type WriteFunc func(b []byte) (int, error)
|
||||||
|
|
||||||
|
// FlushFunc is part of the http.Flusher interface.
|
||||||
|
type FlushFunc func()
|
||||||
|
|
||||||
|
// CloseNotifyFunc is part of the http.CloseNotifier interface.
|
||||||
|
type CloseNotifyFunc func() <-chan bool
|
||||||
|
|
||||||
|
// HijackFunc is part of the http.Hijacker interface.
|
||||||
|
type HijackFunc func() (net.Conn, *bufio.ReadWriter, error)
|
||||||
|
|
||||||
|
// ReadFromFunc is part of the io.ReaderFrom interface.
|
||||||
|
type ReadFromFunc func(src io.Reader) (int64, error)
|
||||||
|
|
||||||
|
// Hooks defines a set of method interceptors for methods included in
|
||||||
|
// http.ResponseWriter as well as some others. You can think of them as
|
||||||
|
// middleware for the function calls they target. See Wrap for more details.
|
||||||
|
type Hooks struct {
|
||||||
|
Header func(HeaderFunc) HeaderFunc
|
||||||
|
WriteHeader func(WriteHeaderFunc) WriteHeaderFunc
|
||||||
|
Write func(WriteFunc) WriteFunc
|
||||||
|
Flush func(FlushFunc) FlushFunc
|
||||||
|
CloseNotify func(CloseNotifyFunc) CloseNotifyFunc
|
||||||
|
Hijack func(HijackFunc) HijackFunc
|
||||||
|
ReadFrom func(ReadFromFunc) ReadFromFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap returns a wrapped version of w that provides the exact same interface
|
||||||
|
// as w. Specifically if w implements any combination of:
|
||||||
|
//
|
||||||
|
// - http.Flusher
|
||||||
|
// - http.CloseNotifier
|
||||||
|
// - http.Hijacker
|
||||||
|
// - io.ReaderFrom
|
||||||
|
//
|
||||||
|
// The wrapped version will implement the exact same combination. If no hooks
|
||||||
|
// are set, the wrapped version also behaves exactly as w. Hooks targeting
|
||||||
|
// methods not supported by w are ignored. Any other hooks will intercept the
|
||||||
|
// method they target and may modify the call's arguments and/or return values.
|
||||||
|
// The CaptureMetrics implementation serves as a working example for how the
|
||||||
|
// hooks can be used.
|
||||||
|
func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter {
|
||||||
|
rw := &rw{w: w, h: hooks}
|
||||||
|
_, i0 := w.(http.Flusher)
|
||||||
|
_, i1 := w.(http.CloseNotifier)
|
||||||
|
_, i2 := w.(http.Hijacker)
|
||||||
|
_, i3 := w.(io.ReaderFrom)
|
||||||
|
switch {
|
||||||
|
// combination 1/16
|
||||||
|
case !i0 && !i1 && !i2 && !i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
}{rw, rw}
|
||||||
|
// combination 2/16
|
||||||
|
case !i0 && !i1 && !i2 && i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 3/16
|
||||||
|
case !i0 && !i1 && i2 && !i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Hijacker
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 4/16
|
||||||
|
case !i0 && !i1 && i2 && i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 5/16
|
||||||
|
case !i0 && i1 && !i2 && !i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 6/16
|
||||||
|
case !i0 && i1 && !i2 && i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 7/16
|
||||||
|
case !i0 && i1 && i2 && !i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 8/16
|
||||||
|
case !i0 && i1 && i2 && i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 9/16
|
||||||
|
case i0 && !i1 && !i2 && !i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
}{rw, rw, rw}
|
||||||
|
// combination 10/16
|
||||||
|
case i0 && !i1 && !i2 && i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 11/16
|
||||||
|
case i0 && !i1 && i2 && !i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.Hijacker
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 12/16
|
||||||
|
case i0 && !i1 && i2 && i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 13/16
|
||||||
|
case i0 && i1 && !i2 && !i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
}{rw, rw, rw, rw}
|
||||||
|
// combination 14/16
|
||||||
|
case i0 && i1 && !i2 && i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 15/16
|
||||||
|
case i0 && i1 && i2 && !i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
}{rw, rw, rw, rw, rw}
|
||||||
|
// combination 16/16
|
||||||
|
case i0 && i1 && i2 && i3:
|
||||||
|
return struct {
|
||||||
|
Unwrapper
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
http.Hijacker
|
||||||
|
io.ReaderFrom
|
||||||
|
}{rw, rw, rw, rw, rw, rw}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
type rw struct {
|
||||||
|
w http.ResponseWriter
|
||||||
|
h Hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Unwrap() http.ResponseWriter {
|
||||||
|
return w.w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Header() http.Header {
|
||||||
|
f := w.w.(http.ResponseWriter).Header
|
||||||
|
if w.h.Header != nil {
|
||||||
|
f = w.h.Header(f)
|
||||||
|
}
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) WriteHeader(code int) {
|
||||||
|
f := w.w.(http.ResponseWriter).WriteHeader
|
||||||
|
if w.h.WriteHeader != nil {
|
||||||
|
f = w.h.WriteHeader(f)
|
||||||
|
}
|
||||||
|
f(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Write(b []byte) (int, error) {
|
||||||
|
f := w.w.(http.ResponseWriter).Write
|
||||||
|
if w.h.Write != nil {
|
||||||
|
f = w.h.Write(f)
|
||||||
|
}
|
||||||
|
return f(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Flush() {
|
||||||
|
f := w.w.(http.Flusher).Flush
|
||||||
|
if w.h.Flush != nil {
|
||||||
|
f = w.h.Flush(f)
|
||||||
|
}
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) CloseNotify() <-chan bool {
|
||||||
|
f := w.w.(http.CloseNotifier).CloseNotify
|
||||||
|
if w.h.CloseNotify != nil {
|
||||||
|
f = w.h.CloseNotify(f)
|
||||||
|
}
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
f := w.w.(http.Hijacker).Hijack
|
||||||
|
if w.h.Hijack != nil {
|
||||||
|
f = w.h.Hijack(f)
|
||||||
|
}
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *rw) ReadFrom(src io.Reader) (int64, error) {
|
||||||
|
f := w.w.(io.ReaderFrom).ReadFrom
|
||||||
|
if w.h.ReadFrom != nil {
|
||||||
|
f = w.h.ReadFrom(f)
|
||||||
|
}
|
||||||
|
return f(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Unwrapper interface {
|
||||||
|
Unwrap() http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns the underlying http.ResponseWriter from within zero or more
|
||||||
|
// layers of httpsnoop wrappers.
|
||||||
|
func Unwrap(w http.ResponseWriter) http.ResponseWriter {
|
||||||
|
if rw, ok := w.(Unwrapper); ok {
|
||||||
|
// recurse until rw.Unwrap() returns a non-Unwrapper
|
||||||
|
return Unwrap(rw.Unwrap())
|
||||||
|
} else {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
}
|
201
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
61
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go
generated
vendored
Normal file
61
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultClient is the default Client and is used by Get, Head, Post and PostForm.
|
||||||
|
// Please be careful of intitialization order - for example, if you change
|
||||||
|
// the global propagator, the DefaultClient might still be using the old one.
|
||||||
|
var DefaultClient = &http.Client{Transport: NewTransport(http.DefaultTransport)}
|
||||||
|
|
||||||
|
// Get is a convenient replacement for http.Get that adds a span around the request.
|
||||||
|
func Get(ctx context.Context, targetURL string) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", targetURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return DefaultClient.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head is a convenient replacement for http.Head that adds a span around the request.
|
||||||
|
func Head(ctx context.Context, targetURL string) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "HEAD", targetURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return DefaultClient.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post is a convenient replacement for http.Post that adds a span around the request.
|
||||||
|
func Post(ctx context.Context, targetURL, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", targetURL, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", contentType)
|
||||||
|
return DefaultClient.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostForm is a convenient replacement for http.PostForm that adds a span around the request.
|
||||||
|
func PostForm(ctx context.Context, targetURL string, data url.Values) (resp *http.Response, err error) {
|
||||||
|
return Post(ctx, targetURL, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||||
|
}
|
46
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go
generated
vendored
Normal file
46
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Attribute keys that can be added to a span.
|
||||||
|
const (
|
||||||
|
ReadBytesKey = attribute.Key("http.read_bytes") // if anything was read from the request body, the total number of bytes read
|
||||||
|
ReadErrorKey = attribute.Key("http.read_error") // If an error occurred while reading a request, the string of the error (io.EOF is not recorded)
|
||||||
|
WroteBytesKey = attribute.Key("http.wrote_bytes") // if anything was written to the response writer, the total number of bytes written
|
||||||
|
WriteErrorKey = attribute.Key("http.write_error") // if an error occurred while writing a reply, the string of the error (io.EOF is not recorded)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Server HTTP metrics.
|
||||||
|
const (
|
||||||
|
RequestCount = "http.server.request_count" // Incoming request count total
|
||||||
|
RequestContentLength = "http.server.request_content_length" // Incoming request bytes total
|
||||||
|
ResponseContentLength = "http.server.response_content_length" // Incoming response bytes total
|
||||||
|
ServerLatency = "http.server.duration" // Incoming end to end duration, microseconds
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter is a predicate used to determine whether a given http.request should
|
||||||
|
// be traced. A Filter must return true if the request should be traced.
|
||||||
|
type Filter func(*http.Request) bool
|
||||||
|
|
||||||
|
func newTracer(tp trace.TracerProvider) trace.Tracer {
|
||||||
|
return tp.Tracer(instrumentationName, trace.WithInstrumentationVersion(Version()))
|
||||||
|
}
|
208
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go
generated
vendored
Normal file
208
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
instrumentationName = "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// config represents the configuration options available for the http.Handler
|
||||||
|
// and http.Transport types.
|
||||||
|
type config struct {
|
||||||
|
ServerName string
|
||||||
|
Tracer trace.Tracer
|
||||||
|
Meter metric.Meter
|
||||||
|
Propagators propagation.TextMapPropagator
|
||||||
|
SpanStartOptions []trace.SpanStartOption
|
||||||
|
PublicEndpoint bool
|
||||||
|
PublicEndpointFn func(*http.Request) bool
|
||||||
|
ReadEvent bool
|
||||||
|
WriteEvent bool
|
||||||
|
Filters []Filter
|
||||||
|
SpanNameFormatter func(string, *http.Request) string
|
||||||
|
ClientTrace func(context.Context) *httptrace.ClientTrace
|
||||||
|
|
||||||
|
TracerProvider trace.TracerProvider
|
||||||
|
MeterProvider metric.MeterProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option interface used for setting optional config properties.
|
||||||
|
type Option interface {
|
||||||
|
apply(*config)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionFunc func(*config)
|
||||||
|
|
||||||
|
func (o optionFunc) apply(c *config) {
|
||||||
|
o(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newConfig creates a new config struct and applies opts to it.
|
||||||
|
func newConfig(opts ...Option) *config {
|
||||||
|
c := &config{
|
||||||
|
Propagators: otel.GetTextMapPropagator(),
|
||||||
|
MeterProvider: otel.GetMeterProvider(),
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt.apply(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tracer is only initialized if manually specified. Otherwise, can be passed with the tracing context.
|
||||||
|
if c.TracerProvider != nil {
|
||||||
|
c.Tracer = newTracer(c.TracerProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Meter = c.MeterProvider.Meter(
|
||||||
|
instrumentationName,
|
||||||
|
metric.WithInstrumentationVersion(Version()),
|
||||||
|
)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTracerProvider specifies a tracer provider to use for creating a tracer.
|
||||||
|
// If none is specified, the global provider is used.
|
||||||
|
func WithTracerProvider(provider trace.TracerProvider) Option {
|
||||||
|
return optionFunc(func(cfg *config) {
|
||||||
|
if provider != nil {
|
||||||
|
cfg.TracerProvider = provider
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMeterProvider specifies a meter provider to use for creating a meter.
|
||||||
|
// If none is specified, the global provider is used.
|
||||||
|
func WithMeterProvider(provider metric.MeterProvider) Option {
|
||||||
|
return optionFunc(func(cfg *config) {
|
||||||
|
if provider != nil {
|
||||||
|
cfg.MeterProvider = provider
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPublicEndpoint configures the Handler to link the span with an incoming
|
||||||
|
// span context. If this option is not provided, then the association is a child
|
||||||
|
// association instead of a link.
|
||||||
|
func WithPublicEndpoint() Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
c.PublicEndpoint = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPublicEndpointFn runs with every request, and allows conditionnally
|
||||||
|
// configuring the Handler to link the span with an incoming span context. If
|
||||||
|
// this option is not provided or returns false, then the association is a
|
||||||
|
// child association instead of a link.
|
||||||
|
// Note: WithPublicEndpoint takes precedence over WithPublicEndpointFn.
|
||||||
|
func WithPublicEndpointFn(fn func(*http.Request) bool) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
c.PublicEndpointFn = fn
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPropagators configures specific propagators. If this
|
||||||
|
// option isn't specified, then the global TextMapPropagator is used.
|
||||||
|
func WithPropagators(ps propagation.TextMapPropagator) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
if ps != nil {
|
||||||
|
c.Propagators = ps
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSpanOptions configures an additional set of
|
||||||
|
// trace.SpanOptions, which are applied to each new span.
|
||||||
|
func WithSpanOptions(opts ...trace.SpanStartOption) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
c.SpanStartOptions = append(c.SpanStartOptions, opts...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFilter adds a filter to the list of filters used by the handler.
|
||||||
|
// If any filter indicates to exclude a request then the request will not be
|
||||||
|
// traced. All filters must allow a request to be traced for a Span to be created.
|
||||||
|
// If no filters are provided then all requests are traced.
|
||||||
|
// Filters will be invoked for each processed request, it is advised to make them
|
||||||
|
// simple and fast.
|
||||||
|
func WithFilter(f Filter) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
c.Filters = append(c.Filters, f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type event int
|
||||||
|
|
||||||
|
// Different types of events that can be recorded, see WithMessageEvents.
|
||||||
|
const (
|
||||||
|
ReadEvents event = iota
|
||||||
|
WriteEvents
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithMessageEvents configures the Handler to record the specified events
|
||||||
|
// (span.AddEvent) on spans. By default only summary attributes are added at the
|
||||||
|
// end of the request.
|
||||||
|
//
|
||||||
|
// Valid events are:
|
||||||
|
// - ReadEvents: Record the number of bytes read after every http.Request.Body.Read
|
||||||
|
// using the ReadBytesKey
|
||||||
|
// - WriteEvents: Record the number of bytes written after every http.ResponeWriter.Write
|
||||||
|
// using the WriteBytesKey
|
||||||
|
func WithMessageEvents(events ...event) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
for _, e := range events {
|
||||||
|
switch e {
|
||||||
|
case ReadEvents:
|
||||||
|
c.ReadEvent = true
|
||||||
|
case WriteEvents:
|
||||||
|
c.WriteEvent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSpanNameFormatter takes a function that will be called on every
|
||||||
|
// request and the returned string will become the Span Name.
|
||||||
|
func WithSpanNameFormatter(f func(operation string, r *http.Request) string) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
c.SpanNameFormatter = f
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientTrace takes a function that returns client trace instance that will be
|
||||||
|
// applied to the requests sent through the otelhttp Transport.
|
||||||
|
func WithClientTrace(f func(context.Context) *httptrace.ClientTrace) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
c.ClientTrace = f
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServerName returns an Option that sets the name of the (virtual) server
|
||||||
|
// handling requests.
|
||||||
|
func WithServerName(server string) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
c.ServerName = server
|
||||||
|
})
|
||||||
|
}
|
18
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go
generated
vendored
Normal file
18
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package otelhttp provides an http.Handler and functions that are intended
|
||||||
|
// to be used to add tracing by wrapping existing handlers (with Handler) and
|
||||||
|
// routes WithRouteTag.
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
275
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go
generated
vendored
Normal file
275
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go
generated
vendored
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/felixge/httpsnoop"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// middleware is an http middleware which wraps the next handler in a span.
|
||||||
|
type middleware struct {
|
||||||
|
operation string
|
||||||
|
server string
|
||||||
|
|
||||||
|
tracer trace.Tracer
|
||||||
|
meter metric.Meter
|
||||||
|
propagators propagation.TextMapPropagator
|
||||||
|
spanStartOptions []trace.SpanStartOption
|
||||||
|
readEvent bool
|
||||||
|
writeEvent bool
|
||||||
|
filters []Filter
|
||||||
|
spanNameFormatter func(string, *http.Request) string
|
||||||
|
counters map[string]metric.Int64Counter
|
||||||
|
valueRecorders map[string]metric.Float64Histogram
|
||||||
|
publicEndpoint bool
|
||||||
|
publicEndpointFn func(*http.Request) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultHandlerFormatter(operation string, _ *http.Request) string {
|
||||||
|
return operation
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler wraps the passed handler in a span named after the operation and
|
||||||
|
// enriches it with metrics.
|
||||||
|
func NewHandler(handler http.Handler, operation string, opts ...Option) http.Handler {
|
||||||
|
return NewMiddleware(operation, opts...)(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMiddleware returns a tracing and metrics instrumentation middleware.
|
||||||
|
// The handler returned by the middleware wraps a handler
|
||||||
|
// in a span named after the operation and enriches it with metrics.
|
||||||
|
func NewMiddleware(operation string, opts ...Option) func(http.Handler) http.Handler {
|
||||||
|
h := middleware{
|
||||||
|
operation: operation,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultOpts := []Option{
|
||||||
|
WithSpanOptions(trace.WithSpanKind(trace.SpanKindServer)),
|
||||||
|
WithSpanNameFormatter(defaultHandlerFormatter),
|
||||||
|
}
|
||||||
|
|
||||||
|
c := newConfig(append(defaultOpts, opts...)...)
|
||||||
|
h.configure(c)
|
||||||
|
h.createMeasures()
|
||||||
|
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h.serveHTTP(w, r, next)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *middleware) configure(c *config) {
|
||||||
|
h.tracer = c.Tracer
|
||||||
|
h.meter = c.Meter
|
||||||
|
h.propagators = c.Propagators
|
||||||
|
h.spanStartOptions = c.SpanStartOptions
|
||||||
|
h.readEvent = c.ReadEvent
|
||||||
|
h.writeEvent = c.WriteEvent
|
||||||
|
h.filters = c.Filters
|
||||||
|
h.spanNameFormatter = c.SpanNameFormatter
|
||||||
|
h.publicEndpoint = c.PublicEndpoint
|
||||||
|
h.publicEndpointFn = c.PublicEndpointFn
|
||||||
|
h.server = c.ServerName
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleErr(err error) {
|
||||||
|
if err != nil {
|
||||||
|
otel.Handle(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *middleware) createMeasures() {
|
||||||
|
h.counters = make(map[string]metric.Int64Counter)
|
||||||
|
h.valueRecorders = make(map[string]metric.Float64Histogram)
|
||||||
|
|
||||||
|
requestBytesCounter, err := h.meter.Int64Counter(RequestContentLength)
|
||||||
|
handleErr(err)
|
||||||
|
|
||||||
|
responseBytesCounter, err := h.meter.Int64Counter(ResponseContentLength)
|
||||||
|
handleErr(err)
|
||||||
|
|
||||||
|
serverLatencyMeasure, err := h.meter.Float64Histogram(ServerLatency)
|
||||||
|
handleErr(err)
|
||||||
|
|
||||||
|
h.counters[RequestContentLength] = requestBytesCounter
|
||||||
|
h.counters[ResponseContentLength] = responseBytesCounter
|
||||||
|
h.valueRecorders[ServerLatency] = serverLatencyMeasure
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveHTTP sets up tracing and calls the given next http.Handler with the span
|
||||||
|
// context injected into the request context.
|
||||||
|
func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http.Handler) {
|
||||||
|
requestStartTime := time.Now()
|
||||||
|
for _, f := range h.filters {
|
||||||
|
if !f(r) {
|
||||||
|
// Simply pass through to the handler if a filter rejects the request
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := h.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||||
|
opts := []trace.SpanStartOption{
|
||||||
|
trace.WithAttributes(semconvutil.HTTPServerRequest(h.server, r)...),
|
||||||
|
}
|
||||||
|
if h.server != "" {
|
||||||
|
hostAttr := semconv.NetHostName(h.server)
|
||||||
|
opts = append(opts, trace.WithAttributes(hostAttr))
|
||||||
|
}
|
||||||
|
opts = append(opts, h.spanStartOptions...)
|
||||||
|
if h.publicEndpoint || (h.publicEndpointFn != nil && h.publicEndpointFn(r.WithContext(ctx))) {
|
||||||
|
opts = append(opts, trace.WithNewRoot())
|
||||||
|
// Linking incoming span context if any for public endpoint.
|
||||||
|
if s := trace.SpanContextFromContext(ctx); s.IsValid() && s.IsRemote() {
|
||||||
|
opts = append(opts, trace.WithLinks(trace.Link{SpanContext: s}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer := h.tracer
|
||||||
|
|
||||||
|
if tracer == nil {
|
||||||
|
if span := trace.SpanFromContext(r.Context()); span.SpanContext().IsValid() {
|
||||||
|
tracer = newTracer(span.TracerProvider())
|
||||||
|
} else {
|
||||||
|
tracer = newTracer(otel.GetTracerProvider())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, span := tracer.Start(ctx, h.spanNameFormatter(h.operation, r), opts...)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
readRecordFunc := func(int64) {}
|
||||||
|
if h.readEvent {
|
||||||
|
readRecordFunc = func(n int64) {
|
||||||
|
span.AddEvent("read", trace.WithAttributes(ReadBytesKey.Int64(n)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bw bodyWrapper
|
||||||
|
// if request body is nil or NoBody, we don't want to mutate the body as it
|
||||||
|
// will affect the identity of it in an unforeseeable way because we assert
|
||||||
|
// ReadCloser fulfills a certain interface and it is indeed nil or NoBody.
|
||||||
|
if r.Body != nil && r.Body != http.NoBody {
|
||||||
|
bw.ReadCloser = r.Body
|
||||||
|
bw.record = readRecordFunc
|
||||||
|
r.Body = &bw
|
||||||
|
}
|
||||||
|
|
||||||
|
writeRecordFunc := func(int64) {}
|
||||||
|
if h.writeEvent {
|
||||||
|
writeRecordFunc = func(n int64) {
|
||||||
|
span.AddEvent("write", trace.WithAttributes(WroteBytesKey.Int64(n)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rww := &respWriterWrapper{
|
||||||
|
ResponseWriter: w,
|
||||||
|
record: writeRecordFunc,
|
||||||
|
ctx: ctx,
|
||||||
|
props: h.propagators,
|
||||||
|
statusCode: http.StatusOK, // default status code in case the Handler doesn't write anything
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap w to use our ResponseWriter methods while also exposing
|
||||||
|
// other interfaces that w may implement (http.CloseNotifier,
|
||||||
|
// http.Flusher, http.Hijacker, http.Pusher, io.ReaderFrom).
|
||||||
|
|
||||||
|
w = httpsnoop.Wrap(w, httpsnoop.Hooks{
|
||||||
|
Header: func(httpsnoop.HeaderFunc) httpsnoop.HeaderFunc {
|
||||||
|
return rww.Header
|
||||||
|
},
|
||||||
|
Write: func(httpsnoop.WriteFunc) httpsnoop.WriteFunc {
|
||||||
|
return rww.Write
|
||||||
|
},
|
||||||
|
WriteHeader: func(httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc {
|
||||||
|
return rww.WriteHeader
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
labeler := &Labeler{}
|
||||||
|
ctx = injectLabeler(ctx, labeler)
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
|
||||||
|
setAfterServeAttributes(span, bw.read, rww.written, rww.statusCode, bw.err, rww.err)
|
||||||
|
|
||||||
|
// Add metrics
|
||||||
|
attributes := append(labeler.Get(), semconvutil.HTTPServerRequestMetrics(h.server, r)...)
|
||||||
|
if rww.statusCode > 0 {
|
||||||
|
attributes = append(attributes, semconv.HTTPStatusCode(rww.statusCode))
|
||||||
|
}
|
||||||
|
o := metric.WithAttributes(attributes...)
|
||||||
|
h.counters[RequestContentLength].Add(ctx, bw.read, o)
|
||||||
|
h.counters[ResponseContentLength].Add(ctx, rww.written, o)
|
||||||
|
|
||||||
|
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||||
|
elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond)
|
||||||
|
|
||||||
|
h.valueRecorders[ServerLatency].Record(ctx, elapsedTime, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAfterServeAttributes(span trace.Span, read, wrote int64, statusCode int, rerr, werr error) {
|
||||||
|
attributes := []attribute.KeyValue{}
|
||||||
|
|
||||||
|
// TODO: Consider adding an event after each read and write, possibly as an
|
||||||
|
// option (defaulting to off), so as to not create needlessly verbose spans.
|
||||||
|
if read > 0 {
|
||||||
|
attributes = append(attributes, ReadBytesKey.Int64(read))
|
||||||
|
}
|
||||||
|
if rerr != nil && rerr != io.EOF {
|
||||||
|
attributes = append(attributes, ReadErrorKey.String(rerr.Error()))
|
||||||
|
}
|
||||||
|
if wrote > 0 {
|
||||||
|
attributes = append(attributes, WroteBytesKey.Int64(wrote))
|
||||||
|
}
|
||||||
|
if statusCode > 0 {
|
||||||
|
attributes = append(attributes, semconv.HTTPStatusCode(statusCode))
|
||||||
|
}
|
||||||
|
span.SetStatus(semconvutil.HTTPServerStatus(statusCode))
|
||||||
|
|
||||||
|
if werr != nil && werr != io.EOF {
|
||||||
|
attributes = append(attributes, WriteErrorKey.String(werr.Error()))
|
||||||
|
}
|
||||||
|
span.SetAttributes(attributes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRouteTag annotates spans and metrics with the provided route name
|
||||||
|
// with HTTP route attribute.
|
||||||
|
func WithRouteTag(route string, h http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
attr := semconv.HTTPRouteKey.String(route)
|
||||||
|
|
||||||
|
span := trace.SpanFromContext(r.Context())
|
||||||
|
span.SetAttributes(attr)
|
||||||
|
|
||||||
|
labeler, _ := LabelerFromContext(r.Context())
|
||||||
|
labeler.Add(attr)
|
||||||
|
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
21
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/gen.go
generated
vendored
Normal file
21
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/gen.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||||
|
|
||||||
|
// Generate semconvutil package:
|
||||||
|
//go:generate gotmpl --body=../../../../../../internal/shared/semconvutil/httpconv_test.go.tmpl "--data={}" --out=httpconv_test.go
|
||||||
|
//go:generate gotmpl --body=../../../../../../internal/shared/semconvutil/httpconv.go.tmpl "--data={}" --out=httpconv.go
|
||||||
|
//go:generate gotmpl --body=../../../../../../internal/shared/semconvutil/netconv_test.go.tmpl "--data={}" --out=netconv_test.go
|
||||||
|
//go:generate gotmpl --body=../../../../../../internal/shared/semconvutil/netconv.go.tmpl "--data={}" --out=netconv.go
|
552
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go
generated
vendored
Normal file
552
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go
generated
vendored
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/semconvutil/httpconv.go.tmpl
|
||||||
|
|
||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPClientResponse returns trace attributes for an HTTP response received by a
|
||||||
|
// client from a server. It will return the following attributes if the related
|
||||||
|
// values are defined in resp: "http.status.code",
|
||||||
|
// "http.response_content_length".
|
||||||
|
//
|
||||||
|
// This does not add all OpenTelemetry required attributes for an HTTP event,
|
||||||
|
// it assumes ClientRequest was used to create the span with a complete set of
|
||||||
|
// attributes. If a complete set of attributes can be generated using the
|
||||||
|
// request contained in resp. For example:
|
||||||
|
//
|
||||||
|
// append(HTTPClientResponse(resp), ClientRequest(resp.Request)...)
|
||||||
|
func HTTPClientResponse(resp *http.Response) []attribute.KeyValue {
|
||||||
|
return hc.ClientResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPClientRequest returns trace attributes for an HTTP request made by a client.
|
||||||
|
// The following attributes are always returned: "http.url", "http.flavor",
|
||||||
|
// "http.method", "net.peer.name". The following attributes are returned if the
|
||||||
|
// related values are defined in req: "net.peer.port", "http.user_agent",
|
||||||
|
// "http.request_content_length", "enduser.id".
|
||||||
|
func HTTPClientRequest(req *http.Request) []attribute.KeyValue {
|
||||||
|
return hc.ClientRequest(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPClientStatus returns a span status code and message for an HTTP status code
|
||||||
|
// value received by a client.
|
||||||
|
func HTTPClientStatus(code int) (codes.Code, string) {
|
||||||
|
return hc.ClientStatus(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPServerRequest returns trace attributes for an HTTP request received by a
|
||||||
|
// server.
|
||||||
|
//
|
||||||
|
// The server must be the primary server name if it is known. For example this
|
||||||
|
// would be the ServerName directive
|
||||||
|
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||||
|
// server, and the server_name directive
|
||||||
|
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||||
|
// nginx server. More generically, the primary server name would be the host
|
||||||
|
// header value that matches the default virtual host of an HTTP server. It
|
||||||
|
// should include the host identifier and if a port is used to route to the
|
||||||
|
// server that port identifier should be included as an appropriate port
|
||||||
|
// suffix.
|
||||||
|
//
|
||||||
|
// If the primary server name is not known, server should be an empty string.
|
||||||
|
// The req Host will be used to determine the server instead.
|
||||||
|
//
|
||||||
|
// The following attributes are always returned: "http.method", "http.scheme",
|
||||||
|
// "http.flavor", "http.target", "net.host.name". The following attributes are
|
||||||
|
// returned if they related values are defined in req: "net.host.port",
|
||||||
|
// "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id",
|
||||||
|
// "http.client_ip".
|
||||||
|
func HTTPServerRequest(server string, req *http.Request) []attribute.KeyValue {
|
||||||
|
return hc.ServerRequest(server, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPServerRequestMetrics returns metric attributes for an HTTP request received by a
|
||||||
|
// server.
|
||||||
|
//
|
||||||
|
// The server must be the primary server name if it is known. For example this
|
||||||
|
// would be the ServerName directive
|
||||||
|
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||||
|
// server, and the server_name directive
|
||||||
|
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||||
|
// nginx server. More generically, the primary server name would be the host
|
||||||
|
// header value that matches the default virtual host of an HTTP server. It
|
||||||
|
// should include the host identifier and if a port is used to route to the
|
||||||
|
// server that port identifier should be included as an appropriate port
|
||||||
|
// suffix.
|
||||||
|
//
|
||||||
|
// If the primary server name is not known, server should be an empty string.
|
||||||
|
// The req Host will be used to determine the server instead.
|
||||||
|
//
|
||||||
|
// The following attributes are always returned: "http.method", "http.scheme",
|
||||||
|
// "http.flavor", "net.host.name". The following attributes are
|
||||||
|
// returned if they related values are defined in req: "net.host.port".
|
||||||
|
func HTTPServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue {
|
||||||
|
return hc.ServerRequestMetrics(server, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPServerStatus returns a span status code and message for an HTTP status code
|
||||||
|
// value returned by a server. Status codes in the 400-499 range are not
|
||||||
|
// returned as errors.
|
||||||
|
func HTTPServerStatus(code int) (codes.Code, string) {
|
||||||
|
return hc.ServerStatus(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPRequestHeader returns the contents of h as attributes.
|
||||||
|
//
|
||||||
|
// Instrumentation should require an explicit configuration of which headers to
|
||||||
|
// captured and then prune what they pass here. Including all headers can be a
|
||||||
|
// security risk - explicit configuration helps avoid leaking sensitive
|
||||||
|
// information.
|
||||||
|
//
|
||||||
|
// The User-Agent header is already captured in the http.user_agent attribute
|
||||||
|
// from ClientRequest and ServerRequest. Instrumentation may provide an option
|
||||||
|
// to capture that header here even though it is not recommended. Otherwise,
|
||||||
|
// instrumentation should filter that out of what is passed.
|
||||||
|
func HTTPRequestHeader(h http.Header) []attribute.KeyValue {
|
||||||
|
return hc.RequestHeader(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPResponseHeader returns the contents of h as attributes.
|
||||||
|
//
|
||||||
|
// Instrumentation should require an explicit configuration of which headers to
|
||||||
|
// captured and then prune what they pass here. Including all headers can be a
|
||||||
|
// security risk - explicit configuration helps avoid leaking sensitive
|
||||||
|
// information.
|
||||||
|
//
|
||||||
|
// The User-Agent header is already captured in the http.user_agent attribute
|
||||||
|
// from ClientRequest and ServerRequest. Instrumentation may provide an option
|
||||||
|
// to capture that header here even though it is not recommended. Otherwise,
|
||||||
|
// instrumentation should filter that out of what is passed.
|
||||||
|
func HTTPResponseHeader(h http.Header) []attribute.KeyValue {
|
||||||
|
return hc.ResponseHeader(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpConv are the HTTP semantic convention attributes defined for a version
|
||||||
|
// of the OpenTelemetry specification.
|
||||||
|
type httpConv struct {
|
||||||
|
NetConv *netConv
|
||||||
|
|
||||||
|
EnduserIDKey attribute.Key
|
||||||
|
HTTPClientIPKey attribute.Key
|
||||||
|
HTTPFlavorKey attribute.Key
|
||||||
|
HTTPMethodKey attribute.Key
|
||||||
|
HTTPRequestContentLengthKey attribute.Key
|
||||||
|
HTTPResponseContentLengthKey attribute.Key
|
||||||
|
HTTPRouteKey attribute.Key
|
||||||
|
HTTPSchemeHTTP attribute.KeyValue
|
||||||
|
HTTPSchemeHTTPS attribute.KeyValue
|
||||||
|
HTTPStatusCodeKey attribute.Key
|
||||||
|
HTTPTargetKey attribute.Key
|
||||||
|
HTTPURLKey attribute.Key
|
||||||
|
HTTPUserAgentKey attribute.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
var hc = &httpConv{
|
||||||
|
NetConv: nc,
|
||||||
|
|
||||||
|
EnduserIDKey: semconv.EnduserIDKey,
|
||||||
|
HTTPClientIPKey: semconv.HTTPClientIPKey,
|
||||||
|
HTTPFlavorKey: semconv.HTTPFlavorKey,
|
||||||
|
HTTPMethodKey: semconv.HTTPMethodKey,
|
||||||
|
HTTPRequestContentLengthKey: semconv.HTTPRequestContentLengthKey,
|
||||||
|
HTTPResponseContentLengthKey: semconv.HTTPResponseContentLengthKey,
|
||||||
|
HTTPRouteKey: semconv.HTTPRouteKey,
|
||||||
|
HTTPSchemeHTTP: semconv.HTTPSchemeHTTP,
|
||||||
|
HTTPSchemeHTTPS: semconv.HTTPSchemeHTTPS,
|
||||||
|
HTTPStatusCodeKey: semconv.HTTPStatusCodeKey,
|
||||||
|
HTTPTargetKey: semconv.HTTPTargetKey,
|
||||||
|
HTTPURLKey: semconv.HTTPURLKey,
|
||||||
|
HTTPUserAgentKey: semconv.HTTPUserAgentKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientResponse returns attributes for an HTTP response received by a client
|
||||||
|
// from a server. The following attributes are returned if the related values
|
||||||
|
// are defined in resp: "http.status.code", "http.response_content_length".
|
||||||
|
//
|
||||||
|
// This does not add all OpenTelemetry required attributes for an HTTP event,
|
||||||
|
// it assumes ClientRequest was used to create the span with a complete set of
|
||||||
|
// attributes. If a complete set of attributes can be generated using the
|
||||||
|
// request contained in resp. For example:
|
||||||
|
//
|
||||||
|
// append(ClientResponse(resp), ClientRequest(resp.Request)...)
|
||||||
|
func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
|
||||||
|
var n int
|
||||||
|
if resp.StatusCode > 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if resp.ContentLength > 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs := make([]attribute.KeyValue, 0, n)
|
||||||
|
if resp.StatusCode > 0 {
|
||||||
|
attrs = append(attrs, c.HTTPStatusCodeKey.Int(resp.StatusCode))
|
||||||
|
}
|
||||||
|
if resp.ContentLength > 0 {
|
||||||
|
attrs = append(attrs, c.HTTPResponseContentLengthKey.Int(int(resp.ContentLength)))
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientRequest returns attributes for an HTTP request made by a client. The
|
||||||
|
// following attributes are always returned: "http.url", "http.flavor",
|
||||||
|
// "http.method", "net.peer.name". The following attributes are returned if the
|
||||||
|
// related values are defined in req: "net.peer.port", "http.user_agent",
|
||||||
|
// "http.request_content_length", "enduser.id".
|
||||||
|
func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
|
||||||
|
n := 3 // URL, peer name, proto, and method.
|
||||||
|
var h string
|
||||||
|
if req.URL != nil {
|
||||||
|
h = req.URL.Host
|
||||||
|
}
|
||||||
|
peer, p := firstHostPort(h, req.Header.Get("Host"))
|
||||||
|
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p)
|
||||||
|
if port > 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
useragent := req.UserAgent()
|
||||||
|
if useragent != "" {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if req.ContentLength > 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
userID, _, hasUserID := req.BasicAuth()
|
||||||
|
if hasUserID {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
attrs := make([]attribute.KeyValue, 0, n)
|
||||||
|
|
||||||
|
attrs = append(attrs, c.method(req.Method))
|
||||||
|
attrs = append(attrs, c.flavor(req.Proto))
|
||||||
|
|
||||||
|
var u string
|
||||||
|
if req.URL != nil {
|
||||||
|
// Remove any username/password info that may be in the URL.
|
||||||
|
userinfo := req.URL.User
|
||||||
|
req.URL.User = nil
|
||||||
|
u = req.URL.String()
|
||||||
|
// Restore any username/password info that was removed.
|
||||||
|
req.URL.User = userinfo
|
||||||
|
}
|
||||||
|
attrs = append(attrs, c.HTTPURLKey.String(u))
|
||||||
|
|
||||||
|
attrs = append(attrs, c.NetConv.PeerName(peer))
|
||||||
|
if port > 0 {
|
||||||
|
attrs = append(attrs, c.NetConv.PeerPort(port))
|
||||||
|
}
|
||||||
|
|
||||||
|
if useragent != "" {
|
||||||
|
attrs = append(attrs, c.HTTPUserAgentKey.String(useragent))
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := req.ContentLength; l > 0 {
|
||||||
|
attrs = append(attrs, c.HTTPRequestContentLengthKey.Int64(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasUserID {
|
||||||
|
attrs = append(attrs, c.EnduserIDKey.String(userID))
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerRequest returns attributes for an HTTP request received by a server.
|
||||||
|
//
|
||||||
|
// The server must be the primary server name if it is known. For example this
|
||||||
|
// would be the ServerName directive
|
||||||
|
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||||
|
// server, and the server_name directive
|
||||||
|
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||||
|
// nginx server. More generically, the primary server name would be the host
|
||||||
|
// header value that matches the default virtual host of an HTTP server. It
|
||||||
|
// should include the host identifier and if a port is used to route to the
|
||||||
|
// server that port identifier should be included as an appropriate port
|
||||||
|
// suffix.
|
||||||
|
//
|
||||||
|
// If the primary server name is not known, server should be an empty string.
|
||||||
|
// The req Host will be used to determine the server instead.
|
||||||
|
//
|
||||||
|
// The following attributes are always returned: "http.method", "http.scheme",
|
||||||
|
// "http.flavor", "http.target", "net.host.name". The following attributes are
|
||||||
|
// returned if they related values are defined in req: "net.host.port",
|
||||||
|
// "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id",
|
||||||
|
// "http.client_ip".
|
||||||
|
func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.KeyValue {
|
||||||
|
// TODO: This currently does not add the specification required
|
||||||
|
// `http.target` attribute. It has too high of a cardinality to safely be
|
||||||
|
// added. An alternate should be added, or this comment removed, when it is
|
||||||
|
// addressed by the specification. If it is ultimately decided to continue
|
||||||
|
// not including the attribute, the HTTPTargetKey field of the httpConv
|
||||||
|
// should be removed as well.
|
||||||
|
|
||||||
|
n := 4 // Method, scheme, proto, and host name.
|
||||||
|
var host string
|
||||||
|
var p int
|
||||||
|
if server == "" {
|
||||||
|
host, p = splitHostPort(req.Host)
|
||||||
|
} else {
|
||||||
|
// Prioritize the primary server name.
|
||||||
|
host, p = splitHostPort(server)
|
||||||
|
if p < 0 {
|
||||||
|
_, p = splitHostPort(req.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||||
|
if hostPort > 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
peer, peerPort := splitHostPort(req.RemoteAddr)
|
||||||
|
if peer != "" {
|
||||||
|
n++
|
||||||
|
if peerPort > 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
useragent := req.UserAgent()
|
||||||
|
if useragent != "" {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
userID, _, hasUserID := req.BasicAuth()
|
||||||
|
if hasUserID {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
|
||||||
|
if clientIP != "" {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
attrs := make([]attribute.KeyValue, 0, n)
|
||||||
|
|
||||||
|
attrs = append(attrs, c.method(req.Method))
|
||||||
|
attrs = append(attrs, c.scheme(req.TLS != nil))
|
||||||
|
attrs = append(attrs, c.flavor(req.Proto))
|
||||||
|
attrs = append(attrs, c.NetConv.HostName(host))
|
||||||
|
|
||||||
|
if hostPort > 0 {
|
||||||
|
attrs = append(attrs, c.NetConv.HostPort(hostPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
if peer != "" {
|
||||||
|
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||||
|
// file-path that would be interpreted with a sock family.
|
||||||
|
attrs = append(attrs, c.NetConv.SockPeerAddr(peer))
|
||||||
|
if peerPort > 0 {
|
||||||
|
attrs = append(attrs, c.NetConv.SockPeerPort(peerPort))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if useragent != "" {
|
||||||
|
attrs = append(attrs, c.HTTPUserAgentKey.String(useragent))
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasUserID {
|
||||||
|
attrs = append(attrs, c.EnduserIDKey.String(userID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientIP != "" {
|
||||||
|
attrs = append(attrs, c.HTTPClientIPKey.String(clientIP))
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerRequestMetrics returns metric attributes for an HTTP request received
|
||||||
|
// by a server.
|
||||||
|
//
|
||||||
|
// The server must be the primary server name if it is known. For example this
|
||||||
|
// would be the ServerName directive
|
||||||
|
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||||
|
// server, and the server_name directive
|
||||||
|
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||||
|
// nginx server. More generically, the primary server name would be the host
|
||||||
|
// header value that matches the default virtual host of an HTTP server. It
|
||||||
|
// should include the host identifier and if a port is used to route to the
|
||||||
|
// server that port identifier should be included as an appropriate port
|
||||||
|
// suffix.
|
||||||
|
//
|
||||||
|
// If the primary server name is not known, server should be an empty string.
|
||||||
|
// The req Host will be used to determine the server instead.
|
||||||
|
//
|
||||||
|
// The following attributes are always returned: "http.method", "http.scheme",
|
||||||
|
// "http.flavor", "net.host.name". The following attributes are
|
||||||
|
// returned if they related values are defined in req: "net.host.port".
|
||||||
|
func (c *httpConv) ServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue {
|
||||||
|
// TODO: This currently does not add the specification required
|
||||||
|
// `http.target` attribute. It has too high of a cardinality to safely be
|
||||||
|
// added. An alternate should be added, or this comment removed, when it is
|
||||||
|
// addressed by the specification. If it is ultimately decided to continue
|
||||||
|
// not including the attribute, the HTTPTargetKey field of the httpConv
|
||||||
|
// should be removed as well.
|
||||||
|
|
||||||
|
n := 4 // Method, scheme, proto, and host name.
|
||||||
|
var host string
|
||||||
|
var p int
|
||||||
|
if server == "" {
|
||||||
|
host, p = splitHostPort(req.Host)
|
||||||
|
} else {
|
||||||
|
// Prioritize the primary server name.
|
||||||
|
host, p = splitHostPort(server)
|
||||||
|
if p < 0 {
|
||||||
|
_, p = splitHostPort(req.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||||
|
if hostPort > 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
attrs := make([]attribute.KeyValue, 0, n)
|
||||||
|
|
||||||
|
attrs = append(attrs, c.methodMetric(req.Method))
|
||||||
|
attrs = append(attrs, c.scheme(req.TLS != nil))
|
||||||
|
attrs = append(attrs, c.flavor(req.Proto))
|
||||||
|
attrs = append(attrs, c.NetConv.HostName(host))
|
||||||
|
|
||||||
|
if hostPort > 0 {
|
||||||
|
attrs = append(attrs, c.NetConv.HostPort(hostPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpConv) method(method string) attribute.KeyValue {
|
||||||
|
if method == "" {
|
||||||
|
return c.HTTPMethodKey.String(http.MethodGet)
|
||||||
|
}
|
||||||
|
return c.HTTPMethodKey.String(method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpConv) methodMetric(method string) attribute.KeyValue {
|
||||||
|
method = strings.ToUpper(method)
|
||||||
|
switch method {
|
||||||
|
case http.MethodConnect, http.MethodDelete, http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodTrace:
|
||||||
|
default:
|
||||||
|
method = "_OTHER"
|
||||||
|
}
|
||||||
|
return c.HTTPMethodKey.String(method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpConv) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||||
|
if https {
|
||||||
|
return c.HTTPSchemeHTTPS
|
||||||
|
}
|
||||||
|
return c.HTTPSchemeHTTP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpConv) flavor(proto string) attribute.KeyValue {
|
||||||
|
switch proto {
|
||||||
|
case "HTTP/1.0":
|
||||||
|
return c.HTTPFlavorKey.String("1.0")
|
||||||
|
case "HTTP/1.1":
|
||||||
|
return c.HTTPFlavorKey.String("1.1")
|
||||||
|
case "HTTP/2":
|
||||||
|
return c.HTTPFlavorKey.String("2.0")
|
||||||
|
case "HTTP/3":
|
||||||
|
return c.HTTPFlavorKey.String("3.0")
|
||||||
|
default:
|
||||||
|
return c.HTTPFlavorKey.String(proto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverClientIP(xForwardedFor string) string {
|
||||||
|
if idx := strings.Index(xForwardedFor, ","); idx >= 0 {
|
||||||
|
xForwardedFor = xForwardedFor[:idx]
|
||||||
|
}
|
||||||
|
return xForwardedFor
|
||||||
|
}
|
||||||
|
|
||||||
|
func requiredHTTPPort(https bool, port int) int { // nolint:revive
|
||||||
|
if https {
|
||||||
|
if port > 0 && port != 443 {
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if port > 0 && port != 80 {
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the request host and port from the first non-empty source.
|
||||||
|
func firstHostPort(source ...string) (host string, port int) {
|
||||||
|
for _, hostport := range source {
|
||||||
|
host, port = splitHostPort(hostport)
|
||||||
|
if host != "" || port > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestHeader returns the contents of h as OpenTelemetry attributes.
|
||||||
|
func (c *httpConv) RequestHeader(h http.Header) []attribute.KeyValue {
|
||||||
|
return c.header("http.request.header", h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseHeader returns the contents of h as OpenTelemetry attributes.
|
||||||
|
func (c *httpConv) ResponseHeader(h http.Header) []attribute.KeyValue {
|
||||||
|
return c.header("http.response.header", h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpConv) header(prefix string, h http.Header) []attribute.KeyValue {
|
||||||
|
key := func(k string) attribute.Key {
|
||||||
|
k = strings.ToLower(k)
|
||||||
|
k = strings.ReplaceAll(k, "-", "_")
|
||||||
|
k = fmt.Sprintf("%s.%s", prefix, k)
|
||||||
|
return attribute.Key(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs := make([]attribute.KeyValue, 0, len(h))
|
||||||
|
for k, v := range h {
|
||||||
|
attrs = append(attrs, key(k).StringSlice(v))
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientStatus returns a span status code and message for an HTTP status code
|
||||||
|
// value received by a client.
|
||||||
|
func (c *httpConv) ClientStatus(code int) (codes.Code, string) {
|
||||||
|
if code < 100 || code >= 600 {
|
||||||
|
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||||
|
}
|
||||||
|
if code >= 400 {
|
||||||
|
return codes.Error, ""
|
||||||
|
}
|
||||||
|
return codes.Unset, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerStatus returns a span status code and message for an HTTP status code
|
||||||
|
// value returned by a server. Status codes in the 400-499 range are not
|
||||||
|
// returned as errors.
|
||||||
|
func (c *httpConv) ServerStatus(code int) (codes.Code, string) {
|
||||||
|
if code < 100 || code >= 600 {
|
||||||
|
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||||
|
}
|
||||||
|
if code >= 500 {
|
||||||
|
return codes.Error, ""
|
||||||
|
}
|
||||||
|
return codes.Unset, ""
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
|
// Code created by gotmpl. DO NOT MODIFY.
|
||||||
|
// source: internal/shared/semconvutil/netconv.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
@ -12,7 +14,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package internal // import "go.opentelemetry.io/otel/semconv/internal/v2"
|
package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
@ -20,11 +22,37 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NetConv are the network semantic convention attributes defined for a version
|
// NetTransport returns a trace attribute describing the transport protocol of the
|
||||||
|
// passed network. See the net.Dial for information about acceptable network
|
||||||
|
// values.
|
||||||
|
func NetTransport(network string) attribute.KeyValue {
|
||||||
|
return nc.Transport(network)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetClient returns trace attributes for a client network connection to address.
|
||||||
|
// See net.Dial for information about acceptable address values, address should
|
||||||
|
// be the same as the one used to create conn. If conn is nil, only network
|
||||||
|
// peer attributes will be returned that describe address. Otherwise, the
|
||||||
|
// socket level information about conn will also be included.
|
||||||
|
func NetClient(address string, conn net.Conn) []attribute.KeyValue {
|
||||||
|
return nc.Client(address, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetServer returns trace attributes for a network listener listening at address.
|
||||||
|
// See net.Listen for information about acceptable address values, address
|
||||||
|
// should be the same as the one used to create ln. If ln is nil, only network
|
||||||
|
// host attributes will be returned that describe address. Otherwise, the
|
||||||
|
// socket level information about ln will also be included.
|
||||||
|
func NetServer(address string, ln net.Listener) []attribute.KeyValue {
|
||||||
|
return nc.Server(address, ln)
|
||||||
|
}
|
||||||
|
|
||||||
|
// netConv are the network semantic convention attributes defined for a version
|
||||||
// of the OpenTelemetry specification.
|
// of the OpenTelemetry specification.
|
||||||
type NetConv struct {
|
type netConv struct {
|
||||||
NetHostNameKey attribute.Key
|
NetHostNameKey attribute.Key
|
||||||
NetHostPortKey attribute.Key
|
NetHostPortKey attribute.Key
|
||||||
NetPeerNameKey attribute.Key
|
NetPeerNameKey attribute.Key
|
||||||
@ -40,7 +68,23 @@ type NetConv struct {
|
|||||||
NetTransportInProc attribute.KeyValue
|
NetTransportInProc attribute.KeyValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NetConv) Transport(network string) attribute.KeyValue {
|
var nc = &netConv{
|
||||||
|
NetHostNameKey: semconv.NetHostNameKey,
|
||||||
|
NetHostPortKey: semconv.NetHostPortKey,
|
||||||
|
NetPeerNameKey: semconv.NetPeerNameKey,
|
||||||
|
NetPeerPortKey: semconv.NetPeerPortKey,
|
||||||
|
NetSockFamilyKey: semconv.NetSockFamilyKey,
|
||||||
|
NetSockPeerAddrKey: semconv.NetSockPeerAddrKey,
|
||||||
|
NetSockPeerPortKey: semconv.NetSockPeerPortKey,
|
||||||
|
NetSockHostAddrKey: semconv.NetSockHostAddrKey,
|
||||||
|
NetSockHostPortKey: semconv.NetSockHostPortKey,
|
||||||
|
NetTransportOther: semconv.NetTransportOther,
|
||||||
|
NetTransportTCP: semconv.NetTransportTCP,
|
||||||
|
NetTransportUDP: semconv.NetTransportUDP,
|
||||||
|
NetTransportInProc: semconv.NetTransportInProc,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *netConv) Transport(network string) attribute.KeyValue {
|
||||||
switch network {
|
switch network {
|
||||||
case "tcp", "tcp4", "tcp6":
|
case "tcp", "tcp4", "tcp6":
|
||||||
return c.NetTransportTCP
|
return c.NetTransportTCP
|
||||||
@ -55,7 +99,7 @@ func (c *NetConv) Transport(network string) attribute.KeyValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Host returns attributes for a network host address.
|
// Host returns attributes for a network host address.
|
||||||
func (c *NetConv) Host(address string) []attribute.KeyValue {
|
func (c *netConv) Host(address string) []attribute.KeyValue {
|
||||||
h, p := splitHostPort(address)
|
h, p := splitHostPort(address)
|
||||||
var n int
|
var n int
|
||||||
if h != "" {
|
if h != "" {
|
||||||
@ -82,7 +126,7 @@ func (c *NetConv) Host(address string) []attribute.KeyValue {
|
|||||||
// be the same as the one used to create ln. If ln is nil, only network host
|
// be the same as the one used to create ln. If ln is nil, only network host
|
||||||
// attributes will be returned that describe address. Otherwise, the socket
|
// attributes will be returned that describe address. Otherwise, the socket
|
||||||
// level information about ln will also be included.
|
// level information about ln will also be included.
|
||||||
func (c *NetConv) Server(address string, ln net.Listener) []attribute.KeyValue {
|
func (c *netConv) Server(address string, ln net.Listener) []attribute.KeyValue {
|
||||||
if ln == nil {
|
if ln == nil {
|
||||||
return c.Host(address)
|
return c.Host(address)
|
||||||
}
|
}
|
||||||
@ -123,11 +167,11 @@ func (c *NetConv) Server(address string, ln net.Listener) []attribute.KeyValue {
|
|||||||
return attr
|
return attr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NetConv) HostName(name string) attribute.KeyValue {
|
func (c *netConv) HostName(name string) attribute.KeyValue {
|
||||||
return c.NetHostNameKey.String(name)
|
return c.NetHostNameKey.String(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NetConv) HostPort(port int) attribute.KeyValue {
|
func (c *netConv) HostPort(port int) attribute.KeyValue {
|
||||||
return c.NetHostPortKey.Int(port)
|
return c.NetHostPortKey.Int(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +180,7 @@ func (c *NetConv) HostPort(port int) attribute.KeyValue {
|
|||||||
// the same as the one used to create conn. If conn is nil, only network peer
|
// the same as the one used to create conn. If conn is nil, only network peer
|
||||||
// attributes will be returned that describe address. Otherwise, the socket
|
// attributes will be returned that describe address. Otherwise, the socket
|
||||||
// level information about conn will also be included.
|
// level information about conn will also be included.
|
||||||
func (c *NetConv) Client(address string, conn net.Conn) []attribute.KeyValue {
|
func (c *netConv) Client(address string, conn net.Conn) []attribute.KeyValue {
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
return c.Peer(address)
|
return c.Peer(address)
|
||||||
}
|
}
|
||||||
@ -246,7 +290,7 @@ func positiveInt(ints ...int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Peer returns attributes for a network peer address.
|
// Peer returns attributes for a network peer address.
|
||||||
func (c *NetConv) Peer(address string) []attribute.KeyValue {
|
func (c *netConv) Peer(address string) []attribute.KeyValue {
|
||||||
h, p := splitHostPort(address)
|
h, p := splitHostPort(address)
|
||||||
var n int
|
var n int
|
||||||
if h != "" {
|
if h != "" {
|
||||||
@ -268,19 +312,19 @@ func (c *NetConv) Peer(address string) []attribute.KeyValue {
|
|||||||
return attrs
|
return attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NetConv) PeerName(name string) attribute.KeyValue {
|
func (c *netConv) PeerName(name string) attribute.KeyValue {
|
||||||
return c.NetPeerNameKey.String(name)
|
return c.NetPeerNameKey.String(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NetConv) PeerPort(port int) attribute.KeyValue {
|
func (c *netConv) PeerPort(port int) attribute.KeyValue {
|
||||||
return c.NetPeerPortKey.Int(port)
|
return c.NetPeerPortKey.Int(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NetConv) SockPeerAddr(addr string) attribute.KeyValue {
|
func (c *netConv) SockPeerAddr(addr string) attribute.KeyValue {
|
||||||
return c.NetSockPeerAddrKey.String(addr)
|
return c.NetSockPeerAddrKey.String(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NetConv) SockPeerPort(port int) attribute.KeyValue {
|
func (c *netConv) SockPeerPort(port int) attribute.KeyValue {
|
||||||
return c.NetSockPeerPortKey.Int(port)
|
return c.NetSockPeerPortKey.Int(port)
|
||||||
}
|
}
|
||||||
|
|
65
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/labeler.go
generated
vendored
Normal file
65
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/labeler.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Labeler is used to allow instrumented HTTP handlers to add custom attributes to
|
||||||
|
// the metrics recorded by the net/http instrumentation.
|
||||||
|
type Labeler struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
attributes []attribute.KeyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add attributes to a Labeler.
|
||||||
|
func (l *Labeler) Add(ls ...attribute.KeyValue) {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
l.attributes = append(l.attributes, ls...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a copy of the attributes added to the Labeler.
|
||||||
|
func (l *Labeler) Get() []attribute.KeyValue {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
ret := make([]attribute.KeyValue, len(l.attributes))
|
||||||
|
copy(ret, l.attributes)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
type labelerContextKeyType int
|
||||||
|
|
||||||
|
const lablelerContextKey labelerContextKeyType = 0
|
||||||
|
|
||||||
|
func injectLabeler(ctx context.Context, l *Labeler) context.Context {
|
||||||
|
return context.WithValue(ctx, lablelerContextKey, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelerFromContext retrieves a Labeler instance from the provided context if
|
||||||
|
// one is available. If no Labeler was found in the provided context a new, empty
|
||||||
|
// Labeler is returned and the second return value is false. In this case it is
|
||||||
|
// safe to use the Labeler but any attributes added to it will not be used.
|
||||||
|
func LabelerFromContext(ctx context.Context) (*Labeler, bool) {
|
||||||
|
l, ok := ctx.Value(lablelerContextKey).(*Labeler)
|
||||||
|
if !ok {
|
||||||
|
l = &Labeler{}
|
||||||
|
}
|
||||||
|
return l, ok
|
||||||
|
}
|
193
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go
generated
vendored
Normal file
193
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transport implements the http.RoundTripper interface and wraps
|
||||||
|
// outbound HTTP(S) requests with a span.
|
||||||
|
type Transport struct {
|
||||||
|
rt http.RoundTripper
|
||||||
|
|
||||||
|
tracer trace.Tracer
|
||||||
|
propagators propagation.TextMapPropagator
|
||||||
|
spanStartOptions []trace.SpanStartOption
|
||||||
|
filters []Filter
|
||||||
|
spanNameFormatter func(string, *http.Request) string
|
||||||
|
clientTrace func(context.Context) *httptrace.ClientTrace
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ http.RoundTripper = &Transport{}
|
||||||
|
|
||||||
|
// NewTransport wraps the provided http.RoundTripper with one that
|
||||||
|
// starts a span and injects the span context into the outbound request headers.
|
||||||
|
//
|
||||||
|
// If the provided http.RoundTripper is nil, http.DefaultTransport will be used
|
||||||
|
// as the base http.RoundTripper.
|
||||||
|
func NewTransport(base http.RoundTripper, opts ...Option) *Transport {
|
||||||
|
if base == nil {
|
||||||
|
base = http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
t := Transport{
|
||||||
|
rt: base,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultOpts := []Option{
|
||||||
|
WithSpanOptions(trace.WithSpanKind(trace.SpanKindClient)),
|
||||||
|
WithSpanNameFormatter(defaultTransportFormatter),
|
||||||
|
}
|
||||||
|
|
||||||
|
c := newConfig(append(defaultOpts, opts...)...)
|
||||||
|
t.applyConfig(c)
|
||||||
|
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transport) applyConfig(c *config) {
|
||||||
|
t.tracer = c.Tracer
|
||||||
|
t.propagators = c.Propagators
|
||||||
|
t.spanStartOptions = c.SpanStartOptions
|
||||||
|
t.filters = c.Filters
|
||||||
|
t.spanNameFormatter = c.SpanNameFormatter
|
||||||
|
t.clientTrace = c.ClientTrace
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultTransportFormatter(_ string, r *http.Request) string {
|
||||||
|
return "HTTP " + r.Method
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTrip creates a Span and propagates its context via the provided request's headers
|
||||||
|
// before handing the request to the configured base RoundTripper. The created span will
|
||||||
|
// end when the response body is closed or when a read from the body returns io.EOF.
|
||||||
|
func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||||
|
for _, f := range t.filters {
|
||||||
|
if !f(r) {
|
||||||
|
// Simply pass through to the base RoundTripper if a filter rejects the request
|
||||||
|
return t.rt.RoundTrip(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer := t.tracer
|
||||||
|
|
||||||
|
if tracer == nil {
|
||||||
|
if span := trace.SpanFromContext(r.Context()); span.SpanContext().IsValid() {
|
||||||
|
tracer = newTracer(span.TracerProvider())
|
||||||
|
} else {
|
||||||
|
tracer = newTracer(otel.GetTracerProvider())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := append([]trace.SpanStartOption{}, t.spanStartOptions...) // start with the configured options
|
||||||
|
|
||||||
|
ctx, span := tracer.Start(r.Context(), t.spanNameFormatter("", r), opts...)
|
||||||
|
|
||||||
|
if t.clientTrace != nil {
|
||||||
|
ctx = httptrace.WithClientTrace(ctx, t.clientTrace(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request.
|
||||||
|
span.SetAttributes(semconvutil.HTTPClientRequest(r)...)
|
||||||
|
t.propagators.Inject(ctx, propagation.HeaderCarrier(r.Header))
|
||||||
|
|
||||||
|
res, err := t.rt.RoundTrip(r)
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
span.End()
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
span.SetAttributes(semconvutil.HTTPClientResponse(res)...)
|
||||||
|
span.SetStatus(semconvutil.HTTPClientStatus(res.StatusCode))
|
||||||
|
res.Body = newWrappedBody(span, res.Body)
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// newWrappedBody returns a new and appropriately scoped *wrappedBody as an
|
||||||
|
// io.ReadCloser. If the passed body implements io.Writer, the returned value
|
||||||
|
// will implement io.ReadWriteCloser.
|
||||||
|
func newWrappedBody(span trace.Span, body io.ReadCloser) io.ReadCloser {
|
||||||
|
// The successful protocol switch responses will have a body that
|
||||||
|
// implement an io.ReadWriteCloser. Ensure this interface type continues
|
||||||
|
// to be satisfied if that is the case.
|
||||||
|
if _, ok := body.(io.ReadWriteCloser); ok {
|
||||||
|
return &wrappedBody{span: span, body: body}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the implementation of the io.ReadWriteCloser and only implement
|
||||||
|
// the io.ReadCloser.
|
||||||
|
return struct{ io.ReadCloser }{&wrappedBody{span: span, body: body}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrappedBody is the response body type returned by the transport
|
||||||
|
// instrumentation to complete a span. Errors encountered when using the
|
||||||
|
// response body are recorded in span tracking the response.
|
||||||
|
//
|
||||||
|
// The span tracking the response is ended when this body is closed.
|
||||||
|
//
|
||||||
|
// If the response body implements the io.Writer interface (i.e. for
|
||||||
|
// successful protocol switches), the wrapped body also will.
|
||||||
|
type wrappedBody struct {
|
||||||
|
span trace.Span
|
||||||
|
body io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ io.ReadWriteCloser = &wrappedBody{}
|
||||||
|
|
||||||
|
func (wb *wrappedBody) Write(p []byte) (int, error) {
|
||||||
|
// This will not panic given the guard in newWrappedBody.
|
||||||
|
n, err := wb.body.(io.Writer).Write(p)
|
||||||
|
if err != nil {
|
||||||
|
wb.span.RecordError(err)
|
||||||
|
wb.span.SetStatus(codes.Error, err.Error())
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wb *wrappedBody) Read(b []byte) (int, error) {
|
||||||
|
n, err := wb.body.Read(b)
|
||||||
|
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
// nothing to do here but fall through to the return
|
||||||
|
case io.EOF:
|
||||||
|
wb.span.End()
|
||||||
|
default:
|
||||||
|
wb.span.RecordError(err)
|
||||||
|
wb.span.SetStatus(codes.Error, err.Error())
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wb *wrappedBody) Close() error {
|
||||||
|
wb.span.End()
|
||||||
|
if wb.body != nil {
|
||||||
|
return wb.body.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
28
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go
generated
vendored
Normal file
28
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
|
||||||
|
// Version is the current release version of the otelhttp instrumentation.
|
||||||
|
func Version() string {
|
||||||
|
return "0.45.0"
|
||||||
|
// This string is updated by the pre_release.sh script during release
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemVersion is the semantic version to be supplied to tracer/meter creation.
|
||||||
|
//
|
||||||
|
// Deprecated: Use [Version] instead.
|
||||||
|
func SemVersion() string {
|
||||||
|
return Version()
|
||||||
|
}
|
99
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/wrap.go
generated
vendored
Normal file
99
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/wrap.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ io.ReadCloser = &bodyWrapper{}
|
||||||
|
|
||||||
|
// bodyWrapper wraps a http.Request.Body (an io.ReadCloser) to track the number
|
||||||
|
// of bytes read and the last error.
|
||||||
|
type bodyWrapper struct {
|
||||||
|
io.ReadCloser
|
||||||
|
record func(n int64) // must not be nil
|
||||||
|
|
||||||
|
read int64
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *bodyWrapper) Read(b []byte) (int, error) {
|
||||||
|
n, err := w.ReadCloser.Read(b)
|
||||||
|
n1 := int64(n)
|
||||||
|
w.read += n1
|
||||||
|
w.err = err
|
||||||
|
w.record(n1)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *bodyWrapper) Close() error {
|
||||||
|
return w.ReadCloser.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ http.ResponseWriter = &respWriterWrapper{}
|
||||||
|
|
||||||
|
// respWriterWrapper wraps a http.ResponseWriter in order to track the number of
|
||||||
|
// bytes written, the last error, and to catch the first written statusCode.
|
||||||
|
// TODO: The wrapped http.ResponseWriter doesn't implement any of the optional
|
||||||
|
// types (http.Hijacker, http.Pusher, http.CloseNotifier, http.Flusher, etc)
|
||||||
|
// that may be useful when using it in real life situations.
|
||||||
|
type respWriterWrapper struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
record func(n int64) // must not be nil
|
||||||
|
|
||||||
|
// used to inject the header
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
props propagation.TextMapPropagator
|
||||||
|
|
||||||
|
written int64
|
||||||
|
statusCode int
|
||||||
|
err error
|
||||||
|
wroteHeader bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriterWrapper) Header() http.Header {
|
||||||
|
return w.ResponseWriter.Header()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriterWrapper) Write(p []byte) (int, error) {
|
||||||
|
if !w.wroteHeader {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
n, err := w.ResponseWriter.Write(p)
|
||||||
|
n1 := int64(n)
|
||||||
|
w.record(n1)
|
||||||
|
w.written += n1
|
||||||
|
w.err = err
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader persists initial statusCode for span attribution.
|
||||||
|
// All calls to WriteHeader will be propagated to the underlying ResponseWriter
|
||||||
|
// and will persist the statusCode from the first call.
|
||||||
|
// Blocking consecutive calls to WriteHeader alters expected behavior and will
|
||||||
|
// remove warning logs from net/http where developers will notice incorrect handler implementations.
|
||||||
|
func (w *respWriterWrapper) WriteHeader(statusCode int) {
|
||||||
|
if !w.wroteHeader {
|
||||||
|
w.wroteHeader = true
|
||||||
|
w.statusCode = statusCode
|
||||||
|
}
|
||||||
|
w.ResponseWriter.WriteHeader(statusCode)
|
||||||
|
}
|
404
vendor/go.opentelemetry.io/otel/semconv/internal/v2/http.go
generated
vendored
404
vendor/go.opentelemetry.io/otel/semconv/internal/v2/http.go
generated
vendored
@ -1,404 +0,0 @@
|
|||||||
// Copyright The OpenTelemetry Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package internal // import "go.opentelemetry.io/otel/semconv/internal/v2"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/codes"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HTTPConv are the HTTP semantic convention attributes defined for a version
|
|
||||||
// of the OpenTelemetry specification.
|
|
||||||
type HTTPConv struct {
|
|
||||||
NetConv *NetConv
|
|
||||||
|
|
||||||
EnduserIDKey attribute.Key
|
|
||||||
HTTPClientIPKey attribute.Key
|
|
||||||
HTTPFlavorKey attribute.Key
|
|
||||||
HTTPMethodKey attribute.Key
|
|
||||||
HTTPRequestContentLengthKey attribute.Key
|
|
||||||
HTTPResponseContentLengthKey attribute.Key
|
|
||||||
HTTPRouteKey attribute.Key
|
|
||||||
HTTPSchemeHTTP attribute.KeyValue
|
|
||||||
HTTPSchemeHTTPS attribute.KeyValue
|
|
||||||
HTTPStatusCodeKey attribute.Key
|
|
||||||
HTTPTargetKey attribute.Key
|
|
||||||
HTTPURLKey attribute.Key
|
|
||||||
HTTPUserAgentKey attribute.Key
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientResponse returns attributes for an HTTP response received by a client
|
|
||||||
// from a server. The following attributes are returned if the related values
|
|
||||||
// are defined in resp: "http.status.code", "http.response_content_length".
|
|
||||||
//
|
|
||||||
// This does not add all OpenTelemetry required attributes for an HTTP event,
|
|
||||||
// it assumes ClientRequest was used to create the span with a complete set of
|
|
||||||
// attributes. If a complete set of attributes can be generated using the
|
|
||||||
// request contained in resp. For example:
|
|
||||||
//
|
|
||||||
// append(ClientResponse(resp), ClientRequest(resp.Request)...)
|
|
||||||
func (c *HTTPConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
|
|
||||||
var n int
|
|
||||||
if resp.StatusCode > 0 {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if resp.ContentLength > 0 {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs := make([]attribute.KeyValue, 0, n)
|
|
||||||
if resp.StatusCode > 0 {
|
|
||||||
attrs = append(attrs, c.HTTPStatusCodeKey.Int(resp.StatusCode))
|
|
||||||
}
|
|
||||||
if resp.ContentLength > 0 {
|
|
||||||
attrs = append(attrs, c.HTTPResponseContentLengthKey.Int(int(resp.ContentLength)))
|
|
||||||
}
|
|
||||||
return attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientRequest returns attributes for an HTTP request made by a client. The
|
|
||||||
// following attributes are always returned: "http.url", "http.flavor",
|
|
||||||
// "http.method", "net.peer.name". The following attributes are returned if the
|
|
||||||
// related values are defined in req: "net.peer.port", "http.user_agent",
|
|
||||||
// "http.request_content_length", "enduser.id".
|
|
||||||
func (c *HTTPConv) ClientRequest(req *http.Request) []attribute.KeyValue {
|
|
||||||
n := 3 // URL, peer name, proto, and method.
|
|
||||||
var h string
|
|
||||||
if req.URL != nil {
|
|
||||||
h = req.URL.Host
|
|
||||||
}
|
|
||||||
peer, p := firstHostPort(h, req.Header.Get("Host"))
|
|
||||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p)
|
|
||||||
if port > 0 {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
useragent := req.UserAgent()
|
|
||||||
if useragent != "" {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if req.ContentLength > 0 {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
userID, _, hasUserID := req.BasicAuth()
|
|
||||||
if hasUserID {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
attrs := make([]attribute.KeyValue, 0, n)
|
|
||||||
|
|
||||||
attrs = append(attrs, c.method(req.Method))
|
|
||||||
attrs = append(attrs, c.proto(req.Proto))
|
|
||||||
|
|
||||||
var u string
|
|
||||||
if req.URL != nil {
|
|
||||||
// Remove any username/password info that may be in the URL.
|
|
||||||
userinfo := req.URL.User
|
|
||||||
req.URL.User = nil
|
|
||||||
u = req.URL.String()
|
|
||||||
// Restore any username/password info that was removed.
|
|
||||||
req.URL.User = userinfo
|
|
||||||
}
|
|
||||||
attrs = append(attrs, c.HTTPURLKey.String(u))
|
|
||||||
|
|
||||||
attrs = append(attrs, c.NetConv.PeerName(peer))
|
|
||||||
if port > 0 {
|
|
||||||
attrs = append(attrs, c.NetConv.PeerPort(port))
|
|
||||||
}
|
|
||||||
|
|
||||||
if useragent != "" {
|
|
||||||
attrs = append(attrs, c.HTTPUserAgentKey.String(useragent))
|
|
||||||
}
|
|
||||||
|
|
||||||
if l := req.ContentLength; l > 0 {
|
|
||||||
attrs = append(attrs, c.HTTPRequestContentLengthKey.Int64(l))
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasUserID {
|
|
||||||
attrs = append(attrs, c.EnduserIDKey.String(userID))
|
|
||||||
}
|
|
||||||
|
|
||||||
return attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerRequest returns attributes for an HTTP request received by a server.
|
|
||||||
//
|
|
||||||
// The server must be the primary server name if it is known. For example this
|
|
||||||
// would be the ServerName directive
|
|
||||||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
|
||||||
// server, and the server_name directive
|
|
||||||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
|
||||||
// nginx server. More generically, the primary server name would be the host
|
|
||||||
// header value that matches the default virtual host of an HTTP server. It
|
|
||||||
// should include the host identifier and if a port is used to route to the
|
|
||||||
// server that port identifier should be included as an appropriate port
|
|
||||||
// suffix.
|
|
||||||
//
|
|
||||||
// If the primary server name is not known, server should be an empty string.
|
|
||||||
// The req Host will be used to determine the server instead.
|
|
||||||
//
|
|
||||||
// The following attributes are always returned: "http.method", "http.scheme",
|
|
||||||
// "http.flavor", "http.target", "net.host.name". The following attributes are
|
|
||||||
// returned if they related values are defined in req: "net.host.port",
|
|
||||||
// "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id",
|
|
||||||
// "http.client_ip".
|
|
||||||
func (c *HTTPConv) ServerRequest(server string, req *http.Request) []attribute.KeyValue {
|
|
||||||
// TODO: This currently does not add the specification required
|
|
||||||
// `http.target` attribute. It has too high of a cardinality to safely be
|
|
||||||
// added. An alternate should be added, or this comment removed, when it is
|
|
||||||
// addressed by the specification. If it is ultimately decided to continue
|
|
||||||
// not including the attribute, the HTTPTargetKey field of the HTTPConv
|
|
||||||
// should be removed as well.
|
|
||||||
|
|
||||||
n := 4 // Method, scheme, proto, and host name.
|
|
||||||
var host string
|
|
||||||
var p int
|
|
||||||
if server == "" {
|
|
||||||
host, p = splitHostPort(req.Host)
|
|
||||||
} else {
|
|
||||||
// Prioritize the primary server name.
|
|
||||||
host, p = splitHostPort(server)
|
|
||||||
if p < 0 {
|
|
||||||
_, p = splitHostPort(req.Host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
|
||||||
if hostPort > 0 {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
peer, peerPort := splitHostPort(req.RemoteAddr)
|
|
||||||
if peer != "" {
|
|
||||||
n++
|
|
||||||
if peerPort > 0 {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
useragent := req.UserAgent()
|
|
||||||
if useragent != "" {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
userID, _, hasUserID := req.BasicAuth()
|
|
||||||
if hasUserID {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
|
|
||||||
if clientIP != "" {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
attrs := make([]attribute.KeyValue, 0, n)
|
|
||||||
|
|
||||||
attrs = append(attrs, c.method(req.Method))
|
|
||||||
attrs = append(attrs, c.scheme(req.TLS != nil))
|
|
||||||
attrs = append(attrs, c.proto(req.Proto))
|
|
||||||
attrs = append(attrs, c.NetConv.HostName(host))
|
|
||||||
|
|
||||||
if hostPort > 0 {
|
|
||||||
attrs = append(attrs, c.NetConv.HostPort(hostPort))
|
|
||||||
}
|
|
||||||
|
|
||||||
if peer != "" {
|
|
||||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
|
||||||
// file-path that would be interpreted with a sock family.
|
|
||||||
attrs = append(attrs, c.NetConv.SockPeerAddr(peer))
|
|
||||||
if peerPort > 0 {
|
|
||||||
attrs = append(attrs, c.NetConv.SockPeerPort(peerPort))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if useragent != "" {
|
|
||||||
attrs = append(attrs, c.HTTPUserAgentKey.String(useragent))
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasUserID {
|
|
||||||
attrs = append(attrs, c.EnduserIDKey.String(userID))
|
|
||||||
}
|
|
||||||
|
|
||||||
if clientIP != "" {
|
|
||||||
attrs = append(attrs, c.HTTPClientIPKey.String(clientIP))
|
|
||||||
}
|
|
||||||
|
|
||||||
return attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HTTPConv) method(method string) attribute.KeyValue {
|
|
||||||
if method == "" {
|
|
||||||
return c.HTTPMethodKey.String(http.MethodGet)
|
|
||||||
}
|
|
||||||
return c.HTTPMethodKey.String(method)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HTTPConv) scheme(https bool) attribute.KeyValue { // nolint:revive
|
|
||||||
if https {
|
|
||||||
return c.HTTPSchemeHTTPS
|
|
||||||
}
|
|
||||||
return c.HTTPSchemeHTTP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HTTPConv) proto(proto string) attribute.KeyValue {
|
|
||||||
switch proto {
|
|
||||||
case "HTTP/1.0":
|
|
||||||
return c.HTTPFlavorKey.String("1.0")
|
|
||||||
case "HTTP/1.1":
|
|
||||||
return c.HTTPFlavorKey.String("1.1")
|
|
||||||
case "HTTP/2":
|
|
||||||
return c.HTTPFlavorKey.String("2.0")
|
|
||||||
case "HTTP/3":
|
|
||||||
return c.HTTPFlavorKey.String("3.0")
|
|
||||||
default:
|
|
||||||
return c.HTTPFlavorKey.String(proto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func serverClientIP(xForwardedFor string) string {
|
|
||||||
if idx := strings.Index(xForwardedFor, ","); idx >= 0 {
|
|
||||||
xForwardedFor = xForwardedFor[:idx]
|
|
||||||
}
|
|
||||||
return xForwardedFor
|
|
||||||
}
|
|
||||||
|
|
||||||
func requiredHTTPPort(https bool, port int) int { // nolint:revive
|
|
||||||
if https {
|
|
||||||
if port > 0 && port != 443 {
|
|
||||||
return port
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if port > 0 && port != 80 {
|
|
||||||
return port
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the request host and port from the first non-empty source.
|
|
||||||
func firstHostPort(source ...string) (host string, port int) {
|
|
||||||
for _, hostport := range source {
|
|
||||||
host, port = splitHostPort(hostport)
|
|
||||||
if host != "" || port > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequestHeader returns the contents of h as OpenTelemetry attributes.
|
|
||||||
func (c *HTTPConv) RequestHeader(h http.Header) []attribute.KeyValue {
|
|
||||||
return c.header("http.request.header", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResponseHeader returns the contents of h as OpenTelemetry attributes.
|
|
||||||
func (c *HTTPConv) ResponseHeader(h http.Header) []attribute.KeyValue {
|
|
||||||
return c.header("http.response.header", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *HTTPConv) header(prefix string, h http.Header) []attribute.KeyValue {
|
|
||||||
key := func(k string) attribute.Key {
|
|
||||||
k = strings.ToLower(k)
|
|
||||||
k = strings.ReplaceAll(k, "-", "_")
|
|
||||||
k = fmt.Sprintf("%s.%s", prefix, k)
|
|
||||||
return attribute.Key(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs := make([]attribute.KeyValue, 0, len(h))
|
|
||||||
for k, v := range h {
|
|
||||||
attrs = append(attrs, key(k).StringSlice(v))
|
|
||||||
}
|
|
||||||
return attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientStatus returns a span status code and message for an HTTP status code
|
|
||||||
// value received by a client.
|
|
||||||
func (c *HTTPConv) ClientStatus(code int) (codes.Code, string) {
|
|
||||||
stat, valid := validateHTTPStatusCode(code)
|
|
||||||
if !valid {
|
|
||||||
return stat, fmt.Sprintf("Invalid HTTP status code %d", code)
|
|
||||||
}
|
|
||||||
return stat, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerStatus returns a span status code and message for an HTTP status code
|
|
||||||
// value returned by a server. Status codes in the 400-499 range are not
|
|
||||||
// returned as errors.
|
|
||||||
func (c *HTTPConv) ServerStatus(code int) (codes.Code, string) {
|
|
||||||
stat, valid := validateHTTPStatusCode(code)
|
|
||||||
if !valid {
|
|
||||||
return stat, fmt.Sprintf("Invalid HTTP status code %d", code)
|
|
||||||
}
|
|
||||||
|
|
||||||
if code/100 == 4 {
|
|
||||||
return codes.Unset, ""
|
|
||||||
}
|
|
||||||
return stat, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type codeRange struct {
|
|
||||||
fromInclusive int
|
|
||||||
toInclusive int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r codeRange) contains(code int) bool {
|
|
||||||
return r.fromInclusive <= code && code <= r.toInclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
var validRangesPerCategory = map[int][]codeRange{
|
|
||||||
1: {
|
|
||||||
{http.StatusContinue, http.StatusEarlyHints},
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
{http.StatusOK, http.StatusAlreadyReported},
|
|
||||||
{http.StatusIMUsed, http.StatusIMUsed},
|
|
||||||
},
|
|
||||||
3: {
|
|
||||||
{http.StatusMultipleChoices, http.StatusUseProxy},
|
|
||||||
{http.StatusTemporaryRedirect, http.StatusPermanentRedirect},
|
|
||||||
},
|
|
||||||
4: {
|
|
||||||
{http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful…
|
|
||||||
{http.StatusMisdirectedRequest, http.StatusUpgradeRequired},
|
|
||||||
{http.StatusPreconditionRequired, http.StatusTooManyRequests},
|
|
||||||
{http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge},
|
|
||||||
{http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons},
|
|
||||||
},
|
|
||||||
5: {
|
|
||||||
{http.StatusInternalServerError, http.StatusLoopDetected},
|
|
||||||
{http.StatusNotExtended, http.StatusNetworkAuthenticationRequired},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateHTTPStatusCode validates the HTTP status code and returns
|
|
||||||
// corresponding span status code. If the `code` is not a valid HTTP status
|
|
||||||
// code, returns span status Error and false.
|
|
||||||
func validateHTTPStatusCode(code int) (codes.Code, bool) {
|
|
||||||
category := code / 100
|
|
||||||
ranges, ok := validRangesPerCategory[category]
|
|
||||||
if !ok {
|
|
||||||
return codes.Error, false
|
|
||||||
}
|
|
||||||
ok = false
|
|
||||||
for _, crange := range ranges {
|
|
||||||
ok = crange.contains(code)
|
|
||||||
if ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return codes.Error, false
|
|
||||||
}
|
|
||||||
if category > 0 && category < 4 {
|
|
||||||
return codes.Unset, true
|
|
||||||
}
|
|
||||||
return codes.Error, true
|
|
||||||
}
|
|
152
vendor/go.opentelemetry.io/otel/semconv/v1.17.0/httpconv/http.go
generated
vendored
152
vendor/go.opentelemetry.io/otel/semconv/v1.17.0/httpconv/http.go
generated
vendored
@ -1,152 +0,0 @@
|
|||||||
// Copyright The OpenTelemetry Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package httpconv provides OpenTelemetry HTTP semantic conventions for
|
|
||||||
// tracing telemetry.
|
|
||||||
package httpconv // import "go.opentelemetry.io/otel/semconv/v1.17.0/httpconv"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/codes"
|
|
||||||
"go.opentelemetry.io/otel/semconv/internal/v2"
|
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
nc = &internal.NetConv{
|
|
||||||
NetHostNameKey: semconv.NetHostNameKey,
|
|
||||||
NetHostPortKey: semconv.NetHostPortKey,
|
|
||||||
NetPeerNameKey: semconv.NetPeerNameKey,
|
|
||||||
NetPeerPortKey: semconv.NetPeerPortKey,
|
|
||||||
NetSockPeerAddrKey: semconv.NetSockPeerAddrKey,
|
|
||||||
NetSockPeerPortKey: semconv.NetSockPeerPortKey,
|
|
||||||
NetTransportOther: semconv.NetTransportOther,
|
|
||||||
NetTransportTCP: semconv.NetTransportTCP,
|
|
||||||
NetTransportUDP: semconv.NetTransportUDP,
|
|
||||||
NetTransportInProc: semconv.NetTransportInProc,
|
|
||||||
}
|
|
||||||
|
|
||||||
hc = &internal.HTTPConv{
|
|
||||||
NetConv: nc,
|
|
||||||
|
|
||||||
EnduserIDKey: semconv.EnduserIDKey,
|
|
||||||
HTTPClientIPKey: semconv.HTTPClientIPKey,
|
|
||||||
HTTPFlavorKey: semconv.HTTPFlavorKey,
|
|
||||||
HTTPMethodKey: semconv.HTTPMethodKey,
|
|
||||||
HTTPRequestContentLengthKey: semconv.HTTPRequestContentLengthKey,
|
|
||||||
HTTPResponseContentLengthKey: semconv.HTTPResponseContentLengthKey,
|
|
||||||
HTTPRouteKey: semconv.HTTPRouteKey,
|
|
||||||
HTTPSchemeHTTP: semconv.HTTPSchemeHTTP,
|
|
||||||
HTTPSchemeHTTPS: semconv.HTTPSchemeHTTPS,
|
|
||||||
HTTPStatusCodeKey: semconv.HTTPStatusCodeKey,
|
|
||||||
HTTPTargetKey: semconv.HTTPTargetKey,
|
|
||||||
HTTPURLKey: semconv.HTTPURLKey,
|
|
||||||
HTTPUserAgentKey: semconv.HTTPUserAgentKey,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// ClientResponse returns trace attributes for an HTTP response received by a
|
|
||||||
// client from a server. It will return the following attributes if the related
|
|
||||||
// values are defined in resp: "http.status.code",
|
|
||||||
// "http.response_content_length".
|
|
||||||
//
|
|
||||||
// This does not add all OpenTelemetry required attributes for an HTTP event,
|
|
||||||
// it assumes ClientRequest was used to create the span with a complete set of
|
|
||||||
// attributes. If a complete set of attributes can be generated using the
|
|
||||||
// request contained in resp. For example:
|
|
||||||
//
|
|
||||||
// append(ClientResponse(resp), ClientRequest(resp.Request)...)
|
|
||||||
func ClientResponse(resp *http.Response) []attribute.KeyValue {
|
|
||||||
return hc.ClientResponse(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientRequest returns trace attributes for an HTTP request made by a client.
|
|
||||||
// The following attributes are always returned: "http.url", "http.flavor",
|
|
||||||
// "http.method", "net.peer.name". The following attributes are returned if the
|
|
||||||
// related values are defined in req: "net.peer.port", "http.user_agent",
|
|
||||||
// "http.request_content_length", "enduser.id".
|
|
||||||
func ClientRequest(req *http.Request) []attribute.KeyValue {
|
|
||||||
return hc.ClientRequest(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientStatus returns a span status code and message for an HTTP status code
|
|
||||||
// value received by a client.
|
|
||||||
func ClientStatus(code int) (codes.Code, string) {
|
|
||||||
return hc.ClientStatus(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerRequest returns trace attributes for an HTTP request received by a
|
|
||||||
// server.
|
|
||||||
//
|
|
||||||
// The server must be the primary server name if it is known. For example this
|
|
||||||
// would be the ServerName directive
|
|
||||||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
|
||||||
// server, and the server_name directive
|
|
||||||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
|
||||||
// nginx server. More generically, the primary server name would be the host
|
|
||||||
// header value that matches the default virtual host of an HTTP server. It
|
|
||||||
// should include the host identifier and if a port is used to route to the
|
|
||||||
// server that port identifier should be included as an appropriate port
|
|
||||||
// suffix.
|
|
||||||
//
|
|
||||||
// If the primary server name is not known, server should be an empty string.
|
|
||||||
// The req Host will be used to determine the server instead.
|
|
||||||
//
|
|
||||||
// The following attributes are always returned: "http.method", "http.scheme",
|
|
||||||
// "http.flavor", "http.target", "net.host.name". The following attributes are
|
|
||||||
// returned if they related values are defined in req: "net.host.port",
|
|
||||||
// "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id",
|
|
||||||
// "http.client_ip".
|
|
||||||
func ServerRequest(server string, req *http.Request) []attribute.KeyValue {
|
|
||||||
return hc.ServerRequest(server, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerStatus returns a span status code and message for an HTTP status code
|
|
||||||
// value returned by a server. Status codes in the 400-499 range are not
|
|
||||||
// returned as errors.
|
|
||||||
func ServerStatus(code int) (codes.Code, string) {
|
|
||||||
return hc.ServerStatus(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequestHeader returns the contents of h as attributes.
|
|
||||||
//
|
|
||||||
// Instrumentation should require an explicit configuration of which headers to
|
|
||||||
// captured and then prune what they pass here. Including all headers can be a
|
|
||||||
// security risk - explicit configuration helps avoid leaking sensitive
|
|
||||||
// information.
|
|
||||||
//
|
|
||||||
// The User-Agent header is already captured in the http.user_agent attribute
|
|
||||||
// from ClientRequest and ServerRequest. Instrumentation may provide an option
|
|
||||||
// to capture that header here even though it is not recommended. Otherwise,
|
|
||||||
// instrumentation should filter that out of what is passed.
|
|
||||||
func RequestHeader(h http.Header) []attribute.KeyValue {
|
|
||||||
return hc.RequestHeader(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResponseHeader returns the contents of h as attributes.
|
|
||||||
//
|
|
||||||
// Instrumentation should require an explicit configuration of which headers to
|
|
||||||
// captured and then prune what they pass here. Including all headers can be a
|
|
||||||
// security risk - explicit configuration helps avoid leaking sensitive
|
|
||||||
// information.
|
|
||||||
//
|
|
||||||
// The User-Agent header is already captured in the http.user_agent attribute
|
|
||||||
// from ClientRequest and ServerRequest. Instrumentation may provide an option
|
|
||||||
// to capture that header here even though it is not recommended. Otherwise,
|
|
||||||
// instrumentation should filter that out of what is passed.
|
|
||||||
func ResponseHeader(h http.Header) []attribute.KeyValue {
|
|
||||||
return hc.ResponseHeader(h)
|
|
||||||
}
|
|
9
vendor/modules.txt
vendored
9
vendor/modules.txt
vendored
@ -195,6 +195,9 @@ github.com/docker/go-units
|
|||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/emicklei/go-restful/v3
|
github.com/emicklei/go-restful/v3
|
||||||
github.com/emicklei/go-restful/v3/log
|
github.com/emicklei/go-restful/v3/log
|
||||||
|
# github.com/felixge/httpsnoop v1.0.3
|
||||||
|
## explicit; go 1.13
|
||||||
|
github.com/felixge/httpsnoop
|
||||||
# github.com/fsnotify/fsnotify v1.7.0
|
# github.com/fsnotify/fsnotify v1.7.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/fsnotify/fsnotify
|
github.com/fsnotify/fsnotify
|
||||||
@ -407,6 +410,10 @@ go.opencensus.io/trace/tracestate
|
|||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal
|
||||||
|
# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0
|
||||||
|
## explicit; go 1.19
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil
|
||||||
# go.opentelemetry.io/otel v1.19.0
|
# go.opentelemetry.io/otel v1.19.0
|
||||||
## explicit; go 1.20
|
## explicit; go 1.20
|
||||||
go.opentelemetry.io/otel
|
go.opentelemetry.io/otel
|
||||||
@ -418,9 +425,7 @@ go.opentelemetry.io/otel/internal/attribute
|
|||||||
go.opentelemetry.io/otel/internal/baggage
|
go.opentelemetry.io/otel/internal/baggage
|
||||||
go.opentelemetry.io/otel/internal/global
|
go.opentelemetry.io/otel/internal/global
|
||||||
go.opentelemetry.io/otel/propagation
|
go.opentelemetry.io/otel/propagation
|
||||||
go.opentelemetry.io/otel/semconv/internal/v2
|
|
||||||
go.opentelemetry.io/otel/semconv/v1.17.0
|
go.opentelemetry.io/otel/semconv/v1.17.0
|
||||||
go.opentelemetry.io/otel/semconv/v1.17.0/httpconv
|
|
||||||
go.opentelemetry.io/otel/semconv/v1.21.0
|
go.opentelemetry.io/otel/semconv/v1.21.0
|
||||||
# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0
|
# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0
|
||||||
## explicit; go 1.20
|
## explicit; go 1.20
|
||||||
|
Loading…
Reference in New Issue
Block a user