opentelemetry-go-contrib/instrumentation/github.com/labstack/echo/echo_test.go

214 lines
6.8 KiB
Go

// 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.
// Based on https://github.com/DataDog/dd-trace-go/blob/8fb554ff7cf694267f9077ae35e27ce4689ed8b6/contrib/gin-gonic/gin/gintrace_test.go
package echo
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
mocktrace "go.opentelemetry.io/contrib/internal/trace"
otelglobal "go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/kv"
otelpropagation "go.opentelemetry.io/otel/api/propagation"
oteltrace "go.opentelemetry.io/otel/api/trace"
)
func TestChildSpanFromGlobalTracer(t *testing.T) {
otelglobal.SetTraceProvider(&mocktrace.Provider{})
router := echo.New()
router.Use(Middleware("foobar"))
router.GET("/user/:id", func(c echo.Context) error {
span := oteltrace.SpanFromContext(c.Request().Context())
_, ok := span.(*mocktrace.Span)
assert.True(t, ok)
spanTracer := span.Tracer()
mockTracer, ok := spanTracer.(*mocktrace.Tracer)
require.True(t, ok)
assert.Equal(t, "go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo", mockTracer.Name)
return c.NoContent(200)
})
r := httptest.NewRequest("GET", "/user/123", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, r)
}
func TestChildSpanFromCustomTracer(t *testing.T) {
tracer := mocktrace.NewTracer("test-tracer")
router := echo.New()
router.Use(Middleware("foobar", WithTracer(tracer)))
router.GET("/user/:id", func(c echo.Context) error {
span := oteltrace.SpanFromContext(c.Request().Context())
_, ok := span.(*mocktrace.Span)
assert.True(t, ok)
spanTracer := span.Tracer()
mockTracer, ok := spanTracer.(*mocktrace.Tracer)
require.True(t, ok)
assert.Equal(t, "test-tracer", mockTracer.Name)
return c.NoContent(200)
})
r := httptest.NewRequest("GET", "/user/123", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, r)
}
func TestTrace200(t *testing.T) {
tracer := mocktrace.NewTracer("test-tracer")
router := echo.New()
router.Use(Middleware("foobar", WithTracer(tracer)))
router.GET("/user/:id", func(c echo.Context) error {
span := oteltrace.SpanFromContext(c.Request().Context())
mspan, ok := span.(*mocktrace.Span)
require.True(t, ok)
assert.Equal(t, kv.StringValue("foobar"), mspan.Attributes["http.server_name"])
id := c.Param("id")
return c.String(200, id)
})
r := httptest.NewRequest("GET", "/user/123", nil)
w := httptest.NewRecorder()
// do and verify the request
router.ServeHTTP(w, r)
response := w.Result()
require.Equal(t, http.StatusOK, response.StatusCode)
// verify traces look good
spans := tracer.EndedSpans()
require.Len(t, spans, 1)
span := spans[0]
assert.Equal(t, "/user/:id", span.Name)
assert.Equal(t, oteltrace.SpanKindServer, span.Kind)
assert.Equal(t, kv.StringValue("foobar"), span.Attributes["http.server_name"])
assert.Equal(t, kv.IntValue(http.StatusOK), span.Attributes["http.status_code"])
assert.Equal(t, kv.StringValue("GET"), span.Attributes["http.method"])
assert.Equal(t, kv.StringValue("/user/123"), span.Attributes["http.target"])
assert.Equal(t, kv.StringValue("/user/:id"), span.Attributes["http.route"])
}
func TestError(t *testing.T) {
tracer := mocktrace.NewTracer("test-tracer")
// setup
router := echo.New()
router.Use(Middleware("foobar", WithTracer(tracer)))
wantErr := errors.New("oh no")
// configure a handler that returns an error and 5xx status
// code
router.GET("/server_err", func(c echo.Context) error {
return wantErr
})
r := httptest.NewRequest("GET", "/server_err", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, r)
response := w.Result()
assert.Equal(t, http.StatusInternalServerError, response.StatusCode)
// verify the errors and status are correct
spans := tracer.EndedSpans()
require.Len(t, spans, 1)
span := spans[0]
assert.Equal(t, "/server_err", span.Name)
assert.Equal(t, kv.StringValue("foobar"), span.Attributes["http.server_name"])
assert.Equal(t, kv.IntValue(http.StatusInternalServerError), span.Attributes["http.status_code"])
assert.Equal(t, kv.StringValue("oh no"), span.Attributes["echo.error"])
// server errors set the status
assert.Equal(t, codes.Internal, span.Status)
}
func TestGetSpanNotInstrumented(t *testing.T) {
router := echo.New()
router.GET("/ping", func(c echo.Context) error {
// Assert we don't have a span on the context.
span := oteltrace.SpanFromContext(c.Request().Context())
_, ok := span.(oteltrace.NoopSpan)
assert.True(t, ok)
return c.String(200, "ok")
})
r := httptest.NewRequest("GET", "/ping", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, r)
response := w.Result()
assert.Equal(t, http.StatusOK, response.StatusCode)
}
func TestPropagationWithGlobalPropagators(t *testing.T) {
tracer := mocktrace.NewTracer("test-tracer")
r := httptest.NewRequest("GET", "/user/123", nil)
w := httptest.NewRecorder()
ctx, pspan := tracer.Start(context.Background(), "test")
otelpropagation.InjectHTTP(ctx, otelglobal.Propagators(), r.Header)
router := echo.New()
router.Use(Middleware("foobar", WithTracer(tracer)))
router.GET("/user/:id", func(c echo.Context) error {
span := oteltrace.SpanFromContext(c.Request().Context())
mspan, ok := span.(*mocktrace.Span)
require.True(t, ok)
assert.Equal(t, pspan.SpanContext().TraceID, mspan.SpanContext().TraceID)
assert.Equal(t, pspan.SpanContext().SpanID, mspan.ParentSpanID)
return c.NoContent(200)
})
router.ServeHTTP(w, r)
}
func TestPropagationWithCustomPropagators(t *testing.T) {
tracer := mocktrace.NewTracer("test-tracer")
b3 := oteltrace.B3{}
props := otelpropagation.New(
otelpropagation.WithExtractors(b3),
otelpropagation.WithInjectors(b3),
)
r := httptest.NewRequest("GET", "/user/123", nil)
w := httptest.NewRecorder()
ctx, pspan := tracer.Start(context.Background(), "test")
otelpropagation.InjectHTTP(ctx, props, r.Header)
router := echo.New()
router.Use(Middleware("foobar", WithTracer(tracer), WithPropagators(props)))
router.GET("/user/:id", func(c echo.Context) error {
span := oteltrace.SpanFromContext(c.Request().Context())
mspan, ok := span.(*mocktrace.Span)
require.True(t, ok)
assert.Equal(t, pspan.SpanContext().TraceID, mspan.SpanContext().TraceID)
assert.Equal(t, pspan.SpanContext().SpanID, mspan.ParentSpanID)
return c.NoContent(200)
})
router.ServeHTTP(w, r)
}