Merged in feature/s3integration (pull request #47)
Set Up S3 integration * cfginterfaceandfirsts3funcs * addlocalstack * generallypassesfullsuite * addedmultipleattemptedpings * cleanup * stabiliseplusskip
This commit is contained in:
+3
-3
@@ -18,7 +18,7 @@ var DefaultClient = &http.Client{Transport: NewTransport(http.DefaultTransport)}
|
||||
|
||||
// Get is a convenient replacement for http.Get that adds a span around the request.
|
||||
func Get(ctx context.Context, targetURL string) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", targetURL, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func Get(ctx context.Context, targetURL string) (resp *http.Response, err error)
|
||||
|
||||
// Head is a convenient replacement for http.Head that adds a span around the request.
|
||||
func Head(ctx context.Context, targetURL string) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "HEAD", targetURL, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodHead, targetURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func Head(ctx context.Context, targetURL string) (resp *http.Response, err error
|
||||
|
||||
// Post is a convenient replacement for http.Post that adds a span around the request.
|
||||
func Post(ctx context.Context, targetURL, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", targetURL, body)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, targetURL, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
-7
@@ -18,13 +18,6 @@ const (
|
||||
WriteErrorKey = attribute.Key("http.write_error") // if an error occurred while writing a reply, the string of the error (io.EOF is not recorded)
|
||||
)
|
||||
|
||||
// Client HTTP metrics.
|
||||
const (
|
||||
clientRequestSize = "http.client.request.size" // Outgoing request bytes total
|
||||
clientResponseSize = "http.client.response.size" // Outgoing response bytes total
|
||||
clientDuration = "http.client.duration" // Outgoing end to end duration, milliseconds
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
+37
-21
@@ -12,6 +12,7 @@ import (
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
@@ -21,15 +22,16 @@ type middleware struct {
|
||||
operation string
|
||||
server string
|
||||
|
||||
tracer trace.Tracer
|
||||
propagators propagation.TextMapPropagator
|
||||
spanStartOptions []trace.SpanStartOption
|
||||
readEvent bool
|
||||
writeEvent bool
|
||||
filters []Filter
|
||||
spanNameFormatter func(string, *http.Request) string
|
||||
publicEndpoint bool
|
||||
publicEndpointFn func(*http.Request) bool
|
||||
tracer trace.Tracer
|
||||
propagators propagation.TextMapPropagator
|
||||
spanStartOptions []trace.SpanStartOption
|
||||
readEvent bool
|
||||
writeEvent bool
|
||||
filters []Filter
|
||||
spanNameFormatter func(string, *http.Request) string
|
||||
publicEndpoint bool
|
||||
publicEndpointFn func(*http.Request) bool
|
||||
metricAttributesFn func(*http.Request) []attribute.KeyValue
|
||||
|
||||
semconv semconv.HTTPServer
|
||||
}
|
||||
@@ -79,12 +81,7 @@ func (h *middleware) configure(c *config) {
|
||||
h.publicEndpointFn = c.PublicEndpointFn
|
||||
h.server = c.ServerName
|
||||
h.semconv = semconv.NewHTTPServer(c.Meter)
|
||||
}
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
h.metricAttributesFn = c.MetricAttributesFn
|
||||
}
|
||||
|
||||
// serveHTTP sets up tracing and calls the given next http.Handler with the span
|
||||
@@ -123,6 +120,11 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
}
|
||||
}
|
||||
|
||||
if startTime := StartTimeFromContext(ctx); !startTime.IsZero() {
|
||||
opts = append(opts, trace.WithTimestamp(startTime))
|
||||
requestStartTime = startTime
|
||||
}
|
||||
|
||||
ctx, span := tracer.Start(ctx, h.spanNameFormatter(h.operation, r), opts...)
|
||||
defer span.End()
|
||||
|
||||
@@ -190,17 +192,31 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond)
|
||||
|
||||
h.semconv.RecordMetrics(ctx, semconv.MetricData{
|
||||
ServerName: h.server,
|
||||
metricAttributes := semconv.MetricAttributes{
|
||||
Req: r,
|
||||
StatusCode: statusCode,
|
||||
AdditionalAttributes: labeler.Get(),
|
||||
RequestSize: bw.BytesRead(),
|
||||
ResponseSize: bytesWritten,
|
||||
ElapsedTime: elapsedTime,
|
||||
AdditionalAttributes: append(labeler.Get(), h.metricAttributesFromRequest(r)...),
|
||||
}
|
||||
|
||||
h.semconv.RecordMetrics(ctx, semconv.ServerMetricData{
|
||||
ServerName: h.server,
|
||||
ResponseSize: bytesWritten,
|
||||
MetricAttributes: metricAttributes,
|
||||
MetricData: semconv.MetricData{
|
||||
RequestSize: bw.BytesRead(),
|
||||
ElapsedTime: elapsedTime,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *middleware) metricAttributesFromRequest(r *http.Request) []attribute.KeyValue {
|
||||
var attributeForRequest []attribute.KeyValue
|
||||
if h.metricAttributesFn != nil {
|
||||
attributeForRequest = h.metricAttributesFn(r)
|
||||
}
|
||||
return attributeForRequest
|
||||
}
|
||||
|
||||
// WithRouteTag annotates spans and metrics with the provided route name
|
||||
// with HTTP route attribute.
|
||||
func WithRouteTag(route string, h http.Handler) http.Handler {
|
||||
|
||||
Generated
Vendored
+9
-2
@@ -44,7 +44,9 @@ func (w *RespWriterWrapper) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
w.writeHeader(http.StatusOK)
|
||||
if !w.wroteHeader {
|
||||
w.writeHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
n, err := w.ResponseWriter.Write(p)
|
||||
n1 := int64(n)
|
||||
@@ -80,7 +82,12 @@ func (w *RespWriterWrapper) writeHeader(statusCode int) {
|
||||
|
||||
// Flush implements [http.Flusher].
|
||||
func (w *RespWriterWrapper) Flush() {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
if !w.wroteHeader {
|
||||
w.writeHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
if f, ok := w.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
|
||||
Generated
Vendored
+157
-32
@@ -1,3 +1,6 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/env.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@@ -9,12 +12,17 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
)
|
||||
|
||||
// OTelSemConvStabilityOptIn is an environment variable.
|
||||
// That can be set to "old" or "http/dup" to opt into the new HTTP semantic conventions.
|
||||
const OTelSemConvStabilityOptIn = "OTEL_SEMCONV_STABILITY_OPT_IN"
|
||||
|
||||
type ResponseTelemetry struct {
|
||||
StatusCode int
|
||||
ReadBytes int64
|
||||
@@ -30,6 +38,11 @@ type HTTPServer struct {
|
||||
requestBytesCounter metric.Int64Counter
|
||||
responseBytesCounter metric.Int64Counter
|
||||
serverLatencyMeasure metric.Float64Histogram
|
||||
|
||||
// New metrics
|
||||
requestBodySizeHistogram metric.Int64Histogram
|
||||
responseBodySizeHistogram metric.Int64Histogram
|
||||
requestDurationHistogram metric.Float64Histogram
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
@@ -50,9 +63,9 @@ type HTTPServer struct {
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
|
||||
if s.duplicate {
|
||||
return append(oldHTTPServer{}.RequestTraceAttrs(server, req), newHTTPServer{}.RequestTraceAttrs(server, req)...)
|
||||
return append(OldHTTPServer{}.RequestTraceAttrs(server, req), CurrentHTTPServer{}.RequestTraceAttrs(server, req)...)
|
||||
}
|
||||
return oldHTTPServer{}.RequestTraceAttrs(server, req)
|
||||
return OldHTTPServer{}.RequestTraceAttrs(server, req)
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response.
|
||||
@@ -60,14 +73,14 @@ func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request) []attrib
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
|
||||
func (s HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
if s.duplicate {
|
||||
return append(oldHTTPServer{}.ResponseTraceAttrs(resp), newHTTPServer{}.ResponseTraceAttrs(resp)...)
|
||||
return append(OldHTTPServer{}.ResponseTraceAttrs(resp), CurrentHTTPServer{}.ResponseTraceAttrs(resp)...)
|
||||
}
|
||||
return oldHTTPServer{}.ResponseTraceAttrs(resp)
|
||||
return OldHTTPServer{}.ResponseTraceAttrs(resp)
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (s HTTPServer) Route(route string) attribute.KeyValue {
|
||||
return oldHTTPServer{}.Route(route)
|
||||
return OldHTTPServer{}.Route(route)
|
||||
}
|
||||
|
||||
// Status returns a span status code and message for an HTTP status code
|
||||
@@ -83,67 +96,120 @@ func (s HTTPServer) Status(code int) (codes.Code, string) {
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
type MetricData struct {
|
||||
ServerName string
|
||||
type ServerMetricData struct {
|
||||
ServerName string
|
||||
ResponseSize int64
|
||||
|
||||
MetricData
|
||||
MetricAttributes
|
||||
}
|
||||
|
||||
type MetricAttributes struct {
|
||||
Req *http.Request
|
||||
StatusCode int
|
||||
AdditionalAttributes []attribute.KeyValue
|
||||
|
||||
RequestSize int64
|
||||
ResponseSize int64
|
||||
ElapsedTime float64
|
||||
}
|
||||
|
||||
func (s HTTPServer) RecordMetrics(ctx context.Context, md MetricData) {
|
||||
if s.requestBytesCounter == nil || s.responseBytesCounter == nil || s.serverLatencyMeasure == nil {
|
||||
// This will happen if an HTTPServer{} is used insted of NewHTTPServer.
|
||||
return
|
||||
type MetricData struct {
|
||||
RequestSize int64
|
||||
ElapsedTime float64
|
||||
}
|
||||
|
||||
var (
|
||||
metricAddOptionPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &[]metric.AddOption{}
|
||||
},
|
||||
}
|
||||
|
||||
attributes := oldHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
addOpts := []metric.AddOption{o} // Allocate vararg slice once.
|
||||
s.requestBytesCounter.Add(ctx, md.RequestSize, addOpts...)
|
||||
s.responseBytesCounter.Add(ctx, md.ResponseSize, addOpts...)
|
||||
s.serverLatencyMeasure.Record(ctx, md.ElapsedTime, o)
|
||||
metricRecordOptionPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &[]metric.RecordOption{}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// TODO: Duplicate Metrics
|
||||
func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
|
||||
if s.requestBytesCounter != nil && s.responseBytesCounter != nil && s.serverLatencyMeasure != nil {
|
||||
attributes := OldHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
addOpts := metricAddOptionPool.Get().(*[]metric.AddOption)
|
||||
*addOpts = append(*addOpts, o)
|
||||
s.requestBytesCounter.Add(ctx, md.RequestSize, *addOpts...)
|
||||
s.responseBytesCounter.Add(ctx, md.ResponseSize, *addOpts...)
|
||||
s.serverLatencyMeasure.Record(ctx, md.ElapsedTime, o)
|
||||
*addOpts = (*addOpts)[:0]
|
||||
metricAddOptionPool.Put(addOpts)
|
||||
}
|
||||
|
||||
if s.duplicate && s.requestDurationHistogram != nil && s.requestBodySizeHistogram != nil && s.responseBodySizeHistogram != nil {
|
||||
attributes := CurrentHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
|
||||
*recordOpts = append(*recordOpts, o)
|
||||
s.requestBodySizeHistogram.Record(ctx, md.RequestSize, *recordOpts...)
|
||||
s.responseBodySizeHistogram.Record(ctx, md.ResponseSize, *recordOpts...)
|
||||
s.requestDurationHistogram.Record(ctx, md.ElapsedTime, o)
|
||||
*recordOpts = (*recordOpts)[:0]
|
||||
metricRecordOptionPool.Put(recordOpts)
|
||||
}
|
||||
}
|
||||
|
||||
func NewHTTPServer(meter metric.Meter) HTTPServer {
|
||||
env := strings.ToLower(os.Getenv("OTEL_SEMCONV_STABILITY_OPT_IN"))
|
||||
env := strings.ToLower(os.Getenv(OTelSemConvStabilityOptIn))
|
||||
duplicate := env == "http/dup"
|
||||
server := HTTPServer{
|
||||
duplicate: duplicate,
|
||||
}
|
||||
server.requestBytesCounter, server.responseBytesCounter, server.serverLatencyMeasure = oldHTTPServer{}.createMeasures(meter)
|
||||
server.requestBytesCounter, server.responseBytesCounter, server.serverLatencyMeasure = OldHTTPServer{}.createMeasures(meter)
|
||||
if duplicate {
|
||||
server.requestBodySizeHistogram, server.responseBodySizeHistogram, server.requestDurationHistogram = CurrentHTTPServer{}.createMeasures(meter)
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
type HTTPClient struct {
|
||||
duplicate bool
|
||||
|
||||
// old metrics
|
||||
requestBytesCounter metric.Int64Counter
|
||||
responseBytesCounter metric.Int64Counter
|
||||
latencyMeasure metric.Float64Histogram
|
||||
|
||||
// new metrics
|
||||
requestBodySize metric.Int64Histogram
|
||||
requestDuration metric.Float64Histogram
|
||||
}
|
||||
|
||||
func NewHTTPClient() HTTPClient {
|
||||
env := strings.ToLower(os.Getenv("OTEL_SEMCONV_STABILITY_OPT_IN"))
|
||||
return HTTPClient{duplicate: env == "http/dup"}
|
||||
func NewHTTPClient(meter metric.Meter) HTTPClient {
|
||||
env := strings.ToLower(os.Getenv(OTelSemConvStabilityOptIn))
|
||||
duplicate := env == "http/dup"
|
||||
client := HTTPClient{
|
||||
duplicate: duplicate,
|
||||
}
|
||||
client.requestBytesCounter, client.responseBytesCounter, client.latencyMeasure = OldHTTPClient{}.createMeasures(meter)
|
||||
if duplicate {
|
||||
client.requestBodySize, client.requestDuration = CurrentHTTPClient{}.createMeasures(meter)
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns attributes for an HTTP request made by a client.
|
||||
func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
if c.duplicate {
|
||||
return append(oldHTTPClient{}.RequestTraceAttrs(req), newHTTPClient{}.RequestTraceAttrs(req)...)
|
||||
return append(OldHTTPClient{}.RequestTraceAttrs(req), CurrentHTTPClient{}.RequestTraceAttrs(req)...)
|
||||
}
|
||||
return oldHTTPClient{}.RequestTraceAttrs(req)
|
||||
return OldHTTPClient{}.RequestTraceAttrs(req)
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns metric attributes for an HTTP request made by a client.
|
||||
func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
if c.duplicate {
|
||||
return append(oldHTTPClient{}.ResponseTraceAttrs(resp), newHTTPClient{}.ResponseTraceAttrs(resp)...)
|
||||
return append(OldHTTPClient{}.ResponseTraceAttrs(resp), CurrentHTTPClient{}.ResponseTraceAttrs(resp)...)
|
||||
}
|
||||
|
||||
return oldHTTPClient{}.ResponseTraceAttrs(resp)
|
||||
return OldHTTPClient{}.ResponseTraceAttrs(resp)
|
||||
}
|
||||
|
||||
func (c HTTPClient) Status(code int) (codes.Code, string) {
|
||||
@@ -158,8 +224,67 @@ func (c HTTPClient) Status(code int) (codes.Code, string) {
|
||||
|
||||
func (c HTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
if c.duplicate {
|
||||
return newHTTPClient{}.ErrorType(err)
|
||||
return CurrentHTTPClient{}.ErrorType(err)
|
||||
}
|
||||
|
||||
return attribute.KeyValue{}
|
||||
}
|
||||
|
||||
type MetricOpts struct {
|
||||
measurement metric.MeasurementOption
|
||||
addOptions metric.AddOption
|
||||
}
|
||||
|
||||
func (o MetricOpts) MeasurementOption() metric.MeasurementOption {
|
||||
return o.measurement
|
||||
}
|
||||
|
||||
func (o MetricOpts) AddOptions() metric.AddOption {
|
||||
return o.addOptions
|
||||
}
|
||||
|
||||
func (c HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts {
|
||||
opts := map[string]MetricOpts{}
|
||||
|
||||
attributes := OldHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
|
||||
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
opts["old"] = MetricOpts{
|
||||
measurement: set,
|
||||
addOptions: set,
|
||||
}
|
||||
|
||||
if c.duplicate {
|
||||
attributes := CurrentHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
|
||||
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
opts["new"] = MetricOpts{
|
||||
measurement: set,
|
||||
addOptions: set,
|
||||
}
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func (s HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) {
|
||||
if s.requestBytesCounter == nil || s.latencyMeasure == nil {
|
||||
// This will happen if an HTTPClient{} is used instead of NewHTTPClient().
|
||||
return
|
||||
}
|
||||
|
||||
s.requestBytesCounter.Add(ctx, md.RequestSize, opts["old"].AddOptions())
|
||||
s.latencyMeasure.Record(ctx, md.ElapsedTime, opts["old"].MeasurementOption())
|
||||
|
||||
if s.duplicate {
|
||||
s.requestBodySize.Record(ctx, md.RequestSize, opts["new"].MeasurementOption())
|
||||
s.requestDuration.Record(ctx, md.ElapsedTime, opts["new"].MeasurementOption())
|
||||
}
|
||||
}
|
||||
|
||||
func (s HTTPClient) RecordResponseSize(ctx context.Context, responseData int64, opts map[string]MetricOpts) {
|
||||
if s.responseBytesCounter == nil {
|
||||
// This will happen if an HTTPClient{} is used instead of NewHTTPClient().
|
||||
return
|
||||
}
|
||||
|
||||
s.responseBytesCounter.Add(ctx, responseData, opts["old"].AddOptions())
|
||||
}
|
||||
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
|
||||
// Generate semconv package:
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/bench_test.go.tmpl "--data={}" --out=bench_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env.go.tmpl "--data={}" --out=env.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env_test.go.tmpl "--data={}" --out=env_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv.go.tmpl "--data={}" --out=httpconv.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv_test.go.tmpl "--data={}" --out=httpconv_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/util.go.tmpl "--data={}" --out=util.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/util_test.go.tmpl "--data={}" --out=util_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/v1.20.0.go.tmpl "--data={}" --out=v1.20.0.go
|
||||
Generated
Vendored
+188
-17
@@ -1,3 +1,6 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/httpconv.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@@ -7,14 +10,17 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/metric/noop"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
)
|
||||
|
||||
type newHTTPServer struct{}
|
||||
type CurrentHTTPServer struct{}
|
||||
|
||||
// TraceRequest returns trace attributes for an HTTP request received by a
|
||||
// server.
|
||||
@@ -32,18 +38,18 @@ type newHTTPServer struct{}
|
||||
//
|
||||
// If the primary server name is not known, server should be an empty string.
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (n newHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
|
||||
func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
|
||||
count := 3 // ServerAddress, Method, Scheme
|
||||
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = splitHostPort(req.Host)
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = splitHostPort(server)
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = splitHostPort(req.Host)
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +65,7 @@ func (n newHTTPServer) RequestTraceAttrs(server string, req *http.Request) []att
|
||||
|
||||
scheme := n.scheme(req.TLS != nil)
|
||||
|
||||
if peer, peerPort := splitHostPort(req.RemoteAddr); peer != "" {
|
||||
if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
count++
|
||||
@@ -104,7 +110,7 @@ func (n newHTTPServer) RequestTraceAttrs(server string, req *http.Request) []att
|
||||
attrs = append(attrs, methodOriginal)
|
||||
}
|
||||
|
||||
if peer, peerPort := splitHostPort(req.RemoteAddr); peer != "" {
|
||||
if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
attrs = append(attrs, semconvNew.NetworkPeerAddress(peer))
|
||||
@@ -135,7 +141,7 @@ func (n newHTTPServer) RequestTraceAttrs(server string, req *http.Request) []att
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n newHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
func (n CurrentHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
@@ -150,7 +156,7 @@ func (n newHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyV
|
||||
return semconvNew.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n newHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||
func (n CurrentHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||
if https {
|
||||
return semconvNew.URLScheme("https")
|
||||
}
|
||||
@@ -160,7 +166,7 @@ func (n newHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||
// TraceResponse returns trace attributes for telemetry from an HTTP response.
|
||||
//
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
|
||||
func (n newHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
func (n CurrentHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
var count int
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
@@ -195,14 +201,94 @@ func (n newHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.Ke
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (n newHTTPServer) Route(route string) attribute.KeyValue {
|
||||
func (n CurrentHTTPServer) Route(route string) attribute.KeyValue {
|
||||
return semconvNew.HTTPRoute(route)
|
||||
}
|
||||
|
||||
type newHTTPClient struct{}
|
||||
func (n CurrentHTTPServer) createMeasures(meter metric.Meter) (metric.Int64Histogram, metric.Int64Histogram, metric.Float64Histogram) {
|
||||
if meter == nil {
|
||||
return noop.Int64Histogram{}, noop.Int64Histogram{}, noop.Float64Histogram{}
|
||||
}
|
||||
|
||||
var err error
|
||||
requestBodySizeHistogram, err := meter.Int64Histogram(
|
||||
semconvNew.HTTPServerRequestBodySizeName,
|
||||
metric.WithUnit(semconvNew.HTTPServerRequestBodySizeUnit),
|
||||
metric.WithDescription(semconvNew.HTTPServerRequestBodySizeDescription),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
responseBodySizeHistogram, err := meter.Int64Histogram(
|
||||
semconvNew.HTTPServerResponseBodySizeName,
|
||||
metric.WithUnit(semconvNew.HTTPServerResponseBodySizeUnit),
|
||||
metric.WithDescription(semconvNew.HTTPServerResponseBodySizeDescription),
|
||||
)
|
||||
handleErr(err)
|
||||
requestDurationHistogram, err := meter.Float64Histogram(
|
||||
semconvNew.HTTPServerRequestDurationName,
|
||||
metric.WithUnit(semconvNew.HTTPServerRequestDurationUnit),
|
||||
metric.WithDescription(semconvNew.HTTPServerRequestDurationDescription),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return requestBodySizeHistogram, responseBodySizeHistogram, requestDurationHistogram
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 3
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
num++
|
||||
}
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
n.scheme(req.TLS != nil),
|
||||
semconvNew.ServerAddress(host))
|
||||
|
||||
if hostPort > 0 {
|
||||
attributes = append(attributes, semconvNew.ServerPort(hostPort))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
type CurrentHTTPClient struct{}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request made by a client.
|
||||
func (n newHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
func (n CurrentHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.request.method
|
||||
@@ -222,7 +308,7 @@ func (n newHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{urlHost, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = splitHostPort(hostport)
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
@@ -284,7 +370,7 @@ func (n newHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client.
|
||||
func (n newHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
func (n CurrentHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.response.status_code
|
||||
@@ -311,7 +397,7 @@ func (n newHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyVa
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n newHTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
func (n CurrentHTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
t := reflect.TypeOf(err)
|
||||
var value string
|
||||
if t.PkgPath() == "" && t.Name() == "" {
|
||||
@@ -328,7 +414,7 @@ func (n newHTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
return semconvNew.ErrorTypeKey.String(value)
|
||||
}
|
||||
|
||||
func (n newHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
func (n CurrentHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
@@ -343,6 +429,91 @@ func (n newHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyV
|
||||
return semconvNew.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) createMeasures(meter metric.Meter) (metric.Int64Histogram, metric.Float64Histogram) {
|
||||
if meter == nil {
|
||||
return noop.Int64Histogram{}, noop.Float64Histogram{}
|
||||
}
|
||||
|
||||
var err error
|
||||
requestBodySize, err := meter.Int64Histogram(
|
||||
semconvNew.HTTPClientRequestBodySizeName,
|
||||
metric.WithUnit(semconvNew.HTTPClientRequestBodySizeUnit),
|
||||
metric.WithDescription(semconvNew.HTTPClientRequestBodySizeDescription),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
requestDuration, err := meter.Float64Histogram(
|
||||
semconvNew.HTTPClientRequestDurationName,
|
||||
metric.WithUnit(semconvNew.HTTPClientRequestDurationUnit),
|
||||
metric.WithDescription(semconvNew.HTTPClientRequestDurationDescription),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return requestBodySize, requestDuration
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 2
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
h = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{h, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if port > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
semconvNew.ServerAddress(requestHost),
|
||||
n.scheme(req.TLS != nil),
|
||||
)
|
||||
|
||||
if port > 0 {
|
||||
attributes = append(attributes, semconvNew.ServerPort(port))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||
if https {
|
||||
return semconvNew.URLScheme("https")
|
||||
}
|
||||
return semconvNew.URLScheme("http")
|
||||
}
|
||||
|
||||
func isErrorStatusCode(code int) bool {
|
||||
return code >= 400 || code < 100
|
||||
}
|
||||
|
||||
Generated
Vendored
+15
-2
@@ -1,3 +1,6 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/util.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@@ -14,14 +17,14 @@ import (
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
)
|
||||
|
||||
// splitHostPort splits a network address hostport of the form "host",
|
||||
// SplitHostPort splits a network address hostport of the form "host",
|
||||
// "host%zone", "[host]", "[host%zone], "host:port", "host%zone:port",
|
||||
// "[host]:port", "[host%zone]:port", or ":port" into host or host%zone and
|
||||
// port.
|
||||
//
|
||||
// An empty host is returned if it is not provided or unparsable. A negative
|
||||
// port is returned if it is not provided or unparsable.
|
||||
func splitHostPort(hostport string) (host string, port int) {
|
||||
func SplitHostPort(hostport string) (host string, port int) {
|
||||
port = -1
|
||||
|
||||
if strings.HasPrefix(hostport, "[") {
|
||||
@@ -96,3 +99,13 @@ func handleErr(err error) {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
|
||||
func standardizeHTTPMethod(method string) string {
|
||||
method = strings.ToUpper(method)
|
||||
switch method {
|
||||
case http.MethodConnect, http.MethodDelete, http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodTrace:
|
||||
default:
|
||||
method = "_OTHER"
|
||||
}
|
||||
return method
|
||||
}
|
||||
|
||||
Generated
Vendored
+99
-25
@@ -1,3 +1,6 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/v120.0.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@@ -8,7 +11,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -17,7 +19,7 @@ import (
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
)
|
||||
|
||||
type oldHTTPServer struct{}
|
||||
type OldHTTPServer struct{}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
// server.
|
||||
@@ -35,14 +37,14 @@ type oldHTTPServer struct{}
|
||||
//
|
||||
// If the primary server name is not known, server should be an empty string.
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (o oldHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
|
||||
func (o OldHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
|
||||
return semconvutil.HTTPServerRequest(server, req)
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response.
|
||||
//
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
|
||||
func (o oldHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
func (o OldHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
attributes := []attribute.KeyValue{}
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
@@ -67,7 +69,7 @@ func (o oldHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.Ke
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (o oldHTTPServer) Route(route string) attribute.KeyValue {
|
||||
func (o OldHTTPServer) Route(route string) attribute.KeyValue {
|
||||
return semconv.HTTPRoute(route)
|
||||
}
|
||||
|
||||
@@ -84,7 +86,7 @@ const (
|
||||
serverDuration = "http.server.duration" // Incoming end to end duration, milliseconds
|
||||
)
|
||||
|
||||
func (h oldHTTPServer) createMeasures(meter metric.Meter) (metric.Int64Counter, metric.Int64Counter, metric.Float64Histogram) {
|
||||
func (h OldHTTPServer) createMeasures(meter metric.Meter) (metric.Int64Counter, metric.Int64Counter, metric.Float64Histogram) {
|
||||
if meter == nil {
|
||||
return noop.Int64Counter{}, noop.Int64Counter{}, noop.Float64Histogram{}
|
||||
}
|
||||
@@ -113,17 +115,17 @@ func (h oldHTTPServer) createMeasures(meter metric.Meter) (metric.Int64Counter,
|
||||
return requestBytesCounter, responseBytesCounter, serverLatencyMeasure
|
||||
}
|
||||
|
||||
func (o oldHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
func (o OldHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
n := len(additionalAttributes) + 3
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = splitHostPort(req.Host)
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = splitHostPort(server)
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = splitHostPort(req.Host)
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
@@ -144,7 +146,7 @@ func (o oldHTTPServer) MetricAttributes(server string, req *http.Request, status
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, n)
|
||||
attributes = append(attributes,
|
||||
o.methodMetric(req.Method),
|
||||
semconv.HTTPMethod(standardizeHTTPMethod(req.Method)),
|
||||
o.scheme(req.TLS != nil),
|
||||
semconv.NetHostName(host))
|
||||
|
||||
@@ -164,29 +166,101 @@ func (o oldHTTPServer) MetricAttributes(server string, req *http.Request, status
|
||||
return attributes
|
||||
}
|
||||
|
||||
func (o oldHTTPServer) methodMetric(method string) attribute.KeyValue {
|
||||
method = strings.ToUpper(method)
|
||||
switch method {
|
||||
case http.MethodConnect, http.MethodDelete, http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodTrace:
|
||||
default:
|
||||
method = "_OTHER"
|
||||
}
|
||||
return semconv.HTTPMethod(method)
|
||||
}
|
||||
|
||||
func (o oldHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||
func (o OldHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||
if https {
|
||||
return semconv.HTTPSchemeHTTPS
|
||||
}
|
||||
return semconv.HTTPSchemeHTTP
|
||||
}
|
||||
|
||||
type oldHTTPClient struct{}
|
||||
type OldHTTPClient struct{}
|
||||
|
||||
func (o oldHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
func (o OldHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
return semconvutil.HTTPClientRequest(req)
|
||||
}
|
||||
|
||||
func (o oldHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
func (o OldHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
return semconvutil.HTTPClientResponse(resp)
|
||||
}
|
||||
|
||||
func (o OldHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
/* The following semantic conventions are returned if present:
|
||||
http.method string
|
||||
http.status_code int
|
||||
net.peer.name string
|
||||
net.peer.port int
|
||||
*/
|
||||
|
||||
n := 2 // method, peer name.
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
h = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{h, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if port > 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, n)
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPMethod(standardizeHTTPMethod(req.Method)),
|
||||
semconv.NetPeerName(requestHost),
|
||||
)
|
||||
|
||||
if port > 0 {
|
||||
attributes = append(attributes, semconv.NetPeerPort(port))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconv.HTTPStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
// Client HTTP metrics.
|
||||
const (
|
||||
clientRequestSize = "http.client.request.size" // Incoming request bytes total
|
||||
clientResponseSize = "http.client.response.size" // Incoming response bytes total
|
||||
clientDuration = "http.client.duration" // Incoming end to end duration, milliseconds
|
||||
)
|
||||
|
||||
func (o OldHTTPClient) createMeasures(meter metric.Meter) (metric.Int64Counter, metric.Int64Counter, metric.Float64Histogram) {
|
||||
if meter == nil {
|
||||
return noop.Int64Counter{}, noop.Int64Counter{}, noop.Float64Histogram{}
|
||||
}
|
||||
requestBytesCounter, err := meter.Int64Counter(
|
||||
clientRequestSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP request messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
responseBytesCounter, err := meter.Int64Counter(
|
||||
clientResponseSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP response messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
latencyMeasure, err := meter.Float64Histogram(
|
||||
clientDuration,
|
||||
metric.WithUnit("ms"),
|
||||
metric.WithDescription("Measures the duration of outbound HTTP requests."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return requestBytesCounter, responseBytesCounter, latencyMeasure
|
||||
}
|
||||
|
||||
Generated
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type startTimeContextKeyType int
|
||||
|
||||
const startTimeContextKey startTimeContextKeyType = 0
|
||||
|
||||
// ContextWithStartTime returns a new context with the provided start time. The
|
||||
// start time will be used for metrics and traces emitted by the
|
||||
// instrumentation. Only one labeller can be injected into the context.
|
||||
// Injecting it multiple times will override the previous calls.
|
||||
func ContextWithStartTime(parent context.Context, start time.Time) context.Context {
|
||||
return context.WithValue(parent, startTimeContextKey, start)
|
||||
}
|
||||
|
||||
// StartTimeFromContext retrieves a time.Time from the provided context if one
|
||||
// is available. If no start time was found in the provided context, a new,
|
||||
// zero start time is returned and the second return value is false.
|
||||
func StartTimeFromContext(ctx context.Context) time.Time {
|
||||
t, _ := ctx.Value(startTimeContextKey).(time.Time)
|
||||
return t
|
||||
}
|
||||
Generated
Vendored
+14
-44
@@ -13,11 +13,9 @@ import (
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -29,7 +27,6 @@ type Transport struct {
|
||||
rt http.RoundTripper
|
||||
|
||||
tracer trace.Tracer
|
||||
meter metric.Meter
|
||||
propagators propagation.TextMapPropagator
|
||||
spanStartOptions []trace.SpanStartOption
|
||||
filters []Filter
|
||||
@@ -37,10 +34,7 @@ type Transport struct {
|
||||
clientTrace func(context.Context) *httptrace.ClientTrace
|
||||
metricAttributesFn func(*http.Request) []attribute.KeyValue
|
||||
|
||||
semconv semconv.HTTPClient
|
||||
requestBytesCounter metric.Int64Counter
|
||||
responseBytesCounter metric.Int64Counter
|
||||
latencyMeasure metric.Float64Histogram
|
||||
semconv semconv.HTTPClient
|
||||
}
|
||||
|
||||
var _ http.RoundTripper = &Transport{}
|
||||
@@ -57,8 +51,7 @@ func NewTransport(base http.RoundTripper, opts ...Option) *Transport {
|
||||
}
|
||||
|
||||
t := Transport{
|
||||
rt: base,
|
||||
semconv: semconv.NewHTTPClient(),
|
||||
rt: base,
|
||||
}
|
||||
|
||||
defaultOpts := []Option{
|
||||
@@ -68,46 +61,21 @@ func NewTransport(base http.RoundTripper, opts ...Option) *Transport {
|
||||
|
||||
c := newConfig(append(defaultOpts, opts...)...)
|
||||
t.applyConfig(c)
|
||||
t.createMeasures()
|
||||
|
||||
return &t
|
||||
}
|
||||
|
||||
func (t *Transport) applyConfig(c *config) {
|
||||
t.tracer = c.Tracer
|
||||
t.meter = c.Meter
|
||||
t.propagators = c.Propagators
|
||||
t.spanStartOptions = c.SpanStartOptions
|
||||
t.filters = c.Filters
|
||||
t.spanNameFormatter = c.SpanNameFormatter
|
||||
t.clientTrace = c.ClientTrace
|
||||
t.semconv = semconv.NewHTTPClient(c.Meter)
|
||||
t.metricAttributesFn = c.MetricAttributesFn
|
||||
}
|
||||
|
||||
func (t *Transport) createMeasures() {
|
||||
var err error
|
||||
t.requestBytesCounter, err = t.meter.Int64Counter(
|
||||
clientRequestSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP request messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
t.responseBytesCounter, err = t.meter.Int64Counter(
|
||||
clientResponseSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP response messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
t.latencyMeasure, err = t.meter.Float64Histogram(
|
||||
clientDuration,
|
||||
metric.WithUnit("ms"),
|
||||
metric.WithDescription("Measures the duration of outbound HTTP requests."),
|
||||
)
|
||||
handleErr(err)
|
||||
}
|
||||
|
||||
func defaultTransportFormatter(_ string, r *http.Request) string {
|
||||
return "HTTP " + r.Method
|
||||
}
|
||||
@@ -177,16 +145,15 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
}
|
||||
|
||||
// metrics
|
||||
metricAttrs := append(append(labeler.Get(), semconvutil.HTTPClientRequestMetrics(r)...), t.metricAttributesFromRequest(r)...)
|
||||
if res.StatusCode > 0 {
|
||||
metricAttrs = append(metricAttrs, semconv.HTTPStatusCode(res.StatusCode))
|
||||
}
|
||||
o := metric.WithAttributeSet(attribute.NewSet(metricAttrs...))
|
||||
metricOpts := t.semconv.MetricOptions(semconv.MetricAttributes{
|
||||
Req: r,
|
||||
StatusCode: res.StatusCode,
|
||||
AdditionalAttributes: append(labeler.Get(), t.metricAttributesFromRequest(r)...),
|
||||
})
|
||||
|
||||
t.requestBytesCounter.Add(ctx, bw.BytesRead(), o)
|
||||
// For handling response bytes we leverage a callback when the client reads the http response
|
||||
readRecordFunc := func(n int64) {
|
||||
t.responseBytesCounter.Add(ctx, n, o)
|
||||
t.semconv.RecordResponseSize(ctx, n, metricOpts)
|
||||
}
|
||||
|
||||
// traces
|
||||
@@ -198,9 +165,12 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond)
|
||||
|
||||
t.latencyMeasure.Record(ctx, elapsedTime, o)
|
||||
t.semconv.RecordMetrics(ctx, semconv.MetricData{
|
||||
RequestSize: bw.BytesRead(),
|
||||
ElapsedTime: elapsedTime,
|
||||
}, metricOpts)
|
||||
|
||||
return res, err
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (t *Transport) metricAttributesFromRequest(r *http.Request) []attribute.KeyValue {
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http
|
||||
|
||||
// Version is the current release version of the otelhttp instrumentation.
|
||||
func Version() string {
|
||||
return "0.54.0"
|
||||
return "0.59.0"
|
||||
// This string is updated by the pre_release.sh script during release
|
||||
}
|
||||
|
||||
|
||||
+17
-4
@@ -8,6 +8,21 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
<!-- Released section -->
|
||||
<!-- Don't change this section unless doing release -->
|
||||
|
||||
## [1.34.0/0.56.0/0.10.0] 2025-01-17
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove the notices from `Logger` to make the whole Logs API user-facing in `go.opentelemetry.io/otel/log`. (#6167)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Relax minimum Go version to 1.22.0 in various modules. (#6073)
|
||||
- The `Type` name logged for the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` client is corrected from `otlphttpgrpc` to `otlptracegrpc`. (#6143)
|
||||
- The `Type` name logged for the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlphttpgrpc` client is corrected from `otlphttphttp` to `otlptracehttp`. (#6143)
|
||||
|
||||
## [1.33.0/0.55.0/0.9.0/0.0.12] 2024-12-12
|
||||
|
||||
### Added
|
||||
@@ -37,9 +52,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
- Fix attribute value truncation in `go.opentelemetry.io/otel/sdk/trace`. (#5997)
|
||||
- Fix attribute value truncation in `go.opentelemetry.io/otel/sdk/log`. (#6032)
|
||||
|
||||
<!-- Released section -->
|
||||
<!-- Don't change this section unless doing release -->
|
||||
|
||||
## [1.32.0/0.54.0/0.8.0/0.0.11] 2024-11-08
|
||||
|
||||
### Added
|
||||
@@ -3185,7 +3197,8 @@ It contains api and sdk for trace and meter.
|
||||
- CircleCI build CI manifest files.
|
||||
- CODEOWNERS file to track owners of this project.
|
||||
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.33.0...HEAD
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.34.0...HEAD
|
||||
[1.34.0/0.56.0/0.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.34.0
|
||||
[1.33.0/0.55.0/0.9.0/0.0.12]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.33.0
|
||||
[1.32.0/0.54.0/0.8.0/0.0.11]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.32.0
|
||||
[1.31.0/0.53.0/0.7.0/0.0.10]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.31.0
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# OpenTelemetry-Go
|
||||
|
||||
[](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amain)
|
||||
[](https://github.com/open-telemetry/opentelemetry-go/actions/workflows/ci.yml)
|
||||
[](https://app.codecov.io/gh/open-telemetry/opentelemetry-go?branch=main)
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel)
|
||||
[](https://goreportcard.com/report/go.opentelemetry.io/otel)
|
||||
|
||||
+3
-3
@@ -130,6 +130,6 @@ Importantly, bump any package versions referenced to be the latest one you just
|
||||
|
||||
Bump the dependencies in the following Go services:
|
||||
|
||||
- [`accountingservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/accountingservice)
|
||||
- [`checkoutservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/checkoutservice)
|
||||
- [`productcatalogservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/productcatalogservice)
|
||||
- [`accounting`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/accounting)
|
||||
- [`checkoutservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/checkout)
|
||||
- [`productcatalogservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/product-catalog)
|
||||
|
||||
+1
-1
@@ -294,7 +294,7 @@ func (c *client) MarshalLog() interface{} {
|
||||
Type string
|
||||
Endpoint string
|
||||
}{
|
||||
Type: "otlphttpgrpc",
|
||||
Type: "otlptracegrpc",
|
||||
Endpoint: c.endpoint,
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP trace exporter in use.
|
||||
func Version() string {
|
||||
return "1.33.0"
|
||||
return "1.34.0"
|
||||
}
|
||||
|
||||
-6
@@ -14,12 +14,6 @@
|
||||
"matchDepTypes": ["indirect"],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"matchFileNames": ["internal/tools/**"],
|
||||
"matchManagers": ["gomod"],
|
||||
"matchDepTypes": ["indirect"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["google.golang.org/genproto/googleapis/**"],
|
||||
"groupName": "googleapis"
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry SDK in use.
|
||||
func Version() string {
|
||||
return "1.33.0"
|
||||
return "1.34.0"
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ package otel // import "go.opentelemetry.io/otel"
|
||||
|
||||
// Version is the current release version of OpenTelemetry in use.
|
||||
func Version() string {
|
||||
return "1.33.0"
|
||||
return "1.34.0"
|
||||
}
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@
|
||||
|
||||
module-sets:
|
||||
stable-v1:
|
||||
version: v1.33.0
|
||||
version: v1.34.0
|
||||
modules:
|
||||
- go.opentelemetry.io/otel
|
||||
- go.opentelemetry.io/otel/bridge/opencensus
|
||||
@@ -23,11 +23,11 @@ module-sets:
|
||||
- go.opentelemetry.io/otel/sdk/metric
|
||||
- go.opentelemetry.io/otel/trace
|
||||
experimental-metrics:
|
||||
version: v0.55.0
|
||||
version: v0.56.0
|
||||
modules:
|
||||
- go.opentelemetry.io/otel/exporters/prometheus
|
||||
experimental-logs:
|
||||
version: v0.9.0
|
||||
version: v0.10.0
|
||||
modules:
|
||||
- go.opentelemetry.io/otel/log
|
||||
- go.opentelemetry.io/otel/sdk/log
|
||||
|
||||
+4
-2
@@ -311,7 +311,8 @@ type ResourceSpans struct {
|
||||
// A list of ScopeSpans that originate from a resource.
|
||||
ScopeSpans []*ScopeSpans `protobuf:"bytes,2,rep,name=scope_spans,json=scopeSpans,proto3" json:"scope_spans,omitempty"`
|
||||
// The Schema URL, if known. This is the identifier of the Schema that the resource data
|
||||
// is recorded in. To learn more about Schema URL see
|
||||
// is recorded in. Notably, the last part of the URL path is the version number of the
|
||||
// schema: http[s]://server[:port]/path/<version>. To learn more about Schema URL see
|
||||
// https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
|
||||
// This schema_url applies to the data in the "resource" field. It does not apply
|
||||
// to the data in the "scope_spans" field which have their own schema_url field.
|
||||
@@ -384,7 +385,8 @@ type ScopeSpans struct {
|
||||
// A list of Spans that originate from an instrumentation scope.
|
||||
Spans []*Span `protobuf:"bytes,2,rep,name=spans,proto3" json:"spans,omitempty"`
|
||||
// The Schema URL, if known. This is the identifier of the Schema that the span data
|
||||
// is recorded in. To learn more about Schema URL see
|
||||
// is recorded in. Notably, the last part of the URL path is the version number of the
|
||||
// schema: http[s]://server[:port]/path/<version>. To learn more about Schema URL see
|
||||
// https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
|
||||
// This schema_url applies to all spans and span events in the "spans" field.
|
||||
SchemaUrl string `protobuf:"bytes,3,opt,name=schema_url,json=schemaUrl,proto3" json:"schema_url,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user