otelhttp: get tracer from current context if not set in constructor (#873)
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
188ebf0dcc
commit
e81f85aff9
|
@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- The `Transport`, `Handler`, and HTTP client convenience wrappers in the `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` package now use the `TracerProvider` from the parent context if one exists and none was explicitly set when configuring the instrumentation. (#873)
|
||||
|
||||
## [1.1.0/0.26.0] - 2021-10-28
|
||||
|
||||
Update dependency on the `go.opentelemetry.io/otel` project to `v1.1.0`.
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// Attribute keys that can be added to a span.
|
||||
|
@ -39,3 +40,7 @@ const (
|
|||
// 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(SemVersion()))
|
||||
}
|
||||
|
|
|
@ -59,17 +59,17 @@ func (o optionFunc) apply(c *config) {
|
|||
func newConfig(opts ...Option) *config {
|
||||
c := &config{
|
||||
Propagators: otel.GetTextMapPropagator(),
|
||||
TracerProvider: otel.GetTracerProvider(),
|
||||
MeterProvider: global.GetMeterProvider(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.apply(c)
|
||||
}
|
||||
|
||||
c.Tracer = c.TracerProvider.Tracer(
|
||||
instrumentationName,
|
||||
trace.WithInstrumentationVersion(SemVersion()),
|
||||
)
|
||||
// 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(SemVersion()),
|
||||
|
|
|
@ -127,8 +127,18 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
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 := h.tracer.Start(ctx, h.spanNameFormatter(h.operation, r), opts...)
|
||||
ctx, span := tracer.Start(ctx, h.spanNameFormatter(h.operation, r), opts...)
|
||||
defer span.End()
|
||||
|
||||
readRecordFunc := func(int64) {}
|
||||
|
|
|
@ -85,3 +85,35 @@ func TestConvenienceWrappers(t *testing.T) {
|
|||
assert.Equal(t, "HTTP POST", spans[2].Name())
|
||||
assert.Equal(t, "HTTP POST", spans[3].Name())
|
||||
}
|
||||
|
||||
func TestClientWithTraceContext(t *testing.T) {
|
||||
sr := tracetest.NewSpanRecorder()
|
||||
provider := trace.NewTracerProvider(trace.WithSpanProcessor(sr))
|
||||
|
||||
tracer := provider.Tracer("")
|
||||
ctx, span := tracer.Start(context.Background(), "http requests")
|
||||
|
||||
content := []byte("Hello, world!")
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := w.Write(content); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := otelhttp.Get(ctx, ts.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
span.End()
|
||||
|
||||
spans := sr.Ended()
|
||||
require.Equal(t, 2, len(spans))
|
||||
assert.Equal(t, "HTTP GET", spans[0].Name())
|
||||
assert.Equal(t, "http requests", spans[1].Name())
|
||||
assert.NotEmpty(t, spans[0].Parent().SpanID())
|
||||
assert.Equal(t, spans[1].SpanContext().SpanID(), spans[0].Parent().SpanID())
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -24,6 +25,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
@ -107,3 +109,37 @@ func TestHandlerBasics(t *testing.T) {
|
|||
t.Fatalf("got %q, expected %q", got, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerRequestWithTraceContext(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
h := otelhttp.NewHandler(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte("hello world"))
|
||||
require.NoError(t, err)
|
||||
}), "test_handler")
|
||||
|
||||
r, err := http.NewRequest(http.MethodGet, "http://localhost/", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
spanRecorder := tracetest.NewSpanRecorder()
|
||||
provider := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSpanProcessor(spanRecorder),
|
||||
)
|
||||
tracer := provider.Tracer("")
|
||||
ctx, span := tracer.Start(context.Background(), "test_request")
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
h.ServeHTTP(rr, r)
|
||||
assert.Equal(t, 200, rr.Result().StatusCode)
|
||||
|
||||
span.End()
|
||||
|
||||
spans := spanRecorder.Ended()
|
||||
require.Len(t, spans, 2)
|
||||
|
||||
assert.Equal(t, "test_handler", spans[0].Name())
|
||||
assert.Equal(t, "test_request", spans[1].Name())
|
||||
assert.NotEmpty(t, spans[0].Parent().SpanID())
|
||||
assert.Equal(t, spans[1].SpanContext().SpanID(), spans[0].Parent().SpanID())
|
||||
}
|
||||
|
|
|
@ -15,11 +15,16 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
|
@ -116,3 +121,48 @@ func TestTransportErrorStatus(t *testing.T) {
|
|||
t.Errorf("expected error status message on span; got: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransportRequestWithTraceContext(t *testing.T) {
|
||||
spanRecorder := tracetest.NewSpanRecorder()
|
||||
provider := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSpanProcessor(spanRecorder),
|
||||
)
|
||||
content := []byte("Hello, world!")
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write(content)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
tracer := provider.Tracer("")
|
||||
ctx, span := tracer.Start(context.Background(), "test_span")
|
||||
|
||||
r, err := http.NewRequest(http.MethodGet, ts.URL, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
tr := otelhttp.NewTransport(
|
||||
http.DefaultTransport,
|
||||
)
|
||||
|
||||
c := http.Client{Transport: tr}
|
||||
res, err := c.Do(r)
|
||||
require.NoError(t, err)
|
||||
|
||||
span.End()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, content, body)
|
||||
|
||||
spans := spanRecorder.Ended()
|
||||
require.Len(t, spans, 2)
|
||||
|
||||
assert.Equal(t, "test_span", spans[0].Name())
|
||||
assert.Equal(t, "HTTP GET", spans[1].Name())
|
||||
assert.NotEmpty(t, spans[1].Parent().SpanID())
|
||||
assert.Equal(t, spans[0].SpanContext().SpanID(), spans[1].Parent().SpanID())
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||
|
@ -87,9 +88,19 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
|||
}
|
||||
}
|
||||
|
||||
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 := t.tracer.Start(r.Context(), t.spanNameFormatter("", r), opts...)
|
||||
ctx, span := tracer.Start(r.Context(), t.spanNameFormatter("", r), opts...)
|
||||
|
||||
r = r.WithContext(ctx)
|
||||
span.SetAttributes(semconv.HTTPClientAttributesFromHTTPRequest(r)...)
|
||||
|
|
Loading…
Reference in New Issue