tracing: update to otelhttp roundtripper
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>v0.9
parent
2a4577efab
commit
5a318dd017
8
go.mod
8
go.mod
|
@ -55,6 +55,8 @@ require (
|
||||||
github.com/urfave/cli v1.22.2
|
github.com/urfave/cli v1.22.2
|
||||||
go.etcd.io/bbolt v1.3.5
|
go.etcd.io/bbolt v1.3.5
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.21.0
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.21.0
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.21.0
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.21.0
|
||||||
go.opentelemetry.io/otel v1.0.0-RC1
|
go.opentelemetry.io/otel v1.0.0-RC1
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1
|
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.0-RC1
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.0-RC1
|
||||||
|
@ -73,3 +75,9 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/docker/docker => github.com/docker/docker v20.10.3-0.20210609100121-ef4d47340142+incompatible
|
replace github.com/docker/docker => github.com/docker/docker v20.10.3-0.20210609100121-ef4d47340142+incompatible
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/otelhttp v0.0.0-20210714055410-d010b05b4939
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20210714055410-d010b05b4939
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -419,6 +419,8 @@ github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
|
||||||
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/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
|
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
|
||||||
|
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
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/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
|
@ -1071,6 +1073,12 @@ github.com/tonistiigi/fsutil v0.0.0-20210609172227-d72af97c0eaf h1:L0ixhsTk9j+dV
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20210609172227-d72af97c0eaf/go.mod h1:lJAxK//iyZ3yGbQswdrPTxugZIDM7sd4bEsD0x3XMHk=
|
github.com/tonistiigi/fsutil v0.0.0-20210609172227-d72af97c0eaf/go.mod h1:lJAxK//iyZ3yGbQswdrPTxugZIDM7sd4bEsD0x3XMHk=
|
||||||
github.com/tonistiigi/go-actions-cache v0.0.0-20210714033416-b93d7f1b2e70 h1:+ZlFs3Tl5qYZJvX2PxfZxGlVXz847LsOJGyNVU5pCHo=
|
github.com/tonistiigi/go-actions-cache v0.0.0-20210714033416-b93d7f1b2e70 h1:+ZlFs3Tl5qYZJvX2PxfZxGlVXz847LsOJGyNVU5pCHo=
|
||||||
github.com/tonistiigi/go-actions-cache v0.0.0-20210714033416-b93d7f1b2e70/go.mod h1:dNS+PPTqGnSl80x3wEyWWCHeON5xiBGtcM0uD6CgHNU=
|
github.com/tonistiigi/go-actions-cache v0.0.0-20210714033416-b93d7f1b2e70/go.mod h1:dNS+PPTqGnSl80x3wEyWWCHeON5xiBGtcM0uD6CgHNU=
|
||||||
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939 h1:s6wDMZYNyWt8KvkjhrMpOthFPgI3JB8ipJS+eCV/psg=
|
||||||
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939/go.mod h1:Vm5u/mtkj1OMhtao0v+BGo2LUoLCgHYXvRmj0jWITlE=
|
||||||
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20210714055410-d010b05b4939 h1:ZZ1KHKvs97BcRoblbm6RhrDzs/OejFv7miYSIcZI7Ds=
|
||||||
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20210714055410-d010b05b4939/go.mod h1:a9cocRplhIBkUAJmak+BPDx+LVL7cTmqUPB0uBcTA4k=
|
||||||
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/otelhttp v0.0.0-20210714055410-d010b05b4939 h1:iYUjYA5PwiJjvlY1PkCjFZIoDFOlL4g/AqSrcd5lJtE=
|
||||||
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/otelhttp v0.0.0-20210714055410-d010b05b4939/go.mod h1:JQAtechjxLEL81EjmbRwxBq/XEzGaHcsPuDHAx54hg4=
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||||
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f h1:DLpt6B5oaaS8jyXHa9VA4rrZloBVPVXeCtrOsrFauxc=
|
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f h1:DLpt6B5oaaS8jyXHa9VA4rrZloBVPVXeCtrOsrFauxc=
|
||||||
|
@ -1139,8 +1147,6 @@ go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/contrib v0.21.0 h1:RMJ6GlUVzLYp/zmItxTTdAmr1gnpO/HHMFmvjAhvJQM=
|
go.opentelemetry.io/contrib v0.21.0 h1:RMJ6GlUVzLYp/zmItxTTdAmr1gnpO/HHMFmvjAhvJQM=
|
||||||
go.opentelemetry.io/contrib v0.21.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM=
|
go.opentelemetry.io/contrib v0.21.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.21.0 h1:68WZYF6CrnsXIVDYc51cR9VmTX2IM7y0svo7s4lu5kQ=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.21.0/go.mod h1:Vm5u/mtkj1OMhtao0v+BGo2LUoLCgHYXvRmj0jWITlE=
|
|
||||||
go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc=
|
go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc=
|
||||||
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
|
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1 h1:tVhw2BMSAk248rhdeirOe9hlXKwGHDvVtF7P8F+H2DU=
|
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1 h1:tVhw2BMSAk248rhdeirOe9hlXKwGHDvVtF7P8F+H2DU=
|
||||||
|
@ -1151,6 +1157,10 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0-RC1 h1:ZO
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0-RC1/go.mod h1:cDwRc2Jrh5Gku1peGK8p9rRuX/Uq2OtVmLicjlw2WYU=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0-RC1/go.mod h1:cDwRc2Jrh5Gku1peGK8p9rRuX/Uq2OtVmLicjlw2WYU=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.0.0-RC1 h1:zoRUmPIQOAhkiXjoZ/BJUd6A9Ug1M/sEJgrEI68m3dU=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.0.0-RC1 h1:zoRUmPIQOAhkiXjoZ/BJUd6A9Ug1M/sEJgrEI68m3dU=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.0.0-RC1/go.mod h1:OYKzEoxgXFvehW7X12WYT4/a2BlASJK9l7RtG4A91fg=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.0.0-RC1/go.mod h1:OYKzEoxgXFvehW7X12WYT4/a2BlASJK9l7RtG4A91fg=
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.21.0 h1:gZlIBo5O51hZOOZz8vEcuRx/l5dnADadKfpT70AELoo=
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.21.0/go.mod h1:iOfAaY2YycsXfYD4kaRSbLx2LKmfpKObWBEv9QK5zFo=
|
||||||
|
go.opentelemetry.io/otel/metric v0.21.0 h1:ZtcJlHqVE4l8Su0WOLOd9fEPheJuYEiQ0wr9wv2p25I=
|
||||||
|
go.opentelemetry.io/otel/metric v0.21.0/go.mod h1:JWCt1bjivC4iCrz/aCrM1GSw+ZcvY44KCbaeeRhzHnc=
|
||||||
go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk=
|
go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk=
|
||||||
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4=
|
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4=
|
||||||
go.opentelemetry.io/otel/sdk v1.0.0-RC1 h1:Sy2VLOOg24bipyC29PhuMXYNJrLsxkie8hyI7kUlG9Q=
|
go.opentelemetry.io/otel/sdk v1.0.0-RC1 h1:Sy2VLOOg24bipyC29PhuMXYNJrLsxkie8hyI7kUlG9Q=
|
||||||
|
|
|
@ -3,10 +3,12 @@ package tracing
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
|
||||||
"github.com/moby/buildkit/util/bklog"
|
"github.com/moby/buildkit/util/bklog"
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
"go.opentelemetry.io/otel/propagation"
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
@ -55,81 +57,17 @@ func ContextWithSpanFromContext(ctx, ctx2 context.Context) context.Context {
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultTransport http.RoundTripper = &Transport{
|
var DefaultTransport http.RoundTripper = NewTransport(http.DefaultTransport)
|
||||||
RoundTripper: NewTransport(http.DefaultTransport),
|
|
||||||
}
|
|
||||||
|
|
||||||
var DefaultClient = &http.Client{
|
var DefaultClient = &http.Client{
|
||||||
Transport: DefaultTransport,
|
Transport: DefaultTransport,
|
||||||
}
|
}
|
||||||
|
|
||||||
var propagators = propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})
|
|
||||||
|
|
||||||
type Transport struct {
|
|
||||||
http.RoundTripper
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTransport(rt http.RoundTripper) http.RoundTripper {
|
func NewTransport(rt http.RoundTripper) http.RoundTripper {
|
||||||
// TODO: switch to otelhttp. needs upstream updates to avoid transport-global tracer
|
return otelhttp.NewTransport(rt,
|
||||||
return &Transport{
|
otelhttp.WithPropagators(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})),
|
||||||
RoundTripper: rt,
|
otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace {
|
||||||
}
|
return otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans())
|
||||||
}
|
}),
|
||||||
|
)
|
||||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
span := trace.SpanFromContext(req.Context())
|
|
||||||
if !span.SpanContext().IsValid() { // no tracer connected with either request or transport
|
|
||||||
return t.RoundTripper.RoundTrip(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, span := span.TracerProvider().Tracer("").Start(req.Context(), req.Method)
|
|
||||||
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
span.SetAttributes(semconv.HTTPClientAttributesFromHTTPRequest(req)...)
|
|
||||||
propagators.Inject(ctx, propagation.HeaderCarrier(req.Header))
|
|
||||||
|
|
||||||
resp, err := t.RoundTripper.RoundTrip(req)
|
|
||||||
if err != nil {
|
|
||||||
span.RecordError(err)
|
|
||||||
span.End()
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
span.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(resp.StatusCode)...)
|
|
||||||
span.SetStatus(semconv.SpanStatusFromHTTPStatusCode(resp.StatusCode))
|
|
||||||
|
|
||||||
if req.Method == "HEAD" {
|
|
||||||
span.End()
|
|
||||||
} else {
|
|
||||||
resp.Body = &wrappedBody{ctx: ctx, span: span, body: resp.Body}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type wrappedBody struct {
|
|
||||||
ctx context.Context
|
|
||||||
span trace.Span
|
|
||||||
body io.ReadCloser
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ io.ReadCloser = &wrappedBody{}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wb *wrappedBody) Close() error {
|
|
||||||
wb.span.End()
|
|
||||||
return wb.body.Close()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.6
|
||||||
|
- 1.7
|
||||||
|
- 1.8
|
|
@ -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.
|
|
@ -0,0 +1,10 @@
|
||||||
|
.PHONY: ci generate clean
|
||||||
|
|
||||||
|
ci: clean generate
|
||||||
|
go test -v ./...
|
||||||
|
|
||||||
|
generate:
|
||||||
|
go generate .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *_generated*.go
|
|
@ -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.
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/felixge/httpsnoop?status.svg)](https://godoc.org/github.com/felixge/httpsnoop)
|
||||||
|
[![Build Status](https://travis-ci.org/felixge/httpsnoop.svg?branch=master)](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
|
|
@ -0,0 +1,79 @@
|
||||||
|
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 {
|
||||||
|
var (
|
||||||
|
start = time.Now()
|
||||||
|
m = Metrics{Code: http.StatusOK}
|
||||||
|
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)
|
||||||
|
return m
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/felixge/httpsnoop
|
||||||
|
|
||||||
|
go 1.13
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -122,8 +122,7 @@ type streamEvent struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
closeEvent streamEventType = iota
|
receiveEndEvent streamEventType = iota
|
||||||
receiveEndEvent
|
|
||||||
errorEvent
|
errorEvent
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -188,19 +187,12 @@ func (w *clientStream) CloseSend() error {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.sendStreamEvent(errorEvent, err)
|
w.sendStreamEvent(errorEvent, err)
|
||||||
} else {
|
|
||||||
w.sendStreamEvent(closeEvent, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
func wrapClientStream(ctx context.Context, s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream {
|
||||||
clientClosedState byte = 1 << iota
|
|
||||||
receiveEndedState
|
|
||||||
)
|
|
||||||
|
|
||||||
func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream {
|
|
||||||
events := make(chan streamEvent)
|
events := make(chan streamEvent)
|
||||||
eventsDone := make(chan struct{})
|
eventsDone := make(chan struct{})
|
||||||
finished := make(chan error)
|
finished := make(chan error)
|
||||||
|
@ -208,23 +200,20 @@ func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream
|
||||||
go func() {
|
go func() {
|
||||||
defer close(eventsDone)
|
defer close(eventsDone)
|
||||||
|
|
||||||
// Both streams have to be closed
|
for {
|
||||||
state := byte(0)
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
for event := range events {
|
finished <- ctx.Err()
|
||||||
switch event.Type {
|
|
||||||
case closeEvent:
|
|
||||||
state |= clientClosedState
|
|
||||||
case receiveEndEvent:
|
|
||||||
state |= receiveEndedState
|
|
||||||
case errorEvent:
|
|
||||||
finished <- event.Err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if state == clientClosedState|receiveEndedState {
|
|
||||||
finished <- nil
|
|
||||||
return
|
return
|
||||||
|
case event := <-events:
|
||||||
|
switch event.Type {
|
||||||
|
case receiveEndEvent:
|
||||||
|
finished <- nil
|
||||||
|
return
|
||||||
|
case errorEvent:
|
||||||
|
finished <- event.Err
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -284,7 +273,7 @@ func StreamClientInterceptor(opts ...Option) grpc.StreamClientInterceptor {
|
||||||
span.End()
|
span.End()
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
stream := wrapClientStream(s, desc)
|
stream := wrapClientStream(ctx, s, desc)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := <-stream.finished
|
err := <-stream.finished
|
||||||
|
|
201
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/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.
|
28
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/api.go
generated
vendored
Normal file
28
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/api.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 otelhttptrace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// W3C client.
|
||||||
|
func W3C(ctx context.Context, req *http.Request) (context.Context, *http.Request) {
|
||||||
|
ctx = httptrace.WithClientTrace(ctx, NewClientTrace(ctx))
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
return ctx, req
|
||||||
|
}
|
370
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go
generated
vendored
Normal file
370
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go
generated
vendored
Normal file
|
@ -0,0 +1,370 @@
|
||||||
|
// 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 otelhttptrace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http/httptrace"
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/contrib"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTP attributes.
|
||||||
|
var (
|
||||||
|
HTTPStatus = attribute.Key("http.status")
|
||||||
|
HTTPHeaderMIME = attribute.Key("http.mime")
|
||||||
|
HTTPRemoteAddr = attribute.Key("http.remote")
|
||||||
|
HTTPLocalAddr = attribute.Key("http.local")
|
||||||
|
HTTPConnectionReused = attribute.Key("http.conn.reused")
|
||||||
|
HTTPConnectionWasIdle = attribute.Key("http.conn.wasidle")
|
||||||
|
HTTPConnectionIdleTime = attribute.Key("http.conn.idletime")
|
||||||
|
HTTPConnectionStartNetwork = attribute.Key("http.conn.start.network")
|
||||||
|
HTTPConnectionDoneNetwork = attribute.Key("http.conn.done.network")
|
||||||
|
HTTPConnectionDoneAddr = attribute.Key("http.conn.done.addr")
|
||||||
|
HTTPDNSAddrs = attribute.Key("http.dns.addrs")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hookMap = map[string]string{
|
||||||
|
"http.dns": "http.getconn",
|
||||||
|
"http.connect": "http.getconn",
|
||||||
|
"http.tls": "http.getconn",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func parentHook(hook string) string {
|
||||||
|
if strings.HasPrefix(hook, "http.connect") {
|
||||||
|
return hookMap["http.connect"]
|
||||||
|
}
|
||||||
|
return hookMap[hook]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientTraceOption allows customizations to how the httptrace.Client
|
||||||
|
// collects information.
|
||||||
|
type ClientTraceOption func(*clientTracer)
|
||||||
|
|
||||||
|
// WithoutSubSpans will modify the httptrace.Client to only collect data
|
||||||
|
// as Events and Attributes on a span found in the context. By default
|
||||||
|
// sub-spans will be generated.
|
||||||
|
func WithoutSubSpans() ClientTraceOption {
|
||||||
|
return func(ct *clientTracer) {
|
||||||
|
ct.useSpans = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRedactedHeaders will be replaced by fixed '****' values for the header
|
||||||
|
// names provided. These are in addition to the sensitive headers already
|
||||||
|
// redacted by default: Authorization, WWW-Authenticate, Proxy-Authenticate
|
||||||
|
// Proxy-Authorization, Cookie, Set-Cookie
|
||||||
|
func WithRedactedHeaders(headers ...string) ClientTraceOption {
|
||||||
|
return func(ct *clientTracer) {
|
||||||
|
for _, header := range headers {
|
||||||
|
ct.redactedHeaders[strings.ToLower(header)] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutHeaders will disable adding span annotations for the http headers
|
||||||
|
// and values.
|
||||||
|
func WithoutHeaders() ClientTraceOption {
|
||||||
|
return func(ct *clientTracer) {
|
||||||
|
ct.addHeaders = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientTracer struct {
|
||||||
|
context.Context
|
||||||
|
|
||||||
|
tr trace.Tracer
|
||||||
|
|
||||||
|
activeHooks map[string]context.Context
|
||||||
|
root trace.Span
|
||||||
|
mtx sync.Mutex
|
||||||
|
redactedHeaders map[string]struct{}
|
||||||
|
addHeaders bool
|
||||||
|
useSpans bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientTrace(ctx context.Context, opts ...ClientTraceOption) *httptrace.ClientTrace {
|
||||||
|
ct := &clientTracer{
|
||||||
|
Context: ctx,
|
||||||
|
activeHooks: make(map[string]context.Context),
|
||||||
|
redactedHeaders: map[string]struct{}{
|
||||||
|
"authorization": {},
|
||||||
|
"www-authenticate": {},
|
||||||
|
"proxy-authenticate": {},
|
||||||
|
"proxy-authorization": {},
|
||||||
|
"cookie": {},
|
||||||
|
"set-cookie": {},
|
||||||
|
},
|
||||||
|
addHeaders: true,
|
||||||
|
useSpans: true,
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(ct)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tp trace.TracerProvider
|
||||||
|
if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() {
|
||||||
|
tp = span.TracerProvider()
|
||||||
|
} else {
|
||||||
|
tp = otel.GetTracerProvider()
|
||||||
|
}
|
||||||
|
|
||||||
|
ct.tr = tp.Tracer(
|
||||||
|
"go.opentelemetry.io/otel/instrumentation/httptrace",
|
||||||
|
trace.WithInstrumentationVersion(contrib.SemVersion()),
|
||||||
|
)
|
||||||
|
|
||||||
|
return &httptrace.ClientTrace{
|
||||||
|
GetConn: ct.getConn,
|
||||||
|
GotConn: ct.gotConn,
|
||||||
|
PutIdleConn: ct.putIdleConn,
|
||||||
|
GotFirstResponseByte: ct.gotFirstResponseByte,
|
||||||
|
Got100Continue: ct.got100Continue,
|
||||||
|
Got1xxResponse: ct.got1xxResponse,
|
||||||
|
DNSStart: ct.dnsStart,
|
||||||
|
DNSDone: ct.dnsDone,
|
||||||
|
ConnectStart: ct.connectStart,
|
||||||
|
ConnectDone: ct.connectDone,
|
||||||
|
TLSHandshakeStart: ct.tlsHandshakeStart,
|
||||||
|
TLSHandshakeDone: ct.tlsHandshakeDone,
|
||||||
|
WroteHeaderField: ct.wroteHeaderField,
|
||||||
|
WroteHeaders: ct.wroteHeaders,
|
||||||
|
Wait100Continue: ct.wait100Continue,
|
||||||
|
WroteRequest: ct.wroteRequest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) start(hook, spanName string, attrs ...attribute.KeyValue) {
|
||||||
|
if !ct.useSpans {
|
||||||
|
if ct.root == nil {
|
||||||
|
ct.root = trace.SpanFromContext(ct.Context)
|
||||||
|
}
|
||||||
|
ct.root.AddEvent(hook+".start", trace.WithAttributes(attrs...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ct.mtx.Lock()
|
||||||
|
defer ct.mtx.Unlock()
|
||||||
|
|
||||||
|
if hookCtx, found := ct.activeHooks[hook]; !found {
|
||||||
|
var sp trace.Span
|
||||||
|
ct.activeHooks[hook], sp = ct.tr.Start(ct.getParentContext(hook), spanName, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient))
|
||||||
|
if ct.root == nil {
|
||||||
|
ct.root = sp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// end was called before start finished, add the start attributes and end the span here
|
||||||
|
span := trace.SpanFromContext(hookCtx)
|
||||||
|
span.SetAttributes(attrs...)
|
||||||
|
span.End()
|
||||||
|
|
||||||
|
delete(ct.activeHooks, hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) end(hook string, err error, attrs ...attribute.KeyValue) {
|
||||||
|
if !ct.useSpans {
|
||||||
|
if err != nil {
|
||||||
|
attrs = append(attrs, attribute.String(hook+".error", err.Error()))
|
||||||
|
}
|
||||||
|
ct.root.AddEvent(hook+".done", trace.WithAttributes(attrs...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ct.mtx.Lock()
|
||||||
|
defer ct.mtx.Unlock()
|
||||||
|
if ctx, ok := ct.activeHooks[hook]; ok {
|
||||||
|
span := trace.SpanFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
}
|
||||||
|
span.SetAttributes(attrs...)
|
||||||
|
span.End()
|
||||||
|
delete(ct.activeHooks, hook)
|
||||||
|
} else {
|
||||||
|
// start is not finished before end is called.
|
||||||
|
// Start a span here with the ending attributes that will be finished when start finishes.
|
||||||
|
// Yes, it's backwards. v0v
|
||||||
|
ctx, span := ct.tr.Start(ct.getParentContext(hook), hook, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient))
|
||||||
|
if err != nil {
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
}
|
||||||
|
ct.activeHooks[hook] = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) getParentContext(hook string) context.Context {
|
||||||
|
ctx, ok := ct.activeHooks[parentHook(hook)]
|
||||||
|
if !ok {
|
||||||
|
return ct.Context
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) span(hook string) trace.Span {
|
||||||
|
ct.mtx.Lock()
|
||||||
|
defer ct.mtx.Unlock()
|
||||||
|
if ctx, ok := ct.activeHooks[hook]; ok {
|
||||||
|
return trace.SpanFromContext(ctx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) getConn(host string) {
|
||||||
|
ct.start("http.getconn", "http.getconn", semconv.HTTPHostKey.String(host))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) {
|
||||||
|
attrs := []attribute.KeyValue{
|
||||||
|
HTTPRemoteAddr.String(info.Conn.RemoteAddr().String()),
|
||||||
|
HTTPLocalAddr.String(info.Conn.LocalAddr().String()),
|
||||||
|
HTTPConnectionReused.Bool(info.Reused),
|
||||||
|
HTTPConnectionWasIdle.Bool(info.WasIdle),
|
||||||
|
}
|
||||||
|
if info.WasIdle {
|
||||||
|
attrs = append(attrs, HTTPConnectionIdleTime.String(info.IdleTime.String()))
|
||||||
|
}
|
||||||
|
ct.end("http.getconn", nil, attrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) putIdleConn(err error) {
|
||||||
|
ct.end("http.receive", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) gotFirstResponseByte() {
|
||||||
|
ct.start("http.receive", "http.receive")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) {
|
||||||
|
ct.start("http.dns", "http.dns", semconv.HTTPHostKey.String(info.Host))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) {
|
||||||
|
var addrs []string
|
||||||
|
for _, netAddr := range info.Addrs {
|
||||||
|
addrs = append(addrs, netAddr.String())
|
||||||
|
}
|
||||||
|
ct.end("http.dns", info.Err, HTTPDNSAddrs.String(sliceToString(addrs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) connectStart(network, addr string) {
|
||||||
|
ct.start("http.connect."+addr, "http.connect",
|
||||||
|
HTTPRemoteAddr.String(addr),
|
||||||
|
HTTPConnectionStartNetwork.String(network),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) connectDone(network, addr string, err error) {
|
||||||
|
ct.end("http.connect."+addr, err,
|
||||||
|
HTTPConnectionDoneAddr.String(addr),
|
||||||
|
HTTPConnectionDoneNetwork.String(network),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) tlsHandshakeStart() {
|
||||||
|
ct.start("http.tls", "http.tls")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) tlsHandshakeDone(_ tls.ConnectionState, err error) {
|
||||||
|
ct.end("http.tls", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) wroteHeaderField(k string, v []string) {
|
||||||
|
if ct.useSpans && ct.span("http.headers") == nil {
|
||||||
|
ct.start("http.headers", "http.headers")
|
||||||
|
}
|
||||||
|
if !ct.addHeaders {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
k = strings.ToLower(k)
|
||||||
|
value := sliceToString(v)
|
||||||
|
if _, ok := ct.redactedHeaders[k]; ok {
|
||||||
|
value = "****"
|
||||||
|
}
|
||||||
|
ct.root.SetAttributes(attribute.String("http."+k, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) wroteHeaders() {
|
||||||
|
if ct.useSpans && ct.span("http.headers") != nil {
|
||||||
|
ct.end("http.headers", nil)
|
||||||
|
}
|
||||||
|
ct.start("http.send", "http.send")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) {
|
||||||
|
if info.Err != nil {
|
||||||
|
ct.root.SetStatus(codes.Error, info.Err.Error())
|
||||||
|
}
|
||||||
|
ct.end("http.send", info.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) got100Continue() {
|
||||||
|
span := ct.root
|
||||||
|
if ct.useSpans {
|
||||||
|
span = ct.span("http.receive")
|
||||||
|
}
|
||||||
|
span.AddEvent("GOT 100 - Continue")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) wait100Continue() {
|
||||||
|
span := ct.root
|
||||||
|
if ct.useSpans {
|
||||||
|
span = ct.span("http.receive")
|
||||||
|
}
|
||||||
|
span.AddEvent("GOT 100 - Wait")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *clientTracer) got1xxResponse(code int, header textproto.MIMEHeader) error {
|
||||||
|
span := ct.root
|
||||||
|
if ct.useSpans {
|
||||||
|
span = ct.span("http.receive")
|
||||||
|
}
|
||||||
|
span.AddEvent("GOT 1xx", trace.WithAttributes(
|
||||||
|
HTTPStatus.Int(code),
|
||||||
|
HTTPHeaderMIME.String(sm2s(header)),
|
||||||
|
))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceToString(value []string) string {
|
||||||
|
if len(value) == 0 {
|
||||||
|
return "undefined"
|
||||||
|
}
|
||||||
|
return strings.Join(value, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sm2s(value map[string][]string) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for k, v := range value {
|
||||||
|
if buf.Len() != 0 {
|
||||||
|
buf.WriteString(",")
|
||||||
|
}
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteString("=")
|
||||||
|
buf.WriteString(sliceToString(v))
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
14
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/go.mod
generated
vendored
Normal file
14
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/contrib => ../../../../..
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/go-cmp v0.5.6
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
|
go.opentelemetry.io/contrib v0.21.0
|
||||||
|
go.opentelemetry.io/otel v1.0.0-RC1
|
||||||
|
go.opentelemetry.io/otel/oteltest v1.0.0-RC1
|
||||||
|
go.opentelemetry.io/otel/trace v1.0.0-RC1
|
||||||
|
)
|
21
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/go.sum
generated
vendored
Normal file
21
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc=
|
||||||
|
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
|
||||||
|
go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk=
|
||||||
|
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58=
|
||||||
|
go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
68
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/httptrace.go
generated
vendored
Normal file
68
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/httptrace.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// 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 otelhttptrace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/baggage"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Option is a function that allows configuration of the httptrace Extract()
|
||||||
|
// and Inject() functions
|
||||||
|
type Option func(*config)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
propagators propagation.TextMapPropagator
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConfig(opts []Option) *config {
|
||||||
|
c := &config{propagators: otel.GetTextMapPropagator()}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(c)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPropagators sets the propagators to use for Extraction and Injection
|
||||||
|
func WithPropagators(props propagation.TextMapPropagator) Option {
|
||||||
|
return func(c *config) {
|
||||||
|
c.propagators = props
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract returns the Attributes, Context Entries, and SpanContext that were encoded by Inject.
|
||||||
|
func Extract(ctx context.Context, req *http.Request, opts ...Option) ([]attribute.KeyValue, baggage.Baggage, trace.SpanContext) {
|
||||||
|
c := newConfig(opts)
|
||||||
|
ctx = c.propagators.Extract(ctx, propagation.HeaderCarrier(req.Header))
|
||||||
|
|
||||||
|
attrs := append(
|
||||||
|
semconv.HTTPServerAttributesFromHTTPRequest("", "", req),
|
||||||
|
semconv.NetAttributesFromHTTPRequest("tcp", req)...,
|
||||||
|
)
|
||||||
|
|
||||||
|
return attrs, baggage.FromContext(ctx), trace.SpanContextFromContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Inject(ctx context.Context, req *http.Request, opts ...Option) {
|
||||||
|
c := newConfig(opts)
|
||||||
|
c.propagators.Inject(ctx, propagation.HeaderCarrier(req.Header))
|
||||||
|
}
|
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 (
|
||||||
|
"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, url string) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, 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, url string) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "HEAD", url, 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, url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", url, 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, url string, data url.Values) (resp *http.Response, err error) {
|
||||||
|
return Post(ctx, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||||
|
}
|
47
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go
generated
vendored
Normal file
47
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// 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 (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/contrib"
|
||||||
|
"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(contrib.SemVersion()))
|
||||||
|
}
|
181
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go
generated
vendored
Normal file
181
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/contrib"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
"go.opentelemetry.io/otel/metric/global"
|
||||||
|
"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 {
|
||||||
|
Tracer trace.Tracer
|
||||||
|
Meter metric.Meter
|
||||||
|
Propagators propagation.TextMapPropagator
|
||||||
|
SpanStartOptions []trace.SpanStartOption
|
||||||
|
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: global.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(contrib.SemVersion()),
|
||||||
|
)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
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.SpanStartOptions = append(c.SpanStartOptions, trace.WithNewRoot())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPropagators configures specific propagators. If this
|
||||||
|
// option isn't specified then
|
||||||
|
func WithPropagators(ps propagation.TextMapPropagator) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
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"
|
15
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/go.mod
generated
vendored
Normal file
15
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
module go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/contrib => ../../../..
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/felixge/httpsnoop v1.0.2
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
|
go.opentelemetry.io/contrib v0.21.0
|
||||||
|
go.opentelemetry.io/otel v1.0.0-RC1
|
||||||
|
go.opentelemetry.io/otel/metric v0.21.0
|
||||||
|
go.opentelemetry.io/otel/oteltest v1.0.0-RC1
|
||||||
|
go.opentelemetry.io/otel/trace v1.0.0-RC1
|
||||||
|
)
|
27
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/go.sum
generated
vendored
Normal file
27
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
|
||||||
|
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc=
|
||||||
|
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.21.0 h1:gZlIBo5O51hZOOZz8vEcuRx/l5dnADadKfpT70AELoo=
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.21.0/go.mod h1:iOfAaY2YycsXfYD4kaRSbLx2LKmfpKObWBEv9QK5zFo=
|
||||||
|
go.opentelemetry.io/otel/metric v0.21.0 h1:ZtcJlHqVE4l8Su0WOLOd9fEPheJuYEiQ0wr9wv2p25I=
|
||||||
|
go.opentelemetry.io/otel/metric v0.21.0/go.mod h1:JWCt1bjivC4iCrz/aCrM1GSw+ZcvY44KCbaeeRhzHnc=
|
||||||
|
go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk=
|
||||||
|
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58=
|
||||||
|
go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
235
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go
generated
vendored
Normal file
235
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go
generated
vendored
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
// 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 (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/felixge/httpsnoop"
|
||||||
|
|
||||||
|
"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.4.0"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ http.Handler = &Handler{}
|
||||||
|
|
||||||
|
// Handler is http middleware that corresponds to the http.Handler interface and
|
||||||
|
// is designed to wrap a http.Mux (or equivalent), while individual routes on
|
||||||
|
// the mux are wrapped with WithRouteTag. A Handler will add various attributes
|
||||||
|
// to the span using the attribute.Keys defined in this package.
|
||||||
|
type Handler struct {
|
||||||
|
operation string
|
||||||
|
handler http.Handler
|
||||||
|
|
||||||
|
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.Int64ValueRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultHandlerFormatter(operation string, _ *http.Request) string {
|
||||||
|
return operation
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler wraps the passed handler, functioning like middleware, in a span
|
||||||
|
// named after the operation and with any provided Options.
|
||||||
|
func NewHandler(handler http.Handler, operation string, opts ...Option) http.Handler {
|
||||||
|
h := Handler{
|
||||||
|
handler: handler,
|
||||||
|
operation: operation,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultOpts := []Option{
|
||||||
|
WithSpanOptions(trace.WithSpanKind(trace.SpanKindServer)),
|
||||||
|
WithSpanNameFormatter(defaultHandlerFormatter),
|
||||||
|
}
|
||||||
|
|
||||||
|
c := newConfig(append(defaultOpts, opts...)...)
|
||||||
|
h.configure(c)
|
||||||
|
h.createMeasures()
|
||||||
|
|
||||||
|
return &h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleErr(err error) {
|
||||||
|
if err != nil {
|
||||||
|
otel.Handle(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) createMeasures() {
|
||||||
|
h.counters = make(map[string]metric.Int64Counter)
|
||||||
|
h.valueRecorders = make(map[string]metric.Int64ValueRecorder)
|
||||||
|
|
||||||
|
requestBytesCounter, err := h.meter.NewInt64Counter(RequestContentLength)
|
||||||
|
handleErr(err)
|
||||||
|
|
||||||
|
responseBytesCounter, err := h.meter.NewInt64Counter(ResponseContentLength)
|
||||||
|
handleErr(err)
|
||||||
|
|
||||||
|
serverLatencyMeasure, err := h.meter.NewInt64ValueRecorder(ServerLatency)
|
||||||
|
handleErr(err)
|
||||||
|
|
||||||
|
h.counters[RequestContentLength] = requestBytesCounter
|
||||||
|
h.counters[ResponseContentLength] = responseBytesCounter
|
||||||
|
h.valueRecorders[ServerLatency] = serverLatencyMeasure
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP serves HTTP requests (http.Handler)
|
||||||
|
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
requestStartTime := time.Now()
|
||||||
|
for _, f := range h.filters {
|
||||||
|
if !f(r) {
|
||||||
|
// Simply pass through to the handler if a filter rejects the request
|
||||||
|
h.handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := append([]trace.SpanStartOption{
|
||||||
|
trace.WithAttributes(semconv.NetAttributesFromHTTPRequest("tcp", r)...),
|
||||||
|
trace.WithAttributes(semconv.EndUserAttributesFromHTTPRequest(r)...),
|
||||||
|
trace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(h.operation, "", r)...),
|
||||||
|
}, h.spanStartOptions...) // start with the configured options
|
||||||
|
|
||||||
|
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 := h.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||||
|
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 we don't want to mutate the body as it will affect
|
||||||
|
// the identity of it in a unforeseeable way because we assert ReadCloser
|
||||||
|
// fullfills a certain interface and it is indeed nil.
|
||||||
|
if r.Body != nil {
|
||||||
|
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}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
h.handler.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
|
||||||
|
setAfterServeAttributes(span, bw.read, rww.written, rww.statusCode, bw.err, rww.err)
|
||||||
|
|
||||||
|
// Add metrics
|
||||||
|
attributes := append(labeler.Get(), semconv.HTTPServerMetricAttributesFromHTTPRequest(h.operation, r)...)
|
||||||
|
h.counters[RequestContentLength].Add(ctx, bw.read, attributes...)
|
||||||
|
h.counters[ResponseContentLength].Add(ctx, rww.written, attributes...)
|
||||||
|
|
||||||
|
elapsedTime := time.Since(requestStartTime).Microseconds()
|
||||||
|
|
||||||
|
h.valueRecorders[ServerLatency].Record(ctx, elapsedTime, attributes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.HTTPAttributesFromHTTPStatusCode(statusCode)...)
|
||||||
|
span.SetStatus(semconv.SpanStatusFromHTTPStatusCode(statusCode))
|
||||||
|
}
|
||||||
|
if werr != nil && werr != io.EOF {
|
||||||
|
attributes = append(attributes, WriteErrorKey.String(werr.Error()))
|
||||||
|
}
|
||||||
|
span.SetAttributes(attributes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRouteTag annotates a span with the provided route name using the
|
||||||
|
// RouteKey Tag.
|
||||||
|
func WithRouteTag(route string, h http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
span := trace.SpanFromContext(r.Context())
|
||||||
|
span.SetAttributes(semconv.HTTPRouteKey.String(route))
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
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 (
|
||||||
|
"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
|
||||||
|
}
|
154
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go
generated
vendored
Normal file
154
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go
generated
vendored
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||||
|
"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.WithContext(ctx)
|
||||||
|
span.SetAttributes(semconv.HTTPClientAttributesFromHTTPRequest(r)...)
|
||||||
|
t.propagators.Inject(ctx, propagation.HeaderCarrier(r.Header))
|
||||||
|
|
||||||
|
res, err := t.rt.RoundTrip(r)
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.End()
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
span.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(res.StatusCode)...)
|
||||||
|
span.SetStatus(semconv.SpanStatusFromHTTPStatusCode(res.StatusCode))
|
||||||
|
res.Body = &wrappedBody{ctx: ctx, span: span, body: res.Body}
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedBody struct {
|
||||||
|
ctx context.Context
|
||||||
|
span trace.Span
|
||||||
|
body io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ io.ReadCloser = &wrappedBody{}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wb *wrappedBody) Close() error {
|
||||||
|
wb.span.End()
|
||||||
|
return wb.body.Close()
|
||||||
|
}
|
96
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/wrap.go
generated
vendored
Normal file
96
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/wrap.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
// 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 (
|
||||||
|
"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 returned 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *respWriterWrapper) WriteHeader(statusCode int) {
|
||||||
|
if w.wroteHeader {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.wroteHeader = true
|
||||||
|
w.statusCode = statusCode
|
||||||
|
w.props.Inject(w.ctx, propagation.HeaderCarrier(w.Header()))
|
||||||
|
w.ResponseWriter.WriteHeader(statusCode)
|
||||||
|
}
|
|
@ -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.
|
|
@ -0,0 +1,348 @@
|
||||||
|
// 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 global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
"go.opentelemetry.io/otel/metric/number"
|
||||||
|
"go.opentelemetry.io/otel/metric/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file contains the forwarding implementation of MeterProvider used as
|
||||||
|
// the default global instance. Metric events using instruments provided by
|
||||||
|
// this implementation are no-ops until the first Meter implementation is set
|
||||||
|
// as the global provider.
|
||||||
|
//
|
||||||
|
// The implementation here uses Mutexes to maintain a list of active Meters in
|
||||||
|
// the MeterProvider and Instruments in each Meter, under the assumption that
|
||||||
|
// these interfaces are not performance-critical.
|
||||||
|
//
|
||||||
|
// We have the invariant that setDelegate() will be called before a new
|
||||||
|
// MeterProvider implementation is registered as the global provider. Mutexes
|
||||||
|
// in the MeterProvider and Meters ensure that each instrument has a delegate
|
||||||
|
// before the global provider is set.
|
||||||
|
//
|
||||||
|
// Bound instrument operations are implemented by delegating to the
|
||||||
|
// instrument after it is registered, with a sync.Once initializer to
|
||||||
|
// protect against races with Release().
|
||||||
|
//
|
||||||
|
// Metric uniqueness checking is implemented by calling the exported
|
||||||
|
// methods of the api/metric/registry package.
|
||||||
|
|
||||||
|
type meterKey struct {
|
||||||
|
Name, Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
type meterProvider struct {
|
||||||
|
delegate metric.MeterProvider
|
||||||
|
|
||||||
|
// lock protects `delegate` and `meters`.
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
// meters maintains a unique entry for every named Meter
|
||||||
|
// that has been registered through the global instance.
|
||||||
|
meters map[meterKey]*meterEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type meterImpl struct {
|
||||||
|
delegate unsafe.Pointer // (*metric.MeterImpl)
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
syncInsts []*syncImpl
|
||||||
|
asyncInsts []*asyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
type meterEntry struct {
|
||||||
|
unique metric.MeterImpl
|
||||||
|
impl meterImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
type instrument struct {
|
||||||
|
descriptor metric.Descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
type syncImpl struct {
|
||||||
|
delegate unsafe.Pointer // (*metric.SyncImpl)
|
||||||
|
|
||||||
|
instrument
|
||||||
|
}
|
||||||
|
|
||||||
|
type asyncImpl struct {
|
||||||
|
delegate unsafe.Pointer // (*metric.AsyncImpl)
|
||||||
|
|
||||||
|
instrument
|
||||||
|
|
||||||
|
runner metric.AsyncRunner
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncImpler is implemented by all of the sync metric
|
||||||
|
// instruments.
|
||||||
|
type SyncImpler interface {
|
||||||
|
SyncImpl() metric.SyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncImpler is implemented by all of the async
|
||||||
|
// metric instruments.
|
||||||
|
type AsyncImpler interface {
|
||||||
|
AsyncImpl() metric.AsyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
type syncHandle struct {
|
||||||
|
delegate unsafe.Pointer // (*metric.BoundInstrumentImpl)
|
||||||
|
|
||||||
|
inst *syncImpl
|
||||||
|
labels []attribute.KeyValue
|
||||||
|
|
||||||
|
initialize sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ metric.MeterProvider = &meterProvider{}
|
||||||
|
var _ metric.MeterImpl = &meterImpl{}
|
||||||
|
var _ metric.InstrumentImpl = &syncImpl{}
|
||||||
|
var _ metric.BoundSyncImpl = &syncHandle{}
|
||||||
|
var _ metric.AsyncImpl = &asyncImpl{}
|
||||||
|
|
||||||
|
func (inst *instrument) Descriptor() metric.Descriptor {
|
||||||
|
return inst.descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeterProvider interface and delegation
|
||||||
|
|
||||||
|
func newMeterProvider() *meterProvider {
|
||||||
|
return &meterProvider{
|
||||||
|
meters: map[meterKey]*meterEntry{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *meterProvider) setDelegate(provider metric.MeterProvider) {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
p.delegate = provider
|
||||||
|
for key, entry := range p.meters {
|
||||||
|
entry.impl.setDelegate(key.Name, key.Version, provider)
|
||||||
|
}
|
||||||
|
p.meters = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *meterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
if p.delegate != nil {
|
||||||
|
return p.delegate.Meter(instrumentationName, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := meterKey{
|
||||||
|
Name: instrumentationName,
|
||||||
|
Version: metric.NewMeterConfig(opts...).InstrumentationVersion(),
|
||||||
|
}
|
||||||
|
entry, ok := p.meters[key]
|
||||||
|
if !ok {
|
||||||
|
entry = &meterEntry{}
|
||||||
|
entry.unique = registry.NewUniqueInstrumentMeterImpl(&entry.impl)
|
||||||
|
p.meters[key] = entry
|
||||||
|
|
||||||
|
}
|
||||||
|
return metric.WrapMeterImpl(entry.unique, key.Name, metric.WithInstrumentationVersion(key.Version))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meter interface and delegation
|
||||||
|
|
||||||
|
func (m *meterImpl) setDelegate(name, version string, provider metric.MeterProvider) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
d := new(metric.MeterImpl)
|
||||||
|
*d = provider.Meter(name, metric.WithInstrumentationVersion(version)).MeterImpl()
|
||||||
|
m.delegate = unsafe.Pointer(d)
|
||||||
|
|
||||||
|
for _, inst := range m.syncInsts {
|
||||||
|
inst.setDelegate(*d)
|
||||||
|
}
|
||||||
|
m.syncInsts = nil
|
||||||
|
for _, obs := range m.asyncInsts {
|
||||||
|
obs.setDelegate(*d)
|
||||||
|
}
|
||||||
|
m.asyncInsts = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *meterImpl) NewSyncInstrument(desc metric.Descriptor) (metric.SyncImpl, error) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
if meterPtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
|
||||||
|
return (*meterPtr).NewSyncInstrument(desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
inst := &syncImpl{
|
||||||
|
instrument: instrument{
|
||||||
|
descriptor: desc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
m.syncInsts = append(m.syncInsts, inst)
|
||||||
|
return inst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronous delegation
|
||||||
|
|
||||||
|
func (inst *syncImpl) setDelegate(d metric.MeterImpl) {
|
||||||
|
implPtr := new(metric.SyncImpl)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
*implPtr, err = d.NewSyncInstrument(inst.descriptor)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// TODO: There is no standard way to deliver this error to the user.
|
||||||
|
// See https://github.com/open-telemetry/opentelemetry-go/issues/514
|
||||||
|
// Note that the default SDK will not generate any errors yet, this is
|
||||||
|
// only for added safety.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inst *syncImpl) Implementation() interface{} {
|
||||||
|
if implPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
|
||||||
|
return (*implPtr).Implementation()
|
||||||
|
}
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inst *syncImpl) Bind(labels []attribute.KeyValue) metric.BoundSyncImpl {
|
||||||
|
if implPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
|
||||||
|
return (*implPtr).Bind(labels)
|
||||||
|
}
|
||||||
|
return &syncHandle{
|
||||||
|
inst: inst,
|
||||||
|
labels: labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bound *syncHandle) Unbind() {
|
||||||
|
bound.initialize.Do(func() {})
|
||||||
|
|
||||||
|
implPtr := (*metric.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate))
|
||||||
|
|
||||||
|
if implPtr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
(*implPtr).Unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async delegation
|
||||||
|
|
||||||
|
func (m *meterImpl) NewAsyncInstrument(
|
||||||
|
desc metric.Descriptor,
|
||||||
|
runner metric.AsyncRunner,
|
||||||
|
) (metric.AsyncImpl, error) {
|
||||||
|
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
if meterPtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
|
||||||
|
return (*meterPtr).NewAsyncInstrument(desc, runner)
|
||||||
|
}
|
||||||
|
|
||||||
|
inst := &asyncImpl{
|
||||||
|
instrument: instrument{
|
||||||
|
descriptor: desc,
|
||||||
|
},
|
||||||
|
runner: runner,
|
||||||
|
}
|
||||||
|
m.asyncInsts = append(m.asyncInsts, inst)
|
||||||
|
return inst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obs *asyncImpl) Implementation() interface{} {
|
||||||
|
if implPtr := (*metric.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil {
|
||||||
|
return (*implPtr).Implementation()
|
||||||
|
}
|
||||||
|
return obs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obs *asyncImpl) setDelegate(d metric.MeterImpl) {
|
||||||
|
implPtr := new(metric.AsyncImpl)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
*implPtr, err = d.NewAsyncInstrument(obs.descriptor, obs.runner)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// TODO: There is no standard way to deliver this error to the user.
|
||||||
|
// See https://github.com/open-telemetry/opentelemetry-go/issues/514
|
||||||
|
// Note that the default SDK will not generate any errors yet, this is
|
||||||
|
// only for added safety.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StorePointer(&obs.delegate, unsafe.Pointer(implPtr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metric updates
|
||||||
|
|
||||||
|
func (m *meterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurements ...metric.Measurement) {
|
||||||
|
if delegatePtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil {
|
||||||
|
(*delegatePtr).RecordBatch(ctx, labels, measurements...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inst *syncImpl) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
|
||||||
|
if instPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil {
|
||||||
|
(*instPtr).RecordOne(ctx, number, labels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bound instrument initialization
|
||||||
|
|
||||||
|
func (bound *syncHandle) RecordOne(ctx context.Context, number number.Number) {
|
||||||
|
instPtr := (*metric.SyncImpl)(atomic.LoadPointer(&bound.inst.delegate))
|
||||||
|
if instPtr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var implPtr *metric.BoundSyncImpl
|
||||||
|
bound.initialize.Do(func() {
|
||||||
|
implPtr = new(metric.BoundSyncImpl)
|
||||||
|
*implPtr = (*instPtr).Bind(bound.labels)
|
||||||
|
atomic.StorePointer(&bound.delegate, unsafe.Pointer(implPtr))
|
||||||
|
})
|
||||||
|
if implPtr == nil {
|
||||||
|
implPtr = (*metric.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate))
|
||||||
|
}
|
||||||
|
// This may still be nil if instrument was created and bound
|
||||||
|
// without a delegate, then the instrument was set to have a
|
||||||
|
// delegate and unbound.
|
||||||
|
if implPtr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
(*implPtr).RecordOne(ctx, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AtomicFieldOffsets() map[string]uintptr {
|
||||||
|
return map[string]uintptr{
|
||||||
|
"meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate),
|
||||||
|
"meterImpl.delegate": unsafe.Offsetof(meterImpl{}.delegate),
|
||||||
|
"syncImpl.delegate": unsafe.Offsetof(syncImpl{}.delegate),
|
||||||
|
"asyncImpl.delegate": unsafe.Offsetof(asyncImpl{}.delegate),
|
||||||
|
"syncHandle.delegate": unsafe.Offsetof(syncHandle{}.delegate),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// 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 global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
type meterProviderHolder struct {
|
||||||
|
mp metric.MeterProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalMeter = defaultMeterValue()
|
||||||
|
|
||||||
|
delegateMeterOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// MeterProvider is the internal implementation for global.MeterProvider.
|
||||||
|
func MeterProvider() metric.MeterProvider {
|
||||||
|
return globalMeter.Load().(meterProviderHolder).mp
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMeterProvider is the internal implementation for global.SetMeterProvider.
|
||||||
|
func SetMeterProvider(mp metric.MeterProvider) {
|
||||||
|
delegateMeterOnce.Do(func() {
|
||||||
|
current := MeterProvider()
|
||||||
|
|
||||||
|
if current == mp {
|
||||||
|
// Setting the provider to the prior default is nonsense, panic.
|
||||||
|
// Panic is acceptable because we are likely still early in the
|
||||||
|
// process lifetime.
|
||||||
|
panic("invalid MeterProvider, the global instance cannot be reinstalled")
|
||||||
|
} else if def, ok := current.(*meterProvider); ok {
|
||||||
|
def.setDelegate(mp)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
globalMeter.Store(meterProviderHolder{mp: mp})
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultMeterValue() *atomic.Value {
|
||||||
|
v := &atomic.Value{}
|
||||||
|
v.Store(meterProviderHolder{mp: newMeterProvider()})
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetForTest restores the initial global state, for testing purposes.
|
||||||
|
func ResetForTest() {
|
||||||
|
globalMeter = defaultMeterValue()
|
||||||
|
delegateMeterOnce = sync.Once{}
|
||||||
|
}
|
|
@ -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.
|
|
@ -0,0 +1,141 @@
|
||||||
|
// 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 metric // import "go.opentelemetry.io/otel/metric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.opentelemetry.io/otel/metric/unit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstrumentConfig contains options for metric instrument descriptors.
|
||||||
|
type InstrumentConfig struct {
|
||||||
|
description string
|
||||||
|
unit unit.Unit
|
||||||
|
instrumentationName string
|
||||||
|
instrumentationVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description describes the instrument in human-readable terms.
|
||||||
|
func (cfg InstrumentConfig) Description() string {
|
||||||
|
return cfg.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit describes the measurement unit for a instrument.
|
||||||
|
func (cfg InstrumentConfig) Unit() unit.Unit {
|
||||||
|
return cfg.unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentationName is the name of the library providing
|
||||||
|
// instrumentation.
|
||||||
|
func (cfg InstrumentConfig) InstrumentationName() string {
|
||||||
|
return cfg.instrumentationName
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentationVersion is the version of the library providing
|
||||||
|
// instrumentation.
|
||||||
|
func (cfg InstrumentConfig) InstrumentationVersion() string {
|
||||||
|
return cfg.instrumentationVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentOption is an interface for applying metric instrument options.
|
||||||
|
type InstrumentOption interface {
|
||||||
|
// ApplyMeter is used to set a InstrumentOption value of a
|
||||||
|
// InstrumentConfig.
|
||||||
|
applyInstrument(*InstrumentConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInstrumentConfig creates a new InstrumentConfig
|
||||||
|
// and applies all the given options.
|
||||||
|
func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig {
|
||||||
|
var config InstrumentConfig
|
||||||
|
for _, o := range opts {
|
||||||
|
o.applyInstrument(&config)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
type instrumentOptionFunc func(*InstrumentConfig)
|
||||||
|
|
||||||
|
func (fn instrumentOptionFunc) applyInstrument(cfg *InstrumentConfig) {
|
||||||
|
fn(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDescription applies provided description.
|
||||||
|
func WithDescription(desc string) InstrumentOption {
|
||||||
|
return instrumentOptionFunc(func(cfg *InstrumentConfig) {
|
||||||
|
cfg.description = desc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUnit applies provided unit.
|
||||||
|
func WithUnit(unit unit.Unit) InstrumentOption {
|
||||||
|
return instrumentOptionFunc(func(cfg *InstrumentConfig) {
|
||||||
|
cfg.unit = unit
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInstrumentationName sets the instrumentation name.
|
||||||
|
func WithInstrumentationName(name string) InstrumentOption {
|
||||||
|
return instrumentOptionFunc(func(cfg *InstrumentConfig) {
|
||||||
|
cfg.instrumentationName = name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeterConfig contains options for Meters.
|
||||||
|
type MeterConfig struct {
|
||||||
|
instrumentationVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentationVersion is the version of the library providing instrumentation.
|
||||||
|
func (cfg MeterConfig) InstrumentationVersion() string {
|
||||||
|
return cfg.instrumentationVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeterOption is an interface for applying Meter options.
|
||||||
|
type MeterOption interface {
|
||||||
|
// ApplyMeter is used to set a MeterOption value of a MeterConfig.
|
||||||
|
applyMeter(*MeterConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMeterConfig creates a new MeterConfig and applies
|
||||||
|
// all the given options.
|
||||||
|
func NewMeterConfig(opts ...MeterOption) MeterConfig {
|
||||||
|
var config MeterConfig
|
||||||
|
for _, o := range opts {
|
||||||
|
o.applyMeter(&config)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentMeterOption are options that can be used as both an InstrumentOption
|
||||||
|
// and MeterOption
|
||||||
|
type InstrumentMeterOption interface {
|
||||||
|
InstrumentOption
|
||||||
|
MeterOption
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInstrumentationVersion sets the instrumentation version.
|
||||||
|
func WithInstrumentationVersion(version string) InstrumentMeterOption {
|
||||||
|
return instrumentationVersionOption(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
type instrumentationVersionOption string
|
||||||
|
|
||||||
|
func (i instrumentationVersionOption) applyMeter(config *MeterConfig) {
|
||||||
|
config.instrumentationVersion = string(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i instrumentationVersionOption) applyInstrument(config *InstrumentConfig) {
|
||||||
|
config.instrumentationVersion = string(i)
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// 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 metric provides an implementation of the metrics part of the
|
||||||
|
OpenTelemetry API.
|
||||||
|
|
||||||
|
This package is currently in a pre-GA phase. Backwards incompatible changes
|
||||||
|
may be introduced in subsequent minor version releases as we work to track the
|
||||||
|
evolving OpenTelemetry specification and user feedback.
|
||||||
|
|
||||||
|
Measurements can be made about an operation being performed or the state of a
|
||||||
|
system in general. These measurements can be crucial to the reliable operation
|
||||||
|
of code and provide valuable insights about the inner workings of a system.
|
||||||
|
|
||||||
|
Measurements are made using instruments provided by this package. The type of
|
||||||
|
instrument used will depend on the type of measurement being made and of what
|
||||||
|
part of a system is being measured.
|
||||||
|
|
||||||
|
Instruments are categorized as Synchronous or Asynchronous and independently
|
||||||
|
as Adding or Grouping. Synchronous instruments are called by the user with a
|
||||||
|
Context. Asynchronous instruments are called by the SDK during collection.
|
||||||
|
Additive instruments are semantically intended for capturing a sum. Grouping
|
||||||
|
instruments are intended for capturing a distribution.
|
||||||
|
|
||||||
|
Additive instruments may be monotonic, in which case they are non-decreasing
|
||||||
|
and naturally define a rate.
|
||||||
|
|
||||||
|
The synchronous instrument names are:
|
||||||
|
|
||||||
|
Counter: additive, monotonic
|
||||||
|
UpDownCounter: additive
|
||||||
|
ValueRecorder: grouping
|
||||||
|
|
||||||
|
and the asynchronous instruments are:
|
||||||
|
|
||||||
|
SumObserver: additive, monotonic
|
||||||
|
UpDownSumObserver: additive
|
||||||
|
ValueObserver: grouping
|
||||||
|
|
||||||
|
All instruments are provided with support for either float64 or int64 input
|
||||||
|
values.
|
||||||
|
|
||||||
|
An instrument is created using a Meter. Additionally, a Meter is used to
|
||||||
|
record batches of synchronous measurements or asynchronous observations. A
|
||||||
|
Meter is obtained using a MeterProvider. A Meter, like a Tracer, is unique to
|
||||||
|
the instrumentation it instruments and must be named and versioned when
|
||||||
|
created with a MeterProvider with the name and version of the instrumentation
|
||||||
|
library.
|
||||||
|
|
||||||
|
Instrumentation should be designed to accept a MeterProvider from which it can
|
||||||
|
create its own unique Meter. Alternatively, the registered global
|
||||||
|
MeterProvider from the go.opentelemetry.io/otel package can be used as a
|
||||||
|
default.
|
||||||
|
*/
|
||||||
|
package metric // import "go.opentelemetry.io/otel/metric"
|
|
@ -0,0 +1,49 @@
|
||||||
|
// 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 global // import "go.opentelemetry.io/otel/metric/global"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.opentelemetry.io/otel/internal/metric/global"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Meter creates an implementation of the Meter interface from the global
|
||||||
|
// MeterProvider. The instrumentationName must be the name of the library
|
||||||
|
// providing instrumentation. This name may be the same as the instrumented
|
||||||
|
// code only if that code provides built-in instrumentation. If the
|
||||||
|
// instrumentationName is empty, then a implementation defined default name
|
||||||
|
// will be used instead.
|
||||||
|
//
|
||||||
|
// This is short for MeterProvider().Meter(name)
|
||||||
|
func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
|
||||||
|
return GetMeterProvider().Meter(instrumentationName, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMeterProvider returns the registered global meter provider. If
|
||||||
|
// none is registered then a default meter provider is returned that
|
||||||
|
// forwards the Meter interface to the first registered Meter.
|
||||||
|
//
|
||||||
|
// Use the meter provider to create a named meter. E.g.
|
||||||
|
// meter := global.MeterProvider().Meter("example.com/foo")
|
||||||
|
// or
|
||||||
|
// meter := global.Meter("example.com/foo")
|
||||||
|
func GetMeterProvider() metric.MeterProvider {
|
||||||
|
return global.MeterProvider()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMeterProvider registers `mp` as the global meter provider.
|
||||||
|
func SetMeterProvider(mp metric.MeterProvider) {
|
||||||
|
global.SetMeterProvider(mp)
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
module go.opentelemetry.io/otel/metric
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel => ../
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/bridge/opencensus => ../bridge/opencensus
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/bridge/opentracing => ../bridge/opentracing
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/example/jaeger => ../example/jaeger
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/example/namedtracer => ../example/namedtracer
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/example/opencensus => ../example/opencensus
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/example/otel-collector => ../example/otel-collector
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/example/prom-collector => ../example/prom-collector
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/example/prometheus => ../example/prometheus
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/prometheus => ../exporters/prometheus
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/jaeger => ../exporters/jaeger
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/zipkin => ../exporters/zipkin
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/internal/tools => ../internal/tools
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/metric => ./
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/oteltest => ../oteltest
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/sdk => ../sdk
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/sdk/export/metric => ../sdk/export/metric
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/sdk/metric => ../sdk/metric
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/trace => ../trace
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/go-cmp v0.5.6
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
|
go.opentelemetry.io/otel v1.0.0-RC1
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.21.0
|
||||||
|
)
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../exporters/otlp/otlptrace
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../exporters/otlp/otlptrace/otlptracegrpc
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../exporters/otlp/otlptrace/otlptracehttp
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/internal/metric => ../internal/metric
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../exporters/metric/prometheus
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../exporters/trace/jaeger
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../exporters/trace/zipkin
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../exporters/otlp/otlpmetric
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../exporters/otlp/otlpmetric/otlpmetricgrpc
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric
|
||||||
|
|
||||||
|
replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace
|
|
@ -0,0 +1,15 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by "stringer -type=InstrumentKind"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package metric
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[ValueRecorderInstrumentKind-0]
|
||||||
|
_ = x[ValueObserverInstrumentKind-1]
|
||||||
|
_ = x[CounterInstrumentKind-2]
|
||||||
|
_ = x[UpDownCounterInstrumentKind-3]
|
||||||
|
_ = x[SumObserverInstrumentKind-4]
|
||||||
|
_ = x[UpDownSumObserverInstrumentKind-5]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _InstrumentKind_name = "ValueRecorderInstrumentKindValueObserverInstrumentKindCounterInstrumentKindUpDownCounterInstrumentKindSumObserverInstrumentKindUpDownSumObserverInstrumentKind"
|
||||||
|
|
||||||
|
var _InstrumentKind_index = [...]uint8{0, 27, 54, 75, 102, 127, 158}
|
||||||
|
|
||||||
|
func (i InstrumentKind) String() string {
|
||||||
|
if i < 0 || i >= InstrumentKind(len(_InstrumentKind_index)-1) {
|
||||||
|
return "InstrumentKind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _InstrumentKind_name[_InstrumentKind_index[i]:_InstrumentKind_index[i+1]]
|
||||||
|
}
|
|
@ -0,0 +1,577 @@
|
||||||
|
// 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 metric // import "go.opentelemetry.io/otel/metric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric/number"
|
||||||
|
"go.opentelemetry.io/otel/metric/unit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MeterProvider supports named Meter instances.
|
||||||
|
type MeterProvider interface {
|
||||||
|
// Meter creates an implementation of the Meter interface.
|
||||||
|
// The instrumentationName must be the name of the library providing
|
||||||
|
// instrumentation. This name may be the same as the instrumented code
|
||||||
|
// only if that code provides built-in instrumentation. If the
|
||||||
|
// instrumentationName is empty, then a implementation defined default
|
||||||
|
// name will be used instead.
|
||||||
|
Meter(instrumentationName string, opts ...MeterOption) Meter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meter is the creator of metric instruments.
|
||||||
|
//
|
||||||
|
// An uninitialized Meter is a no-op implementation.
|
||||||
|
type Meter struct {
|
||||||
|
impl MeterImpl
|
||||||
|
name, version string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordBatch atomically records a batch of measurements.
|
||||||
|
func (m Meter) RecordBatch(ctx context.Context, ls []attribute.KeyValue, ms ...Measurement) {
|
||||||
|
if m.impl == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.impl.RecordBatch(ctx, ls, ms...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatchObserver creates a new BatchObserver that supports
|
||||||
|
// making batches of observations for multiple instruments.
|
||||||
|
func (m Meter) NewBatchObserver(callback BatchObserverFunc) BatchObserver {
|
||||||
|
return BatchObserver{
|
||||||
|
meter: m,
|
||||||
|
runner: newBatchAsyncRunner(callback),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64Counter creates a new integer Counter instrument with the
|
||||||
|
// given name, customized with options. May return an error if the
|
||||||
|
// name is invalid (e.g., empty) or improperly registered (e.g.,
|
||||||
|
// duplicate registration).
|
||||||
|
func (m Meter) NewInt64Counter(name string, options ...InstrumentOption) (Int64Counter, error) {
|
||||||
|
return wrapInt64CounterInstrument(
|
||||||
|
m.newSync(name, CounterInstrumentKind, number.Int64Kind, options))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64Counter creates a new floating point Counter with the
|
||||||
|
// given name, customized with options. May return an error if the
|
||||||
|
// name is invalid (e.g., empty) or improperly registered (e.g.,
|
||||||
|
// duplicate registration).
|
||||||
|
func (m Meter) NewFloat64Counter(name string, options ...InstrumentOption) (Float64Counter, error) {
|
||||||
|
return wrapFloat64CounterInstrument(
|
||||||
|
m.newSync(name, CounterInstrumentKind, number.Float64Kind, options))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64UpDownCounter creates a new integer UpDownCounter instrument with the
|
||||||
|
// given name, customized with options. May return an error if the
|
||||||
|
// name is invalid (e.g., empty) or improperly registered (e.g.,
|
||||||
|
// duplicate registration).
|
||||||
|
func (m Meter) NewInt64UpDownCounter(name string, options ...InstrumentOption) (Int64UpDownCounter, error) {
|
||||||
|
return wrapInt64UpDownCounterInstrument(
|
||||||
|
m.newSync(name, UpDownCounterInstrumentKind, number.Int64Kind, options))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64UpDownCounter creates a new floating point UpDownCounter with the
|
||||||
|
// given name, customized with options. May return an error if the
|
||||||
|
// name is invalid (e.g., empty) or improperly registered (e.g.,
|
||||||
|
// duplicate registration).
|
||||||
|
func (m Meter) NewFloat64UpDownCounter(name string, options ...InstrumentOption) (Float64UpDownCounter, error) {
|
||||||
|
return wrapFloat64UpDownCounterInstrument(
|
||||||
|
m.newSync(name, UpDownCounterInstrumentKind, number.Float64Kind, options))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64ValueRecorder creates a new integer ValueRecorder instrument with the
|
||||||
|
// given name, customized with options. May return an error if the
|
||||||
|
// name is invalid (e.g., empty) or improperly registered (e.g.,
|
||||||
|
// duplicate registration).
|
||||||
|
func (m Meter) NewInt64ValueRecorder(name string, opts ...InstrumentOption) (Int64ValueRecorder, error) {
|
||||||
|
return wrapInt64ValueRecorderInstrument(
|
||||||
|
m.newSync(name, ValueRecorderInstrumentKind, number.Int64Kind, opts))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64ValueRecorder creates a new floating point ValueRecorder with the
|
||||||
|
// given name, customized with options. May return an error if the
|
||||||
|
// name is invalid (e.g., empty) or improperly registered (e.g.,
|
||||||
|
// duplicate registration).
|
||||||
|
func (m Meter) NewFloat64ValueRecorder(name string, opts ...InstrumentOption) (Float64ValueRecorder, error) {
|
||||||
|
return wrapFloat64ValueRecorderInstrument(
|
||||||
|
m.newSync(name, ValueRecorderInstrumentKind, number.Float64Kind, opts))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64ValueObserver creates a new integer ValueObserver instrument
|
||||||
|
// with the given name, running a given callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (m Meter) NewInt64ValueObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64ValueObserver, error) {
|
||||||
|
if callback == nil {
|
||||||
|
return wrapInt64ValueObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapInt64ValueObserverInstrument(
|
||||||
|
m.newAsync(name, ValueObserverInstrumentKind, number.Int64Kind, opts,
|
||||||
|
newInt64AsyncRunner(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64ValueObserver creates a new floating point ValueObserver with
|
||||||
|
// the given name, running a given callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (m Meter) NewFloat64ValueObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64ValueObserver, error) {
|
||||||
|
if callback == nil {
|
||||||
|
return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapFloat64ValueObserverInstrument(
|
||||||
|
m.newAsync(name, ValueObserverInstrumentKind, number.Float64Kind, opts,
|
||||||
|
newFloat64AsyncRunner(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64SumObserver creates a new integer SumObserver instrument
|
||||||
|
// with the given name, running a given callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (m Meter) NewInt64SumObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64SumObserver, error) {
|
||||||
|
if callback == nil {
|
||||||
|
return wrapInt64SumObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapInt64SumObserverInstrument(
|
||||||
|
m.newAsync(name, SumObserverInstrumentKind, number.Int64Kind, opts,
|
||||||
|
newInt64AsyncRunner(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64SumObserver creates a new floating point SumObserver with
|
||||||
|
// the given name, running a given callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (m Meter) NewFloat64SumObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64SumObserver, error) {
|
||||||
|
if callback == nil {
|
||||||
|
return wrapFloat64SumObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapFloat64SumObserverInstrument(
|
||||||
|
m.newAsync(name, SumObserverInstrumentKind, number.Float64Kind, opts,
|
||||||
|
newFloat64AsyncRunner(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument
|
||||||
|
// with the given name, running a given callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (m Meter) NewInt64UpDownSumObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64UpDownSumObserver, error) {
|
||||||
|
if callback == nil {
|
||||||
|
return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapInt64UpDownSumObserverInstrument(
|
||||||
|
m.newAsync(name, UpDownSumObserverInstrumentKind, number.Int64Kind, opts,
|
||||||
|
newInt64AsyncRunner(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with
|
||||||
|
// the given name, running a given callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (m Meter) NewFloat64UpDownSumObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64UpDownSumObserver, error) {
|
||||||
|
if callback == nil {
|
||||||
|
return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapFloat64UpDownSumObserverInstrument(
|
||||||
|
m.newAsync(name, UpDownSumObserverInstrumentKind, number.Float64Kind, opts,
|
||||||
|
newFloat64AsyncRunner(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64ValueObserver creates a new integer ValueObserver instrument
|
||||||
|
// with the given name, running in a batch callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (b BatchObserver) NewInt64ValueObserver(name string, opts ...InstrumentOption) (Int64ValueObserver, error) {
|
||||||
|
if b.runner == nil {
|
||||||
|
return wrapInt64ValueObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapInt64ValueObserverInstrument(
|
||||||
|
b.meter.newAsync(name, ValueObserverInstrumentKind, number.Int64Kind, opts, b.runner))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64ValueObserver creates a new floating point ValueObserver with
|
||||||
|
// the given name, running in a batch callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (b BatchObserver) NewFloat64ValueObserver(name string, opts ...InstrumentOption) (Float64ValueObserver, error) {
|
||||||
|
if b.runner == nil {
|
||||||
|
return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapFloat64ValueObserverInstrument(
|
||||||
|
b.meter.newAsync(name, ValueObserverInstrumentKind, number.Float64Kind, opts,
|
||||||
|
b.runner))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64SumObserver creates a new integer SumObserver instrument
|
||||||
|
// with the given name, running in a batch callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (b BatchObserver) NewInt64SumObserver(name string, opts ...InstrumentOption) (Int64SumObserver, error) {
|
||||||
|
if b.runner == nil {
|
||||||
|
return wrapInt64SumObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapInt64SumObserverInstrument(
|
||||||
|
b.meter.newAsync(name, SumObserverInstrumentKind, number.Int64Kind, opts, b.runner))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64SumObserver creates a new floating point SumObserver with
|
||||||
|
// the given name, running in a batch callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (b BatchObserver) NewFloat64SumObserver(name string, opts ...InstrumentOption) (Float64SumObserver, error) {
|
||||||
|
if b.runner == nil {
|
||||||
|
return wrapFloat64SumObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapFloat64SumObserverInstrument(
|
||||||
|
b.meter.newAsync(name, SumObserverInstrumentKind, number.Float64Kind, opts,
|
||||||
|
b.runner))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument
|
||||||
|
// with the given name, running in a batch callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (b BatchObserver) NewInt64UpDownSumObserver(name string, opts ...InstrumentOption) (Int64UpDownSumObserver, error) {
|
||||||
|
if b.runner == nil {
|
||||||
|
return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapInt64UpDownSumObserverInstrument(
|
||||||
|
b.meter.newAsync(name, UpDownSumObserverInstrumentKind, number.Int64Kind, opts, b.runner))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with
|
||||||
|
// the given name, running in a batch callback, and customized with
|
||||||
|
// options. May return an error if the name is invalid (e.g., empty)
|
||||||
|
// or improperly registered (e.g., duplicate registration).
|
||||||
|
func (b BatchObserver) NewFloat64UpDownSumObserver(name string, opts ...InstrumentOption) (Float64UpDownSumObserver, error) {
|
||||||
|
if b.runner == nil {
|
||||||
|
return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil)
|
||||||
|
}
|
||||||
|
return wrapFloat64UpDownSumObserverInstrument(
|
||||||
|
b.meter.newAsync(name, UpDownSumObserverInstrumentKind, number.Float64Kind, opts,
|
||||||
|
b.runner))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeterImpl returns the underlying MeterImpl of this Meter.
|
||||||
|
func (m Meter) MeterImpl() MeterImpl {
|
||||||
|
return m.impl
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAsync constructs one new asynchronous instrument.
|
||||||
|
func (m Meter) newAsync(
|
||||||
|
name string,
|
||||||
|
mkind InstrumentKind,
|
||||||
|
nkind number.Kind,
|
||||||
|
opts []InstrumentOption,
|
||||||
|
runner AsyncRunner,
|
||||||
|
) (
|
||||||
|
AsyncImpl,
|
||||||
|
error,
|
||||||
|
) {
|
||||||
|
if m.impl == nil {
|
||||||
|
return NoopAsync{}, nil
|
||||||
|
}
|
||||||
|
desc := NewDescriptor(name, mkind, nkind, opts...)
|
||||||
|
desc.config.instrumentationName = m.name
|
||||||
|
desc.config.instrumentationVersion = m.version
|
||||||
|
return m.impl.NewAsyncInstrument(desc, runner)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSync constructs one new synchronous instrument.
|
||||||
|
func (m Meter) newSync(
|
||||||
|
name string,
|
||||||
|
metricKind InstrumentKind,
|
||||||
|
numberKind number.Kind,
|
||||||
|
opts []InstrumentOption,
|
||||||
|
) (
|
||||||
|
SyncImpl,
|
||||||
|
error,
|
||||||
|
) {
|
||||||
|
if m.impl == nil {
|
||||||
|
return NoopSync{}, nil
|
||||||
|
}
|
||||||
|
desc := NewDescriptor(name, metricKind, numberKind, opts...)
|
||||||
|
desc.config.instrumentationName = m.name
|
||||||
|
desc.config.instrumentationVersion = m.version
|
||||||
|
return m.impl.NewSyncInstrument(desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeterMust is a wrapper for Meter interfaces that panics when any
|
||||||
|
// instrument constructor encounters an error.
|
||||||
|
type MeterMust struct {
|
||||||
|
meter Meter
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchObserverMust is a wrapper for BatchObserver that panics when
|
||||||
|
// any instrument constructor encounters an error.
|
||||||
|
type BatchObserverMust struct {
|
||||||
|
batch BatchObserver
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must constructs a MeterMust implementation from a Meter, allowing
|
||||||
|
// the application to panic when any instrument constructor yields an
|
||||||
|
// error.
|
||||||
|
func Must(meter Meter) MeterMust {
|
||||||
|
return MeterMust{meter: meter}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64Counter calls `Meter.NewInt64Counter` and returns the
|
||||||
|
// instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewInt64Counter(name string, cos ...InstrumentOption) Int64Counter {
|
||||||
|
if inst, err := mm.meter.NewInt64Counter(name, cos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64Counter calls `Meter.NewFloat64Counter` and returns the
|
||||||
|
// instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewFloat64Counter(name string, cos ...InstrumentOption) Float64Counter {
|
||||||
|
if inst, err := mm.meter.NewFloat64Counter(name, cos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64UpDownCounter calls `Meter.NewInt64UpDownCounter` and returns the
|
||||||
|
// instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewInt64UpDownCounter(name string, cos ...InstrumentOption) Int64UpDownCounter {
|
||||||
|
if inst, err := mm.meter.NewInt64UpDownCounter(name, cos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64UpDownCounter calls `Meter.NewFloat64UpDownCounter` and returns the
|
||||||
|
// instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewFloat64UpDownCounter(name string, cos ...InstrumentOption) Float64UpDownCounter {
|
||||||
|
if inst, err := mm.meter.NewFloat64UpDownCounter(name, cos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64ValueRecorder calls `Meter.NewInt64ValueRecorder` and returns the
|
||||||
|
// instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewInt64ValueRecorder(name string, mos ...InstrumentOption) Int64ValueRecorder {
|
||||||
|
if inst, err := mm.meter.NewInt64ValueRecorder(name, mos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64ValueRecorder calls `Meter.NewFloat64ValueRecorder` and returns the
|
||||||
|
// instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewFloat64ValueRecorder(name string, mos ...InstrumentOption) Float64ValueRecorder {
|
||||||
|
if inst, err := mm.meter.NewFloat64ValueRecorder(name, mos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64ValueObserver calls `Meter.NewInt64ValueObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewInt64ValueObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64ValueObserver {
|
||||||
|
if inst, err := mm.meter.NewInt64ValueObserver(name, callback, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64ValueObserver calls `Meter.NewFloat64ValueObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewFloat64ValueObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64ValueObserver {
|
||||||
|
if inst, err := mm.meter.NewFloat64ValueObserver(name, callback, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64SumObserver calls `Meter.NewInt64SumObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewInt64SumObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64SumObserver {
|
||||||
|
if inst, err := mm.meter.NewInt64SumObserver(name, callback, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64SumObserver calls `Meter.NewFloat64SumObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewFloat64SumObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64SumObserver {
|
||||||
|
if inst, err := mm.meter.NewFloat64SumObserver(name, callback, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64UpDownSumObserver calls `Meter.NewInt64UpDownSumObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewInt64UpDownSumObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64UpDownSumObserver {
|
||||||
|
if inst, err := mm.meter.NewInt64UpDownSumObserver(name, callback, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64UpDownSumObserver calls `Meter.NewFloat64UpDownSumObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (mm MeterMust) NewFloat64UpDownSumObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64UpDownSumObserver {
|
||||||
|
if inst, err := mm.meter.NewFloat64UpDownSumObserver(name, callback, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatchObserver returns a wrapper around BatchObserver that panics
|
||||||
|
// when any instrument constructor returns an error.
|
||||||
|
func (mm MeterMust) NewBatchObserver(callback BatchObserverFunc) BatchObserverMust {
|
||||||
|
return BatchObserverMust{
|
||||||
|
batch: mm.meter.NewBatchObserver(callback),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64ValueObserver calls `BatchObserver.NewInt64ValueObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (bm BatchObserverMust) NewInt64ValueObserver(name string, oos ...InstrumentOption) Int64ValueObserver {
|
||||||
|
if inst, err := bm.batch.NewInt64ValueObserver(name, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64ValueObserver calls `BatchObserver.NewFloat64ValueObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (bm BatchObserverMust) NewFloat64ValueObserver(name string, oos ...InstrumentOption) Float64ValueObserver {
|
||||||
|
if inst, err := bm.batch.NewFloat64ValueObserver(name, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64SumObserver calls `BatchObserver.NewInt64SumObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (bm BatchObserverMust) NewInt64SumObserver(name string, oos ...InstrumentOption) Int64SumObserver {
|
||||||
|
if inst, err := bm.batch.NewInt64SumObserver(name, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64SumObserver calls `BatchObserver.NewFloat64SumObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (bm BatchObserverMust) NewFloat64SumObserver(name string, oos ...InstrumentOption) Float64SumObserver {
|
||||||
|
if inst, err := bm.batch.NewFloat64SumObserver(name, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64UpDownSumObserver calls `BatchObserver.NewInt64UpDownSumObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (bm BatchObserverMust) NewInt64UpDownSumObserver(name string, oos ...InstrumentOption) Int64UpDownSumObserver {
|
||||||
|
if inst, err := bm.batch.NewInt64UpDownSumObserver(name, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64UpDownSumObserver calls `BatchObserver.NewFloat64UpDownSumObserver` and
|
||||||
|
// returns the instrument, panicking if it encounters an error.
|
||||||
|
func (bm BatchObserverMust) NewFloat64UpDownSumObserver(name string, oos ...InstrumentOption) Float64UpDownSumObserver {
|
||||||
|
if inst, err := bm.batch.NewFloat64UpDownSumObserver(name, oos...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Descriptor contains all the settings that describe an instrument,
|
||||||
|
// including its name, metric kind, number kind, and the configurable
|
||||||
|
// options.
|
||||||
|
type Descriptor struct {
|
||||||
|
name string
|
||||||
|
instrumentKind InstrumentKind
|
||||||
|
numberKind number.Kind
|
||||||
|
config InstrumentConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDescriptor returns a Descriptor with the given contents.
|
||||||
|
func NewDescriptor(name string, ikind InstrumentKind, nkind number.Kind, opts ...InstrumentOption) Descriptor {
|
||||||
|
return Descriptor{
|
||||||
|
name: name,
|
||||||
|
instrumentKind: ikind,
|
||||||
|
numberKind: nkind,
|
||||||
|
config: NewInstrumentConfig(opts...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the metric instrument's name.
|
||||||
|
func (d Descriptor) Name() string {
|
||||||
|
return d.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentKind returns the specific kind of instrument.
|
||||||
|
func (d Descriptor) InstrumentKind() InstrumentKind {
|
||||||
|
return d.instrumentKind
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description provides a human-readable description of the metric
|
||||||
|
// instrument.
|
||||||
|
func (d Descriptor) Description() string {
|
||||||
|
return d.config.Description()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit describes the units of the metric instrument. Unitless
|
||||||
|
// metrics return the empty string.
|
||||||
|
func (d Descriptor) Unit() unit.Unit {
|
||||||
|
return d.config.Unit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberKind returns whether this instrument is declared over int64,
|
||||||
|
// float64, or uint64 values.
|
||||||
|
func (d Descriptor) NumberKind() number.Kind {
|
||||||
|
return d.numberKind
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentationName returns the name of the library that provided
|
||||||
|
// instrumentation for this instrument.
|
||||||
|
func (d Descriptor) InstrumentationName() string {
|
||||||
|
return d.config.InstrumentationName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentationVersion returns the version of the library that provided
|
||||||
|
// instrumentation for this instrument.
|
||||||
|
func (d Descriptor) InstrumentationVersion() string {
|
||||||
|
return d.config.InstrumentationVersion()
|
||||||
|
}
|
|
@ -0,0 +1,777 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:generate stringer -type=InstrumentKind
|
||||||
|
|
||||||
|
package metric // import "go.opentelemetry.io/otel/metric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric/number"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrSDKReturnedNilImpl is returned when a new `MeterImpl` returns nil.
|
||||||
|
var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation")
|
||||||
|
|
||||||
|
// InstrumentKind describes the kind of instrument.
|
||||||
|
type InstrumentKind int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ValueRecorderInstrumentKind indicates a ValueRecorder instrument.
|
||||||
|
ValueRecorderInstrumentKind InstrumentKind = iota
|
||||||
|
// ValueObserverInstrumentKind indicates an ValueObserver instrument.
|
||||||
|
ValueObserverInstrumentKind
|
||||||
|
|
||||||
|
// CounterInstrumentKind indicates a Counter instrument.
|
||||||
|
CounterInstrumentKind
|
||||||
|
// UpDownCounterInstrumentKind indicates a UpDownCounter instrument.
|
||||||
|
UpDownCounterInstrumentKind
|
||||||
|
|
||||||
|
// SumObserverInstrumentKind indicates a SumObserver instrument.
|
||||||
|
SumObserverInstrumentKind
|
||||||
|
// UpDownSumObserverInstrumentKind indicates a UpDownSumObserver
|
||||||
|
// instrument.
|
||||||
|
UpDownSumObserverInstrumentKind
|
||||||
|
)
|
||||||
|
|
||||||
|
// Synchronous returns whether this is a synchronous kind of instrument.
|
||||||
|
func (k InstrumentKind) Synchronous() bool {
|
||||||
|
switch k {
|
||||||
|
case CounterInstrumentKind, UpDownCounterInstrumentKind, ValueRecorderInstrumentKind:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asynchronous returns whether this is an asynchronous kind of instrument.
|
||||||
|
func (k InstrumentKind) Asynchronous() bool {
|
||||||
|
return !k.Synchronous()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding returns whether this kind of instrument adds its inputs (as opposed to Grouping).
|
||||||
|
func (k InstrumentKind) Adding() bool {
|
||||||
|
switch k {
|
||||||
|
case CounterInstrumentKind, UpDownCounterInstrumentKind, SumObserverInstrumentKind, UpDownSumObserverInstrumentKind:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grouping returns whether this kind of instrument groups its inputs (as opposed to Adding).
|
||||||
|
func (k InstrumentKind) Grouping() bool {
|
||||||
|
return !k.Adding()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monotonic returns whether this kind of instrument exposes a non-decreasing sum.
|
||||||
|
func (k InstrumentKind) Monotonic() bool {
|
||||||
|
switch k {
|
||||||
|
case CounterInstrumentKind, SumObserverInstrumentKind:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrecomputedSum returns whether this kind of instrument receives precomputed sums.
|
||||||
|
func (k InstrumentKind) PrecomputedSum() bool {
|
||||||
|
return k.Adding() && k.Asynchronous()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observation is used for reporting an asynchronous batch of metric
|
||||||
|
// values. Instances of this type should be created by asynchronous
|
||||||
|
// instruments (e.g., Int64ValueObserver.Observation()).
|
||||||
|
type Observation struct {
|
||||||
|
// number needs to be aligned for 64-bit atomic operations.
|
||||||
|
number number.Number
|
||||||
|
instrument AsyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64ObserverFunc is a type of callback that integral
|
||||||
|
// observers run.
|
||||||
|
type Int64ObserverFunc func(context.Context, Int64ObserverResult)
|
||||||
|
|
||||||
|
// Float64ObserverFunc is a type of callback that floating point
|
||||||
|
// observers run.
|
||||||
|
type Float64ObserverFunc func(context.Context, Float64ObserverResult)
|
||||||
|
|
||||||
|
// BatchObserverFunc is a callback argument for use with any
|
||||||
|
// Observer instrument that will be reported as a batch of
|
||||||
|
// observations.
|
||||||
|
type BatchObserverFunc func(context.Context, BatchObserverResult)
|
||||||
|
|
||||||
|
// Int64ObserverResult is passed to an observer callback to capture
|
||||||
|
// observations for one asynchronous integer metric instrument.
|
||||||
|
type Int64ObserverResult struct {
|
||||||
|
instrument AsyncImpl
|
||||||
|
function func([]attribute.KeyValue, ...Observation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64ObserverResult is passed to an observer callback to capture
|
||||||
|
// observations for one asynchronous floating point metric instrument.
|
||||||
|
type Float64ObserverResult struct {
|
||||||
|
instrument AsyncImpl
|
||||||
|
function func([]attribute.KeyValue, ...Observation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchObserverResult is passed to a batch observer callback to
|
||||||
|
// capture observations for multiple asynchronous instruments.
|
||||||
|
type BatchObserverResult struct {
|
||||||
|
function func([]attribute.KeyValue, ...Observation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observe captures a single integer value from the associated
|
||||||
|
// instrument callback, with the given labels.
|
||||||
|
func (ir Int64ObserverResult) Observe(value int64, labels ...attribute.KeyValue) {
|
||||||
|
ir.function(labels, Observation{
|
||||||
|
instrument: ir.instrument,
|
||||||
|
number: number.NewInt64Number(value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observe captures a single floating point value from the associated
|
||||||
|
// instrument callback, with the given labels.
|
||||||
|
func (fr Float64ObserverResult) Observe(value float64, labels ...attribute.KeyValue) {
|
||||||
|
fr.function(labels, Observation{
|
||||||
|
instrument: fr.instrument,
|
||||||
|
number: number.NewFloat64Number(value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observe captures a multiple observations from the associated batch
|
||||||
|
// instrument callback, with the given labels.
|
||||||
|
func (br BatchObserverResult) Observe(labels []attribute.KeyValue, obs ...Observation) {
|
||||||
|
br.function(labels, obs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncRunner is expected to convert into an AsyncSingleRunner or an
|
||||||
|
// AsyncBatchRunner. SDKs will encounter an error if the AsyncRunner
|
||||||
|
// does not satisfy one of these interfaces.
|
||||||
|
type AsyncRunner interface {
|
||||||
|
// AnyRunner() is a non-exported method with no functional use
|
||||||
|
// other than to make this a non-empty interface.
|
||||||
|
AnyRunner()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncSingleRunner is an interface implemented by single-observer
|
||||||
|
// callbacks.
|
||||||
|
type AsyncSingleRunner interface {
|
||||||
|
// Run accepts a single instrument and function for capturing
|
||||||
|
// observations of that instrument. Each call to the function
|
||||||
|
// receives one captured observation. (The function accepts
|
||||||
|
// multiple observations so the same implementation can be
|
||||||
|
// used for batch runners.)
|
||||||
|
Run(ctx context.Context, single AsyncImpl, capture func([]attribute.KeyValue, ...Observation))
|
||||||
|
|
||||||
|
AsyncRunner
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncBatchRunner is an interface implemented by batch-observer
|
||||||
|
// callbacks.
|
||||||
|
type AsyncBatchRunner interface {
|
||||||
|
// Run accepts a function for capturing observations of
|
||||||
|
// multiple instruments.
|
||||||
|
Run(ctx context.Context, capture func([]attribute.KeyValue, ...Observation))
|
||||||
|
|
||||||
|
AsyncRunner
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ AsyncSingleRunner = (*Int64ObserverFunc)(nil)
|
||||||
|
var _ AsyncSingleRunner = (*Float64ObserverFunc)(nil)
|
||||||
|
var _ AsyncBatchRunner = (*BatchObserverFunc)(nil)
|
||||||
|
|
||||||
|
// newInt64AsyncRunner returns a single-observer callback for integer Observer instruments.
|
||||||
|
func newInt64AsyncRunner(c Int64ObserverFunc) AsyncSingleRunner {
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFloat64AsyncRunner returns a single-observer callback for floating point Observer instruments.
|
||||||
|
func newFloat64AsyncRunner(c Float64ObserverFunc) AsyncSingleRunner {
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBatchAsyncRunner returns a batch-observer callback use with multiple Observer instruments.
|
||||||
|
func newBatchAsyncRunner(c BatchObserverFunc) AsyncBatchRunner {
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyRunner implements AsyncRunner.
|
||||||
|
func (*Int64ObserverFunc) AnyRunner() {}
|
||||||
|
|
||||||
|
// AnyRunner implements AsyncRunner.
|
||||||
|
func (*Float64ObserverFunc) AnyRunner() {}
|
||||||
|
|
||||||
|
// AnyRunner implements AsyncRunner.
|
||||||
|
func (*BatchObserverFunc) AnyRunner() {}
|
||||||
|
|
||||||
|
// Run implements AsyncSingleRunner.
|
||||||
|
func (i *Int64ObserverFunc) Run(ctx context.Context, impl AsyncImpl, function func([]attribute.KeyValue, ...Observation)) {
|
||||||
|
(*i)(ctx, Int64ObserverResult{
|
||||||
|
instrument: impl,
|
||||||
|
function: function,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements AsyncSingleRunner.
|
||||||
|
func (f *Float64ObserverFunc) Run(ctx context.Context, impl AsyncImpl, function func([]attribute.KeyValue, ...Observation)) {
|
||||||
|
(*f)(ctx, Float64ObserverResult{
|
||||||
|
instrument: impl,
|
||||||
|
function: function,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements AsyncBatchRunner.
|
||||||
|
func (b *BatchObserverFunc) Run(ctx context.Context, function func([]attribute.KeyValue, ...Observation)) {
|
||||||
|
(*b)(ctx, BatchObserverResult{
|
||||||
|
function: function,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapInt64ValueObserverInstrument converts an AsyncImpl into Int64ValueObserver.
|
||||||
|
func wrapInt64ValueObserverInstrument(asyncInst AsyncImpl, err error) (Int64ValueObserver, error) {
|
||||||
|
common, err := checkNewAsync(asyncInst, err)
|
||||||
|
return Int64ValueObserver{asyncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapFloat64ValueObserverInstrument converts an AsyncImpl into Float64ValueObserver.
|
||||||
|
func wrapFloat64ValueObserverInstrument(asyncInst AsyncImpl, err error) (Float64ValueObserver, error) {
|
||||||
|
common, err := checkNewAsync(asyncInst, err)
|
||||||
|
return Float64ValueObserver{asyncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapInt64SumObserverInstrument converts an AsyncImpl into Int64SumObserver.
|
||||||
|
func wrapInt64SumObserverInstrument(asyncInst AsyncImpl, err error) (Int64SumObserver, error) {
|
||||||
|
common, err := checkNewAsync(asyncInst, err)
|
||||||
|
return Int64SumObserver{asyncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapFloat64SumObserverInstrument converts an AsyncImpl into Float64SumObserver.
|
||||||
|
func wrapFloat64SumObserverInstrument(asyncInst AsyncImpl, err error) (Float64SumObserver, error) {
|
||||||
|
common, err := checkNewAsync(asyncInst, err)
|
||||||
|
return Float64SumObserver{asyncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapInt64UpDownSumObserverInstrument converts an AsyncImpl into Int64UpDownSumObserver.
|
||||||
|
func wrapInt64UpDownSumObserverInstrument(asyncInst AsyncImpl, err error) (Int64UpDownSumObserver, error) {
|
||||||
|
common, err := checkNewAsync(asyncInst, err)
|
||||||
|
return Int64UpDownSumObserver{asyncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapFloat64UpDownSumObserverInstrument converts an AsyncImpl into Float64UpDownSumObserver.
|
||||||
|
func wrapFloat64UpDownSumObserverInstrument(asyncInst AsyncImpl, err error) (Float64UpDownSumObserver, error) {
|
||||||
|
common, err := checkNewAsync(asyncInst, err)
|
||||||
|
return Float64UpDownSumObserver{asyncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchObserver represents an Observer callback that can report
|
||||||
|
// observations for multiple instruments.
|
||||||
|
type BatchObserver struct {
|
||||||
|
meter Meter
|
||||||
|
runner AsyncBatchRunner
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64ValueObserver is a metric that captures a set of int64 values at a
|
||||||
|
// point in time.
|
||||||
|
type Int64ValueObserver struct {
|
||||||
|
asyncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64ValueObserver is a metric that captures a set of float64 values
|
||||||
|
// at a point in time.
|
||||||
|
type Float64ValueObserver struct {
|
||||||
|
asyncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64SumObserver is a metric that captures a precomputed sum of
|
||||||
|
// int64 values at a point in time.
|
||||||
|
type Int64SumObserver struct {
|
||||||
|
asyncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64SumObserver is a metric that captures a precomputed sum of
|
||||||
|
// float64 values at a point in time.
|
||||||
|
type Float64SumObserver struct {
|
||||||
|
asyncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64UpDownSumObserver is a metric that captures a precomputed sum of
|
||||||
|
// int64 values at a point in time.
|
||||||
|
type Int64UpDownSumObserver struct {
|
||||||
|
asyncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64UpDownSumObserver is a metric that captures a precomputed sum of
|
||||||
|
// float64 values at a point in time.
|
||||||
|
type Float64UpDownSumObserver struct {
|
||||||
|
asyncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observation returns an Observation, a BatchObserverFunc
|
||||||
|
// argument, for an asynchronous integer instrument.
|
||||||
|
// This returns an implementation-level object for use by the SDK,
|
||||||
|
// users should not refer to this.
|
||||||
|
func (i Int64ValueObserver) Observation(v int64) Observation {
|
||||||
|
return Observation{
|
||||||
|
number: number.NewInt64Number(v),
|
||||||
|
instrument: i.instrument,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observation returns an Observation, a BatchObserverFunc
|
||||||
|
// argument, for an asynchronous integer instrument.
|
||||||
|
// This returns an implementation-level object for use by the SDK,
|
||||||
|
// users should not refer to this.
|
||||||
|
func (f Float64ValueObserver) Observation(v float64) Observation {
|
||||||
|
return Observation{
|
||||||
|
number: number.NewFloat64Number(v),
|
||||||
|
instrument: f.instrument,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observation returns an Observation, a BatchObserverFunc
|
||||||
|
// argument, for an asynchronous integer instrument.
|
||||||
|
// This returns an implementation-level object for use by the SDK,
|
||||||
|
// users should not refer to this.
|
||||||
|
func (i Int64SumObserver) Observation(v int64) Observation {
|
||||||
|
return Observation{
|
||||||
|
number: number.NewInt64Number(v),
|
||||||
|
instrument: i.instrument,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observation returns an Observation, a BatchObserverFunc
|
||||||
|
// argument, for an asynchronous integer instrument.
|
||||||
|
// This returns an implementation-level object for use by the SDK,
|
||||||
|
// users should not refer to this.
|
||||||
|
func (f Float64SumObserver) Observation(v float64) Observation {
|
||||||
|
return Observation{
|
||||||
|
number: number.NewFloat64Number(v),
|
||||||
|
instrument: f.instrument,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observation returns an Observation, a BatchObserverFunc
|
||||||
|
// argument, for an asynchronous integer instrument.
|
||||||
|
// This returns an implementation-level object for use by the SDK,
|
||||||
|
// users should not refer to this.
|
||||||
|
func (i Int64UpDownSumObserver) Observation(v int64) Observation {
|
||||||
|
return Observation{
|
||||||
|
number: number.NewInt64Number(v),
|
||||||
|
instrument: i.instrument,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observation returns an Observation, a BatchObserverFunc
|
||||||
|
// argument, for an asynchronous integer instrument.
|
||||||
|
// This returns an implementation-level object for use by the SDK,
|
||||||
|
// users should not refer to this.
|
||||||
|
func (f Float64UpDownSumObserver) Observation(v float64) Observation {
|
||||||
|
return Observation{
|
||||||
|
number: number.NewFloat64Number(v),
|
||||||
|
instrument: f.instrument,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurement is used for reporting a synchronous batch of metric
|
||||||
|
// values. Instances of this type should be created by synchronous
|
||||||
|
// instruments (e.g., Int64Counter.Measurement()).
|
||||||
|
type Measurement struct {
|
||||||
|
// number needs to be aligned for 64-bit atomic operations.
|
||||||
|
number number.Number
|
||||||
|
instrument SyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncInstrument contains a SyncImpl.
|
||||||
|
type syncInstrument struct {
|
||||||
|
instrument SyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncBoundInstrument contains a BoundSyncImpl.
|
||||||
|
type syncBoundInstrument struct {
|
||||||
|
boundInstrument BoundSyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncInstrument contains a AsyncImpl.
|
||||||
|
type asyncInstrument struct {
|
||||||
|
instrument AsyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncImpl returns the instrument that created this measurement.
|
||||||
|
// This returns an implementation-level object for use by the SDK,
|
||||||
|
// users should not refer to this.
|
||||||
|
func (m Measurement) SyncImpl() SyncImpl {
|
||||||
|
return m.instrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number returns a number recorded in this measurement.
|
||||||
|
func (m Measurement) Number() number.Number {
|
||||||
|
return m.number
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncImpl returns the instrument that created this observation.
|
||||||
|
// This returns an implementation-level object for use by the SDK,
|
||||||
|
// users should not refer to this.
|
||||||
|
func (m Observation) AsyncImpl() AsyncImpl {
|
||||||
|
return m.instrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number returns a number recorded in this observation.
|
||||||
|
func (m Observation) Number() number.Number {
|
||||||
|
return m.number
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncImpl implements AsyncImpl.
|
||||||
|
func (a asyncInstrument) AsyncImpl() AsyncImpl {
|
||||||
|
return a.instrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncImpl returns the implementation object for synchronous instruments.
|
||||||
|
func (s syncInstrument) SyncImpl() SyncImpl {
|
||||||
|
return s.instrument
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s syncInstrument) bind(labels []attribute.KeyValue) syncBoundInstrument {
|
||||||
|
return newSyncBoundInstrument(s.instrument.Bind(labels))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s syncInstrument) float64Measurement(value float64) Measurement {
|
||||||
|
return newMeasurement(s.instrument, number.NewFloat64Number(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s syncInstrument) int64Measurement(value int64) Measurement {
|
||||||
|
return newMeasurement(s.instrument, number.NewInt64Number(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s syncInstrument) directRecord(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
|
||||||
|
s.instrument.RecordOne(ctx, number, labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h syncBoundInstrument) directRecord(ctx context.Context, number number.Number) {
|
||||||
|
h.boundInstrument.RecordOne(ctx, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unbind calls SyncImpl.Unbind.
|
||||||
|
func (h syncBoundInstrument) Unbind() {
|
||||||
|
h.boundInstrument.Unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkNewAsync receives an AsyncImpl and potential
|
||||||
|
// error, and returns the same types, checking for and ensuring that
|
||||||
|
// the returned interface is not nil.
|
||||||
|
func checkNewAsync(instrument AsyncImpl, err error) (asyncInstrument, error) {
|
||||||
|
if instrument == nil {
|
||||||
|
if err == nil {
|
||||||
|
err = ErrSDKReturnedNilImpl
|
||||||
|
}
|
||||||
|
instrument = NoopAsync{}
|
||||||
|
}
|
||||||
|
return asyncInstrument{
|
||||||
|
instrument: instrument,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkNewSync receives an SyncImpl and potential
|
||||||
|
// error, and returns the same types, checking for and ensuring that
|
||||||
|
// the returned interface is not nil.
|
||||||
|
func checkNewSync(instrument SyncImpl, err error) (syncInstrument, error) {
|
||||||
|
if instrument == nil {
|
||||||
|
if err == nil {
|
||||||
|
err = ErrSDKReturnedNilImpl
|
||||||
|
}
|
||||||
|
// Note: an alternate behavior would be to synthesize a new name
|
||||||
|
// or group all duplicately-named instruments of a certain type
|
||||||
|
// together and use a tag for the original name, e.g.,
|
||||||
|
// name = 'invalid.counter.int64'
|
||||||
|
// label = 'original-name=duplicate-counter-name'
|
||||||
|
instrument = NoopSync{}
|
||||||
|
}
|
||||||
|
return syncInstrument{
|
||||||
|
instrument: instrument,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSyncBoundInstrument(boundInstrument BoundSyncImpl) syncBoundInstrument {
|
||||||
|
return syncBoundInstrument{
|
||||||
|
boundInstrument: boundInstrument,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMeasurement(instrument SyncImpl, number number.Number) Measurement {
|
||||||
|
return Measurement{
|
||||||
|
instrument: instrument,
|
||||||
|
number: number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapInt64CounterInstrument converts a SyncImpl into Int64Counter.
|
||||||
|
func wrapInt64CounterInstrument(syncInst SyncImpl, err error) (Int64Counter, error) {
|
||||||
|
common, err := checkNewSync(syncInst, err)
|
||||||
|
return Int64Counter{syncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapFloat64CounterInstrument converts a SyncImpl into Float64Counter.
|
||||||
|
func wrapFloat64CounterInstrument(syncInst SyncImpl, err error) (Float64Counter, error) {
|
||||||
|
common, err := checkNewSync(syncInst, err)
|
||||||
|
return Float64Counter{syncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapInt64UpDownCounterInstrument converts a SyncImpl into Int64UpDownCounter.
|
||||||
|
func wrapInt64UpDownCounterInstrument(syncInst SyncImpl, err error) (Int64UpDownCounter, error) {
|
||||||
|
common, err := checkNewSync(syncInst, err)
|
||||||
|
return Int64UpDownCounter{syncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapFloat64UpDownCounterInstrument converts a SyncImpl into Float64UpDownCounter.
|
||||||
|
func wrapFloat64UpDownCounterInstrument(syncInst SyncImpl, err error) (Float64UpDownCounter, error) {
|
||||||
|
common, err := checkNewSync(syncInst, err)
|
||||||
|
return Float64UpDownCounter{syncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapInt64ValueRecorderInstrument converts a SyncImpl into Int64ValueRecorder.
|
||||||
|
func wrapInt64ValueRecorderInstrument(syncInst SyncImpl, err error) (Int64ValueRecorder, error) {
|
||||||
|
common, err := checkNewSync(syncInst, err)
|
||||||
|
return Int64ValueRecorder{syncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapFloat64ValueRecorderInstrument converts a SyncImpl into Float64ValueRecorder.
|
||||||
|
func wrapFloat64ValueRecorderInstrument(syncInst SyncImpl, err error) (Float64ValueRecorder, error) {
|
||||||
|
common, err := checkNewSync(syncInst, err)
|
||||||
|
return Float64ValueRecorder{syncInstrument: common}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Counter is a metric that accumulates float64 values.
|
||||||
|
type Float64Counter struct {
|
||||||
|
syncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Counter is a metric that accumulates int64 values.
|
||||||
|
type Int64Counter struct {
|
||||||
|
syncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundFloat64Counter is a bound instrument for Float64Counter.
|
||||||
|
//
|
||||||
|
// It inherits the Unbind function from syncBoundInstrument.
|
||||||
|
type BoundFloat64Counter struct {
|
||||||
|
syncBoundInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundInt64Counter is a boundInstrument for Int64Counter.
|
||||||
|
//
|
||||||
|
// It inherits the Unbind function from syncBoundInstrument.
|
||||||
|
type BoundInt64Counter struct {
|
||||||
|
syncBoundInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind creates a bound instrument for this counter. The labels are
|
||||||
|
// associated with values recorded via subsequent calls to Record.
|
||||||
|
func (c Float64Counter) Bind(labels ...attribute.KeyValue) (h BoundFloat64Counter) {
|
||||||
|
h.syncBoundInstrument = c.bind(labels)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind creates a bound instrument for this counter. The labels are
|
||||||
|
// associated with values recorded via subsequent calls to Record.
|
||||||
|
func (c Int64Counter) Bind(labels ...attribute.KeyValue) (h BoundInt64Counter) {
|
||||||
|
h.syncBoundInstrument = c.bind(labels)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurement creates a Measurement object to use with batch
|
||||||
|
// recording.
|
||||||
|
func (c Float64Counter) Measurement(value float64) Measurement {
|
||||||
|
return c.float64Measurement(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurement creates a Measurement object to use with batch
|
||||||
|
// recording.
|
||||||
|
func (c Int64Counter) Measurement(value int64) Measurement {
|
||||||
|
return c.int64Measurement(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the value to the counter's sum. The labels should contain
|
||||||
|
// the keys and values to be associated with this value.
|
||||||
|
func (c Float64Counter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) {
|
||||||
|
c.directRecord(ctx, number.NewFloat64Number(value), labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the value to the counter's sum. The labels should contain
|
||||||
|
// the keys and values to be associated with this value.
|
||||||
|
func (c Int64Counter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) {
|
||||||
|
c.directRecord(ctx, number.NewInt64Number(value), labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the value to the counter's sum using the labels
|
||||||
|
// previously bound to this counter via Bind()
|
||||||
|
func (b BoundFloat64Counter) Add(ctx context.Context, value float64) {
|
||||||
|
b.directRecord(ctx, number.NewFloat64Number(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the value to the counter's sum using the labels
|
||||||
|
// previously bound to this counter via Bind()
|
||||||
|
func (b BoundInt64Counter) Add(ctx context.Context, value int64) {
|
||||||
|
b.directRecord(ctx, number.NewInt64Number(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64UpDownCounter is a metric instrument that sums floating
|
||||||
|
// point values.
|
||||||
|
type Float64UpDownCounter struct {
|
||||||
|
syncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64UpDownCounter is a metric instrument that sums integer values.
|
||||||
|
type Int64UpDownCounter struct {
|
||||||
|
syncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundFloat64UpDownCounter is a bound instrument for Float64UpDownCounter.
|
||||||
|
//
|
||||||
|
// It inherits the Unbind function from syncBoundInstrument.
|
||||||
|
type BoundFloat64UpDownCounter struct {
|
||||||
|
syncBoundInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundInt64UpDownCounter is a boundInstrument for Int64UpDownCounter.
|
||||||
|
//
|
||||||
|
// It inherits the Unbind function from syncBoundInstrument.
|
||||||
|
type BoundInt64UpDownCounter struct {
|
||||||
|
syncBoundInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind creates a bound instrument for this counter. The labels are
|
||||||
|
// associated with values recorded via subsequent calls to Record.
|
||||||
|
func (c Float64UpDownCounter) Bind(labels ...attribute.KeyValue) (h BoundFloat64UpDownCounter) {
|
||||||
|
h.syncBoundInstrument = c.bind(labels)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind creates a bound instrument for this counter. The labels are
|
||||||
|
// associated with values recorded via subsequent calls to Record.
|
||||||
|
func (c Int64UpDownCounter) Bind(labels ...attribute.KeyValue) (h BoundInt64UpDownCounter) {
|
||||||
|
h.syncBoundInstrument = c.bind(labels)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurement creates a Measurement object to use with batch
|
||||||
|
// recording.
|
||||||
|
func (c Float64UpDownCounter) Measurement(value float64) Measurement {
|
||||||
|
return c.float64Measurement(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurement creates a Measurement object to use with batch
|
||||||
|
// recording.
|
||||||
|
func (c Int64UpDownCounter) Measurement(value int64) Measurement {
|
||||||
|
return c.int64Measurement(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the value to the counter's sum. The labels should contain
|
||||||
|
// the keys and values to be associated with this value.
|
||||||
|
func (c Float64UpDownCounter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) {
|
||||||
|
c.directRecord(ctx, number.NewFloat64Number(value), labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the value to the counter's sum. The labels should contain
|
||||||
|
// the keys and values to be associated with this value.
|
||||||
|
func (c Int64UpDownCounter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) {
|
||||||
|
c.directRecord(ctx, number.NewInt64Number(value), labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the value to the counter's sum using the labels
|
||||||
|
// previously bound to this counter via Bind()
|
||||||
|
func (b BoundFloat64UpDownCounter) Add(ctx context.Context, value float64) {
|
||||||
|
b.directRecord(ctx, number.NewFloat64Number(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the value to the counter's sum using the labels
|
||||||
|
// previously bound to this counter via Bind()
|
||||||
|
func (b BoundInt64UpDownCounter) Add(ctx context.Context, value int64) {
|
||||||
|
b.directRecord(ctx, number.NewInt64Number(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64ValueRecorder is a metric that records float64 values.
|
||||||
|
type Float64ValueRecorder struct {
|
||||||
|
syncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64ValueRecorder is a metric that records int64 values.
|
||||||
|
type Int64ValueRecorder struct {
|
||||||
|
syncInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundFloat64ValueRecorder is a bound instrument for Float64ValueRecorder.
|
||||||
|
//
|
||||||
|
// It inherits the Unbind function from syncBoundInstrument.
|
||||||
|
type BoundFloat64ValueRecorder struct {
|
||||||
|
syncBoundInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundInt64ValueRecorder is a bound instrument for Int64ValueRecorder.
|
||||||
|
//
|
||||||
|
// It inherits the Unbind function from syncBoundInstrument.
|
||||||
|
type BoundInt64ValueRecorder struct {
|
||||||
|
syncBoundInstrument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind creates a bound instrument for this ValueRecorder. The labels are
|
||||||
|
// associated with values recorded via subsequent calls to Record.
|
||||||
|
func (c Float64ValueRecorder) Bind(labels ...attribute.KeyValue) (h BoundFloat64ValueRecorder) {
|
||||||
|
h.syncBoundInstrument = c.bind(labels)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind creates a bound instrument for this ValueRecorder. The labels are
|
||||||
|
// associated with values recorded via subsequent calls to Record.
|
||||||
|
func (c Int64ValueRecorder) Bind(labels ...attribute.KeyValue) (h BoundInt64ValueRecorder) {
|
||||||
|
h.syncBoundInstrument = c.bind(labels)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurement creates a Measurement object to use with batch
|
||||||
|
// recording.
|
||||||
|
func (c Float64ValueRecorder) Measurement(value float64) Measurement {
|
||||||
|
return c.float64Measurement(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurement creates a Measurement object to use with batch
|
||||||
|
// recording.
|
||||||
|
func (c Int64ValueRecorder) Measurement(value int64) Measurement {
|
||||||
|
return c.int64Measurement(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record adds a new value to the list of ValueRecorder's records. The
|
||||||
|
// labels should contain the keys and values to be associated with
|
||||||
|
// this value.
|
||||||
|
func (c Float64ValueRecorder) Record(ctx context.Context, value float64, labels ...attribute.KeyValue) {
|
||||||
|
c.directRecord(ctx, number.NewFloat64Number(value), labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record adds a new value to the ValueRecorder's distribution. The
|
||||||
|
// labels should contain the keys and values to be associated with
|
||||||
|
// this value.
|
||||||
|
func (c Int64ValueRecorder) Record(ctx context.Context, value int64, labels ...attribute.KeyValue) {
|
||||||
|
c.directRecord(ctx, number.NewInt64Number(value), labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record adds a new value to the ValueRecorder's distribution using the labels
|
||||||
|
// previously bound to the ValueRecorder via Bind().
|
||||||
|
func (b BoundFloat64ValueRecorder) Record(ctx context.Context, value float64) {
|
||||||
|
b.directRecord(ctx, number.NewFloat64Number(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record adds a new value to the ValueRecorder's distribution using the labels
|
||||||
|
// previously bound to the ValueRecorder via Bind().
|
||||||
|
func (b BoundInt64ValueRecorder) Record(ctx context.Context, value int64) {
|
||||||
|
b.directRecord(ctx, number.NewInt64Number(value))
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// 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 metric // import "go.opentelemetry.io/otel/metric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric/number"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NoopMeterProvider struct{}
|
||||||
|
|
||||||
|
type noopInstrument struct{}
|
||||||
|
type noopBoundInstrument struct{}
|
||||||
|
type NoopSync struct{ noopInstrument }
|
||||||
|
type NoopAsync struct{ noopInstrument }
|
||||||
|
|
||||||
|
var _ MeterProvider = NoopMeterProvider{}
|
||||||
|
var _ SyncImpl = NoopSync{}
|
||||||
|
var _ BoundSyncImpl = noopBoundInstrument{}
|
||||||
|
var _ AsyncImpl = NoopAsync{}
|
||||||
|
|
||||||
|
func (NoopMeterProvider) Meter(_ string, _ ...MeterOption) Meter {
|
||||||
|
return Meter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (noopInstrument) Implementation() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (noopInstrument) Descriptor() Descriptor {
|
||||||
|
return Descriptor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (noopBoundInstrument) RecordOne(context.Context, number.Number) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (noopBoundInstrument) Unbind() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NoopSync) Bind([]attribute.KeyValue) BoundSyncImpl {
|
||||||
|
return noopBoundInstrument{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NoopSync) RecordOne(context.Context, number.Number, []attribute.KeyValue) {
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
// 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 metric // import "go.opentelemetry.io/otel/metric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric/number"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MeterImpl is the interface an SDK must implement to supply a Meter
|
||||||
|
// implementation.
|
||||||
|
type MeterImpl interface {
|
||||||
|
// RecordBatch atomically records a batch of measurements.
|
||||||
|
RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurement ...Measurement)
|
||||||
|
|
||||||
|
// NewSyncInstrument returns a newly constructed
|
||||||
|
// synchronous instrument implementation or an error, should
|
||||||
|
// one occur.
|
||||||
|
NewSyncInstrument(descriptor Descriptor) (SyncImpl, error)
|
||||||
|
|
||||||
|
// NewAsyncInstrument returns a newly constructed
|
||||||
|
// asynchronous instrument implementation or an error, should
|
||||||
|
// one occur.
|
||||||
|
NewAsyncInstrument(
|
||||||
|
descriptor Descriptor,
|
||||||
|
runner AsyncRunner,
|
||||||
|
) (AsyncImpl, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentImpl is a common interface for synchronous and
|
||||||
|
// asynchronous instruments.
|
||||||
|
type InstrumentImpl interface {
|
||||||
|
// Implementation returns the underlying implementation of the
|
||||||
|
// instrument, which allows the implementation to gain access
|
||||||
|
// to its own representation especially from a `Measurement`.
|
||||||
|
Implementation() interface{}
|
||||||
|
|
||||||
|
// Descriptor returns a copy of the instrument's Descriptor.
|
||||||
|
Descriptor() Descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncImpl is the implementation-level interface to a generic
|
||||||
|
// synchronous instrument (e.g., ValueRecorder and Counter instruments).
|
||||||
|
type SyncImpl interface {
|
||||||
|
InstrumentImpl
|
||||||
|
|
||||||
|
// Bind creates an implementation-level bound instrument,
|
||||||
|
// binding a label set with this instrument implementation.
|
||||||
|
Bind(labels []attribute.KeyValue) BoundSyncImpl
|
||||||
|
|
||||||
|
// RecordOne captures a single synchronous metric event.
|
||||||
|
RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundSyncImpl is the implementation-level interface to a
|
||||||
|
// generic bound synchronous instrument
|
||||||
|
type BoundSyncImpl interface {
|
||||||
|
|
||||||
|
// RecordOne captures a single synchronous metric event.
|
||||||
|
RecordOne(ctx context.Context, number number.Number)
|
||||||
|
|
||||||
|
// Unbind frees the resources associated with this bound instrument. It
|
||||||
|
// does not affect the metric this bound instrument was created through.
|
||||||
|
Unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncImpl is an implementation-level interface to an
|
||||||
|
// asynchronous instrument (e.g., Observer instruments).
|
||||||
|
type AsyncImpl interface {
|
||||||
|
InstrumentImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapMeterImpl constructs a `Meter` implementation from a
|
||||||
|
// `MeterImpl` implementation.
|
||||||
|
func WrapMeterImpl(impl MeterImpl, instrumentationName string, opts ...MeterOption) Meter {
|
||||||
|
return Meter{
|
||||||
|
impl: impl,
|
||||||
|
name: instrumentationName,
|
||||||
|
version: NewMeterConfig(opts...).InstrumentationVersion(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// 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 number provides a number abstraction for instruments that
|
||||||
|
either support int64 or float64 input values.
|
||||||
|
|
||||||
|
This package is currently in a pre-GA phase. Backwards incompatible changes
|
||||||
|
may be introduced in subsequent minor version releases as we work to track the
|
||||||
|
evolving OpenTelemetry specification and user feedback.
|
||||||
|
*/
|
||||||
|
package number // import "go.opentelemetry.io/otel/metric/number"
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Code generated by "stringer -type=Kind"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package number
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Int64Kind-0]
|
||||||
|
_ = x[Float64Kind-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Kind_name = "Int64KindFloat64Kind"
|
||||||
|
|
||||||
|
var _Kind_index = [...]uint8{0, 9, 20}
|
||||||
|
|
||||||
|
func (i Kind) String() string {
|
||||||
|
if i < 0 || i >= Kind(len(_Kind_index)-1) {
|
||||||
|
return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
|
||||||
|
}
|
|
@ -0,0 +1,538 @@
|
||||||
|
// 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 number // import "go.opentelemetry.io/otel/metric/number"
|
||||||
|
|
||||||
|
//go:generate stringer -type=Kind
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Kind describes the data type of the Number.
|
||||||
|
type Kind int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Int64Kind means that the Number stores int64.
|
||||||
|
Int64Kind Kind = iota
|
||||||
|
// Float64Kind means that the Number stores float64.
|
||||||
|
Float64Kind
|
||||||
|
)
|
||||||
|
|
||||||
|
// Zero returns a zero value for a given Kind
|
||||||
|
func (k Kind) Zero() Number {
|
||||||
|
switch k {
|
||||||
|
case Int64Kind:
|
||||||
|
return NewInt64Number(0)
|
||||||
|
case Float64Kind:
|
||||||
|
return NewFloat64Number(0.)
|
||||||
|
default:
|
||||||
|
return Number(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum returns the minimum representable value
|
||||||
|
// for a given Kind
|
||||||
|
func (k Kind) Minimum() Number {
|
||||||
|
switch k {
|
||||||
|
case Int64Kind:
|
||||||
|
return NewInt64Number(math.MinInt64)
|
||||||
|
case Float64Kind:
|
||||||
|
return NewFloat64Number(-1. * math.MaxFloat64)
|
||||||
|
default:
|
||||||
|
return Number(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum returns the maximum representable value
|
||||||
|
// for a given Kind
|
||||||
|
func (k Kind) Maximum() Number {
|
||||||
|
switch k {
|
||||||
|
case Int64Kind:
|
||||||
|
return NewInt64Number(math.MaxInt64)
|
||||||
|
case Float64Kind:
|
||||||
|
return NewFloat64Number(math.MaxFloat64)
|
||||||
|
default:
|
||||||
|
return Number(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number represents either an integral or a floating point value. It
|
||||||
|
// needs to be accompanied with a source of Kind that describes
|
||||||
|
// the actual type of the value stored within Number.
|
||||||
|
type Number uint64
|
||||||
|
|
||||||
|
// - constructors
|
||||||
|
|
||||||
|
// NewNumberFromRaw creates a new Number from a raw value.
|
||||||
|
func NewNumberFromRaw(r uint64) Number {
|
||||||
|
return Number(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64Number creates an integral Number.
|
||||||
|
func NewInt64Number(i int64) Number {
|
||||||
|
return NewNumberFromRaw(internal.Int64ToRaw(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64Number creates a floating point Number.
|
||||||
|
func NewFloat64Number(f float64) Number {
|
||||||
|
return NewNumberFromRaw(internal.Float64ToRaw(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNumberSignChange returns a number with the same magnitude and
|
||||||
|
// the opposite sign. `kind` must describe the kind of number in `nn`.
|
||||||
|
func NewNumberSignChange(kind Kind, nn Number) Number {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
return NewInt64Number(-nn.AsInt64())
|
||||||
|
case Float64Kind:
|
||||||
|
return NewFloat64Number(-nn.AsFloat64())
|
||||||
|
}
|
||||||
|
return nn
|
||||||
|
}
|
||||||
|
|
||||||
|
// - as x
|
||||||
|
|
||||||
|
// AsNumber gets the Number.
|
||||||
|
func (n *Number) AsNumber() Number {
|
||||||
|
return *n
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsRaw gets the uninterpreted raw value. Might be useful for some
|
||||||
|
// atomic operations.
|
||||||
|
func (n *Number) AsRaw() uint64 {
|
||||||
|
return uint64(*n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInt64 assumes that the value contains an int64 and returns it as
|
||||||
|
// such.
|
||||||
|
func (n *Number) AsInt64() int64 {
|
||||||
|
return internal.RawToInt64(n.AsRaw())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsFloat64 assumes that the measurement value contains a float64 and
|
||||||
|
// returns it as such.
|
||||||
|
func (n *Number) AsFloat64() float64 {
|
||||||
|
return internal.RawToFloat64(n.AsRaw())
|
||||||
|
}
|
||||||
|
|
||||||
|
// - as x atomic
|
||||||
|
|
||||||
|
// AsNumberAtomic gets the Number atomically.
|
||||||
|
func (n *Number) AsNumberAtomic() Number {
|
||||||
|
return NewNumberFromRaw(n.AsRawAtomic())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsRawAtomic gets the uninterpreted raw value atomically. Might be
|
||||||
|
// useful for some atomic operations.
|
||||||
|
func (n *Number) AsRawAtomic() uint64 {
|
||||||
|
return atomic.LoadUint64(n.AsRawPtr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInt64Atomic assumes that the number contains an int64 and returns
|
||||||
|
// it as such atomically.
|
||||||
|
func (n *Number) AsInt64Atomic() int64 {
|
||||||
|
return atomic.LoadInt64(n.AsInt64Ptr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsFloat64Atomic assumes that the measurement value contains a
|
||||||
|
// float64 and returns it as such atomically.
|
||||||
|
func (n *Number) AsFloat64Atomic() float64 {
|
||||||
|
return internal.RawToFloat64(n.AsRawAtomic())
|
||||||
|
}
|
||||||
|
|
||||||
|
// - as x ptr
|
||||||
|
|
||||||
|
// AsRawPtr gets the pointer to the raw, uninterpreted raw
|
||||||
|
// value. Might be useful for some atomic operations.
|
||||||
|
func (n *Number) AsRawPtr() *uint64 {
|
||||||
|
return (*uint64)(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInt64Ptr assumes that the number contains an int64 and returns a
|
||||||
|
// pointer to it.
|
||||||
|
func (n *Number) AsInt64Ptr() *int64 {
|
||||||
|
return internal.RawPtrToInt64Ptr(n.AsRawPtr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsFloat64Ptr assumes that the number contains a float64 and returns a
|
||||||
|
// pointer to it.
|
||||||
|
func (n *Number) AsFloat64Ptr() *float64 {
|
||||||
|
return internal.RawPtrToFloat64Ptr(n.AsRawPtr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// - coerce
|
||||||
|
|
||||||
|
// CoerceToInt64 casts the number to int64. May result in
|
||||||
|
// data/precision loss.
|
||||||
|
func (n *Number) CoerceToInt64(kind Kind) int64 {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
return n.AsInt64()
|
||||||
|
case Float64Kind:
|
||||||
|
return int64(n.AsFloat64())
|
||||||
|
default:
|
||||||
|
// you get what you deserve
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CoerceToFloat64 casts the number to float64. May result in
|
||||||
|
// data/precision loss.
|
||||||
|
func (n *Number) CoerceToFloat64(kind Kind) float64 {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
return float64(n.AsInt64())
|
||||||
|
case Float64Kind:
|
||||||
|
return n.AsFloat64()
|
||||||
|
default:
|
||||||
|
// you get what you deserve
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - set
|
||||||
|
|
||||||
|
// SetNumber sets the number to the passed number. Both should be of
|
||||||
|
// the same kind.
|
||||||
|
func (n *Number) SetNumber(nn Number) {
|
||||||
|
*n.AsRawPtr() = nn.AsRaw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRaw sets the number to the passed raw value. Both number and the
|
||||||
|
// raw number should represent the same kind.
|
||||||
|
func (n *Number) SetRaw(r uint64) {
|
||||||
|
*n.AsRawPtr() = r
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInt64 assumes that the number contains an int64 and sets it to
|
||||||
|
// the passed value.
|
||||||
|
func (n *Number) SetInt64(i int64) {
|
||||||
|
*n.AsInt64Ptr() = i
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFloat64 assumes that the number contains a float64 and sets it
|
||||||
|
// to the passed value.
|
||||||
|
func (n *Number) SetFloat64(f float64) {
|
||||||
|
*n.AsFloat64Ptr() = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// - set atomic
|
||||||
|
|
||||||
|
// SetNumberAtomic sets the number to the passed number
|
||||||
|
// atomically. Both should be of the same kind.
|
||||||
|
func (n *Number) SetNumberAtomic(nn Number) {
|
||||||
|
atomic.StoreUint64(n.AsRawPtr(), nn.AsRaw())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawAtomic sets the number to the passed raw value
|
||||||
|
// atomically. Both number and the raw number should represent the
|
||||||
|
// same kind.
|
||||||
|
func (n *Number) SetRawAtomic(r uint64) {
|
||||||
|
atomic.StoreUint64(n.AsRawPtr(), r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInt64Atomic assumes that the number contains an int64 and sets
|
||||||
|
// it to the passed value atomically.
|
||||||
|
func (n *Number) SetInt64Atomic(i int64) {
|
||||||
|
atomic.StoreInt64(n.AsInt64Ptr(), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFloat64Atomic assumes that the number contains a float64 and
|
||||||
|
// sets it to the passed value atomically.
|
||||||
|
func (n *Number) SetFloat64Atomic(f float64) {
|
||||||
|
atomic.StoreUint64(n.AsRawPtr(), internal.Float64ToRaw(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// - swap
|
||||||
|
|
||||||
|
// SwapNumber sets the number to the passed number and returns the old
|
||||||
|
// number. Both this number and the passed number should be of the
|
||||||
|
// same kind.
|
||||||
|
func (n *Number) SwapNumber(nn Number) Number {
|
||||||
|
old := *n
|
||||||
|
n.SetNumber(nn)
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapRaw sets the number to the passed raw value and returns the old
|
||||||
|
// raw value. Both number and the raw number should represent the same
|
||||||
|
// kind.
|
||||||
|
func (n *Number) SwapRaw(r uint64) uint64 {
|
||||||
|
old := n.AsRaw()
|
||||||
|
n.SetRaw(r)
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapInt64 assumes that the number contains an int64, sets it to the
|
||||||
|
// passed value and returns the old int64 value.
|
||||||
|
func (n *Number) SwapInt64(i int64) int64 {
|
||||||
|
old := n.AsInt64()
|
||||||
|
n.SetInt64(i)
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapFloat64 assumes that the number contains an float64, sets it to
|
||||||
|
// the passed value and returns the old float64 value.
|
||||||
|
func (n *Number) SwapFloat64(f float64) float64 {
|
||||||
|
old := n.AsFloat64()
|
||||||
|
n.SetFloat64(f)
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
// - swap atomic
|
||||||
|
|
||||||
|
// SwapNumberAtomic sets the number to the passed number and returns
|
||||||
|
// the old number atomically. Both this number and the passed number
|
||||||
|
// should be of the same kind.
|
||||||
|
func (n *Number) SwapNumberAtomic(nn Number) Number {
|
||||||
|
return NewNumberFromRaw(atomic.SwapUint64(n.AsRawPtr(), nn.AsRaw()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapRawAtomic sets the number to the passed raw value and returns
|
||||||
|
// the old raw value atomically. Both number and the raw number should
|
||||||
|
// represent the same kind.
|
||||||
|
func (n *Number) SwapRawAtomic(r uint64) uint64 {
|
||||||
|
return atomic.SwapUint64(n.AsRawPtr(), r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapInt64Atomic assumes that the number contains an int64, sets it
|
||||||
|
// to the passed value and returns the old int64 value atomically.
|
||||||
|
func (n *Number) SwapInt64Atomic(i int64) int64 {
|
||||||
|
return atomic.SwapInt64(n.AsInt64Ptr(), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapFloat64Atomic assumes that the number contains an float64, sets
|
||||||
|
// it to the passed value and returns the old float64 value
|
||||||
|
// atomically.
|
||||||
|
func (n *Number) SwapFloat64Atomic(f float64) float64 {
|
||||||
|
return internal.RawToFloat64(atomic.SwapUint64(n.AsRawPtr(), internal.Float64ToRaw(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// - add
|
||||||
|
|
||||||
|
// AddNumber assumes that this and the passed number are of the passed
|
||||||
|
// kind and adds the passed number to this number.
|
||||||
|
func (n *Number) AddNumber(kind Kind, nn Number) {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
n.AddInt64(nn.AsInt64())
|
||||||
|
case Float64Kind:
|
||||||
|
n.AddFloat64(nn.AsFloat64())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRaw assumes that this number and the passed raw value are of the
|
||||||
|
// passed kind and adds the passed raw value to this number.
|
||||||
|
func (n *Number) AddRaw(kind Kind, r uint64) {
|
||||||
|
n.AddNumber(kind, NewNumberFromRaw(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInt64 assumes that the number contains an int64 and adds the
|
||||||
|
// passed int64 to it.
|
||||||
|
func (n *Number) AddInt64(i int64) {
|
||||||
|
*n.AsInt64Ptr() += i
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFloat64 assumes that the number contains a float64 and adds the
|
||||||
|
// passed float64 to it.
|
||||||
|
func (n *Number) AddFloat64(f float64) {
|
||||||
|
*n.AsFloat64Ptr() += f
|
||||||
|
}
|
||||||
|
|
||||||
|
// - add atomic
|
||||||
|
|
||||||
|
// AddNumberAtomic assumes that this and the passed number are of the
|
||||||
|
// passed kind and adds the passed number to this number atomically.
|
||||||
|
func (n *Number) AddNumberAtomic(kind Kind, nn Number) {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
n.AddInt64Atomic(nn.AsInt64())
|
||||||
|
case Float64Kind:
|
||||||
|
n.AddFloat64Atomic(nn.AsFloat64())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRawAtomic assumes that this number and the passed raw value are
|
||||||
|
// of the passed kind and adds the passed raw value to this number
|
||||||
|
// atomically.
|
||||||
|
func (n *Number) AddRawAtomic(kind Kind, r uint64) {
|
||||||
|
n.AddNumberAtomic(kind, NewNumberFromRaw(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInt64Atomic assumes that the number contains an int64 and adds
|
||||||
|
// the passed int64 to it atomically.
|
||||||
|
func (n *Number) AddInt64Atomic(i int64) {
|
||||||
|
atomic.AddInt64(n.AsInt64Ptr(), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFloat64Atomic assumes that the number contains a float64 and
|
||||||
|
// adds the passed float64 to it atomically.
|
||||||
|
func (n *Number) AddFloat64Atomic(f float64) {
|
||||||
|
for {
|
||||||
|
o := n.AsFloat64Atomic()
|
||||||
|
if n.CompareAndSwapFloat64(o, o+f) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - compare and swap (atomic only)
|
||||||
|
|
||||||
|
// CompareAndSwapNumber does the atomic CAS operation on this
|
||||||
|
// number. This number and passed old and new numbers should be of the
|
||||||
|
// same kind.
|
||||||
|
func (n *Number) CompareAndSwapNumber(on, nn Number) bool {
|
||||||
|
return atomic.CompareAndSwapUint64(n.AsRawPtr(), on.AsRaw(), nn.AsRaw())
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwapRaw does the atomic CAS operation on this
|
||||||
|
// number. This number and passed old and new raw values should be of
|
||||||
|
// the same kind.
|
||||||
|
func (n *Number) CompareAndSwapRaw(or, nr uint64) bool {
|
||||||
|
return atomic.CompareAndSwapUint64(n.AsRawPtr(), or, nr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwapInt64 assumes that this number contains an int64 and
|
||||||
|
// does the atomic CAS operation on it.
|
||||||
|
func (n *Number) CompareAndSwapInt64(oi, ni int64) bool {
|
||||||
|
return atomic.CompareAndSwapInt64(n.AsInt64Ptr(), oi, ni)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwapFloat64 assumes that this number contains a float64 and
|
||||||
|
// does the atomic CAS operation on it.
|
||||||
|
func (n *Number) CompareAndSwapFloat64(of, nf float64) bool {
|
||||||
|
return atomic.CompareAndSwapUint64(n.AsRawPtr(), internal.Float64ToRaw(of), internal.Float64ToRaw(nf))
|
||||||
|
}
|
||||||
|
|
||||||
|
// - compare
|
||||||
|
|
||||||
|
// CompareNumber compares two Numbers given their kind. Both numbers
|
||||||
|
// should have the same kind. This returns:
|
||||||
|
// 0 if the numbers are equal
|
||||||
|
// -1 if the subject `n` is less than the argument `nn`
|
||||||
|
// +1 if the subject `n` is greater than the argument `nn`
|
||||||
|
func (n *Number) CompareNumber(kind Kind, nn Number) int {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
return n.CompareInt64(nn.AsInt64())
|
||||||
|
case Float64Kind:
|
||||||
|
return n.CompareFloat64(nn.AsFloat64())
|
||||||
|
default:
|
||||||
|
// you get what you deserve
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareRaw compares two numbers, where one is input as a raw
|
||||||
|
// uint64, interpreting both values as a `kind` of number.
|
||||||
|
func (n *Number) CompareRaw(kind Kind, r uint64) int {
|
||||||
|
return n.CompareNumber(kind, NewNumberFromRaw(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareInt64 assumes that the Number contains an int64 and performs
|
||||||
|
// a comparison between the value and the other value. It returns the
|
||||||
|
// typical result of the compare function: -1 if the value is less
|
||||||
|
// than the other, 0 if both are equal, 1 if the value is greater than
|
||||||
|
// the other.
|
||||||
|
func (n *Number) CompareInt64(i int64) int {
|
||||||
|
this := n.AsInt64()
|
||||||
|
if this < i {
|
||||||
|
return -1
|
||||||
|
} else if this > i {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareFloat64 assumes that the Number contains a float64 and
|
||||||
|
// performs a comparison between the value and the other value. It
|
||||||
|
// returns the typical result of the compare function: -1 if the value
|
||||||
|
// is less than the other, 0 if both are equal, 1 if the value is
|
||||||
|
// greater than the other.
|
||||||
|
//
|
||||||
|
// Do not compare NaN values.
|
||||||
|
func (n *Number) CompareFloat64(f float64) int {
|
||||||
|
this := n.AsFloat64()
|
||||||
|
if this < f {
|
||||||
|
return -1
|
||||||
|
} else if this > f {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// - relations to zero
|
||||||
|
|
||||||
|
// IsPositive returns true if the actual value is greater than zero.
|
||||||
|
func (n *Number) IsPositive(kind Kind) bool {
|
||||||
|
return n.compareWithZero(kind) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNegative returns true if the actual value is less than zero.
|
||||||
|
func (n *Number) IsNegative(kind Kind) bool {
|
||||||
|
return n.compareWithZero(kind) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns true if the actual value is equal to zero.
|
||||||
|
func (n *Number) IsZero(kind Kind) bool {
|
||||||
|
return n.compareWithZero(kind) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// - misc
|
||||||
|
|
||||||
|
// Emit returns a string representation of the raw value of the
|
||||||
|
// Number. A %d is used for integral values, %f for floating point
|
||||||
|
// values.
|
||||||
|
func (n *Number) Emit(kind Kind) string {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
return fmt.Sprintf("%d", n.AsInt64())
|
||||||
|
case Float64Kind:
|
||||||
|
return fmt.Sprintf("%f", n.AsFloat64())
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInterface returns the number as an interface{}, typically used
|
||||||
|
// for Kind-correct JSON conversion.
|
||||||
|
func (n *Number) AsInterface(kind Kind) interface{} {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
return n.AsInt64()
|
||||||
|
case Float64Kind:
|
||||||
|
return n.AsFloat64()
|
||||||
|
default:
|
||||||
|
return math.NaN()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - private stuff
|
||||||
|
|
||||||
|
func (n *Number) compareWithZero(kind Kind) int {
|
||||||
|
switch kind {
|
||||||
|
case Int64Kind:
|
||||||
|
return n.CompareInt64(0)
|
||||||
|
case Float64Kind:
|
||||||
|
return n.CompareFloat64(0.)
|
||||||
|
default:
|
||||||
|
// you get what you deserve
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// 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 registry provides a non-standalone implementation of
|
||||||
|
MeterProvider that adds uniqueness checking for instrument descriptors
|
||||||
|
on top of other MeterProvider it wraps.
|
||||||
|
|
||||||
|
This package is currently in a pre-GA phase. Backwards incompatible changes
|
||||||
|
may be introduced in subsequent minor version releases as we work to track the
|
||||||
|
evolving OpenTelemetry specification and user feedback.
|
||||||
|
*/
|
||||||
|
package registry // import "go.opentelemetry.io/otel/metric/registry"
|
|
@ -0,0 +1,170 @@
|
||||||
|
// 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 registry // import "go.opentelemetry.io/otel/metric/registry"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MeterProvider is a standard MeterProvider for wrapping `MeterImpl`
|
||||||
|
type MeterProvider struct {
|
||||||
|
impl metric.MeterImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ metric.MeterProvider = (*MeterProvider)(nil)
|
||||||
|
|
||||||
|
// uniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding
|
||||||
|
// uniqueness checking for instrument descriptors. Use NewUniqueInstrumentMeter
|
||||||
|
// to wrap an implementation with uniqueness checking.
|
||||||
|
type uniqueInstrumentMeterImpl struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
impl metric.MeterImpl
|
||||||
|
state map[key]metric.InstrumentImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ metric.MeterImpl = (*uniqueInstrumentMeterImpl)(nil)
|
||||||
|
|
||||||
|
type key struct {
|
||||||
|
instrumentName string
|
||||||
|
instrumentationName string
|
||||||
|
InstrumentationVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMeterProvider returns a new provider that implements instrument
|
||||||
|
// name-uniqueness checking.
|
||||||
|
func NewMeterProvider(impl metric.MeterImpl) *MeterProvider {
|
||||||
|
return &MeterProvider{
|
||||||
|
impl: NewUniqueInstrumentMeterImpl(impl),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meter implements MeterProvider.
|
||||||
|
func (p *MeterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
|
||||||
|
return metric.WrapMeterImpl(p.impl, instrumentationName, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMetricKindMismatch is the standard error for mismatched metric
|
||||||
|
// instrument definitions.
|
||||||
|
var ErrMetricKindMismatch = fmt.Errorf(
|
||||||
|
"a metric was already registered by this name with another kind or number type")
|
||||||
|
|
||||||
|
// NewUniqueInstrumentMeterImpl returns a wrapped metric.MeterImpl with
|
||||||
|
// the addition of uniqueness checking.
|
||||||
|
func NewUniqueInstrumentMeterImpl(impl metric.MeterImpl) metric.MeterImpl {
|
||||||
|
return &uniqueInstrumentMeterImpl{
|
||||||
|
impl: impl,
|
||||||
|
state: map[key]metric.InstrumentImpl{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordBatch implements metric.MeterImpl.
|
||||||
|
func (u *uniqueInstrumentMeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, ms ...metric.Measurement) {
|
||||||
|
u.impl.RecordBatch(ctx, labels, ms...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyOf(descriptor metric.Descriptor) key {
|
||||||
|
return key{
|
||||||
|
descriptor.Name(),
|
||||||
|
descriptor.InstrumentationName(),
|
||||||
|
descriptor.InstrumentationVersion(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetricKindMismatchError formats an error that describes a
|
||||||
|
// mismatched metric instrument definition.
|
||||||
|
func NewMetricKindMismatchError(desc metric.Descriptor) error {
|
||||||
|
return fmt.Errorf("metric was %s (%s %s)registered as a %s %s: %w",
|
||||||
|
desc.Name(),
|
||||||
|
desc.InstrumentationName(),
|
||||||
|
desc.InstrumentationVersion(),
|
||||||
|
desc.NumberKind(),
|
||||||
|
desc.InstrumentKind(),
|
||||||
|
ErrMetricKindMismatch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compatible determines whether two metric.Descriptors are considered
|
||||||
|
// the same for the purpose of uniqueness checking.
|
||||||
|
func Compatible(candidate, existing metric.Descriptor) bool {
|
||||||
|
return candidate.InstrumentKind() == existing.InstrumentKind() &&
|
||||||
|
candidate.NumberKind() == existing.NumberKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkUniqueness returns an ErrMetricKindMismatch error if there is
|
||||||
|
// a conflict between a descriptor that was already registered and the
|
||||||
|
// `descriptor` argument. If there is an existing compatible
|
||||||
|
// registration, this returns the already-registered instrument. If
|
||||||
|
// there is no conflict and no prior registration, returns (nil, nil).
|
||||||
|
func (u *uniqueInstrumentMeterImpl) checkUniqueness(descriptor metric.Descriptor) (metric.InstrumentImpl, error) {
|
||||||
|
impl, ok := u.state[keyOf(descriptor)]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Compatible(descriptor, impl.Descriptor()) {
|
||||||
|
return nil, NewMetricKindMismatchError(impl.Descriptor())
|
||||||
|
}
|
||||||
|
|
||||||
|
return impl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSyncInstrument implements metric.MeterImpl.
|
||||||
|
func (u *uniqueInstrumentMeterImpl) NewSyncInstrument(descriptor metric.Descriptor) (metric.SyncImpl, error) {
|
||||||
|
u.lock.Lock()
|
||||||
|
defer u.lock.Unlock()
|
||||||
|
|
||||||
|
impl, err := u.checkUniqueness(descriptor)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if impl != nil {
|
||||||
|
return impl.(metric.SyncImpl), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
syncInst, err := u.impl.NewSyncInstrument(descriptor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u.state[keyOf(descriptor)] = syncInst
|
||||||
|
return syncInst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAsyncInstrument implements metric.MeterImpl.
|
||||||
|
func (u *uniqueInstrumentMeterImpl) NewAsyncInstrument(
|
||||||
|
descriptor metric.Descriptor,
|
||||||
|
runner metric.AsyncRunner,
|
||||||
|
) (metric.AsyncImpl, error) {
|
||||||
|
u.lock.Lock()
|
||||||
|
defer u.lock.Unlock()
|
||||||
|
|
||||||
|
impl, err := u.checkUniqueness(descriptor)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if impl != nil {
|
||||||
|
return impl.(metric.AsyncImpl), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncInst, err := u.impl.NewAsyncInstrument(descriptor, runner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u.state[keyOf(descriptor)] = asyncInst
|
||||||
|
return asyncInst, nil
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// 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 unit provides units.
|
||||||
|
//
|
||||||
|
// This package is currently in a pre-GA phase. Backwards incompatible changes
|
||||||
|
// may be introduced in subsequent minor version releases as we work to track
|
||||||
|
// the evolving OpenTelemetry specification and user feedback.
|
||||||
|
package unit // import "go.opentelemetry.io/otel/metric/unit"
|
|
@ -0,0 +1,24 @@
|
||||||
|
// 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 unit // import "go.opentelemetry.io/otel/metric/unit"
|
||||||
|
|
||||||
|
type Unit string
|
||||||
|
|
||||||
|
// Units defined by OpenTelemetry.
|
||||||
|
const (
|
||||||
|
Dimensionless Unit = "1"
|
||||||
|
Bytes Unit = "By"
|
||||||
|
Milliseconds Unit = "ms"
|
||||||
|
)
|
|
@ -239,6 +239,8 @@ github.com/docker/go-events
|
||||||
github.com/docker/go-metrics
|
github.com/docker/go-metrics
|
||||||
# github.com/docker/go-units v0.4.0
|
# github.com/docker/go-units v0.4.0
|
||||||
github.com/docker/go-units
|
github.com/docker/go-units
|
||||||
|
# github.com/felixge/httpsnoop v1.0.2
|
||||||
|
github.com/felixge/httpsnoop
|
||||||
# github.com/gofrs/flock v0.7.3
|
# github.com/gofrs/flock v0.7.3
|
||||||
github.com/gofrs/flock
|
github.com/gofrs/flock
|
||||||
# github.com/gogo/googleapis v1.4.0
|
# github.com/gogo/googleapis v1.4.0
|
||||||
|
@ -385,8 +387,12 @@ go.opencensus.io/trace/internal
|
||||||
go.opencensus.io/trace/tracestate
|
go.opencensus.io/trace/tracestate
|
||||||
# go.opentelemetry.io/contrib v0.21.0
|
# go.opentelemetry.io/contrib v0.21.0
|
||||||
go.opentelemetry.io/contrib
|
go.opentelemetry.io/contrib
|
||||||
# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.21.0
|
# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.21.0 => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939
|
||||||
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/net/http/httptrace/otelhttptrace v0.21.0 => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20210714055410-d010b05b4939
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace
|
||||||
|
# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.21.0 => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/otelhttp v0.0.0-20210714055410-d010b05b4939
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
|
||||||
# go.opentelemetry.io/otel v1.0.0-RC1
|
# go.opentelemetry.io/otel v1.0.0-RC1
|
||||||
go.opentelemetry.io/otel
|
go.opentelemetry.io/otel
|
||||||
go.opentelemetry.io/otel/attribute
|
go.opentelemetry.io/otel/attribute
|
||||||
|
@ -412,6 +418,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
|
||||||
# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.0.0-RC1
|
# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.0.0-RC1
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
|
||||||
|
# go.opentelemetry.io/otel/internal/metric v0.21.0
|
||||||
|
go.opentelemetry.io/otel/internal/metric/global
|
||||||
|
# go.opentelemetry.io/otel/metric v0.21.0
|
||||||
|
go.opentelemetry.io/otel/metric
|
||||||
|
go.opentelemetry.io/otel/metric/global
|
||||||
|
go.opentelemetry.io/otel/metric/number
|
||||||
|
go.opentelemetry.io/otel/metric/registry
|
||||||
|
go.opentelemetry.io/otel/metric/unit
|
||||||
# go.opentelemetry.io/otel/sdk v1.0.0-RC1
|
# go.opentelemetry.io/otel/sdk v1.0.0-RC1
|
||||||
go.opentelemetry.io/otel/sdk/instrumentation
|
go.opentelemetry.io/otel/sdk/instrumentation
|
||||||
go.opentelemetry.io/otel/sdk/internal
|
go.opentelemetry.io/otel/sdk/internal
|
||||||
|
|
Loading…
Reference in New Issue