Dynamic Configuration Refactoring
This commit is contained in:
parent
d3ae88f108
commit
a09dfa3ce1
452 changed files with 21023 additions and 9419 deletions
|
@ -1,25 +0,0 @@
|
|||
package tracing
|
||||
|
||||
import "net/http"
|
||||
|
||||
// HTTPHeadersCarrier custom implementation to fix duplicated headers
|
||||
// It has been fixed in https://github.com/opentracing/opentracing-go/pull/191
|
||||
type HTTPHeadersCarrier http.Header
|
||||
|
||||
// Set conforms to the TextMapWriter interface.
|
||||
func (c HTTPHeadersCarrier) Set(key, val string) {
|
||||
h := http.Header(c)
|
||||
h.Set(key, val)
|
||||
}
|
||||
|
||||
// ForeachKey conforms to the TextMapReader interface.
|
||||
func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
|
||||
for k, vals := range c {
|
||||
for _, v := range vals {
|
||||
if err := handler(k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package datadog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
ddtracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer"
|
||||
datadog "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
||||
)
|
||||
|
||||
// Name sets the name of this tracer
|
||||
const Name = "datadog"
|
||||
|
||||
// Config provides configuration settings for a datadog tracer
|
||||
type Config struct {
|
||||
LocalAgentHostPort string `description:"Set datadog-agent's host:port that the reporter will used. Defaults to localhost:8126" export:"false"`
|
||||
GlobalTag string `description:"Key:Value tag to be set on all the spans." export:"true"`
|
||||
Debug bool `description:"Enable DataDog debug." export:"true"`
|
||||
}
|
||||
|
||||
// Setup sets up the tracer
|
||||
func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error) {
|
||||
tag := strings.SplitN(c.GlobalTag, ":", 2)
|
||||
|
||||
value := ""
|
||||
if len(tag) == 2 {
|
||||
value = tag[1]
|
||||
}
|
||||
|
||||
tracer := ddtracer.New(
|
||||
datadog.WithAgentAddr(c.LocalAgentHostPort),
|
||||
datadog.WithServiceName(serviceName),
|
||||
datadog.WithGlobalTag(tag[0], value),
|
||||
datadog.WithDebugMode(c.Debug),
|
||||
)
|
||||
|
||||
// Without this, child spans are getting the NOOP tracer
|
||||
opentracing.SetGlobalTracer(tracer)
|
||||
|
||||
log.Debug("DataDog tracer configured")
|
||||
|
||||
return tracer, nil, nil
|
||||
}
|
|
@ -1,57 +1,57 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/alice"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/tracing"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/urfave/negroni"
|
||||
)
|
||||
|
||||
type entryPointMiddleware struct {
|
||||
entryPoint string
|
||||
*Tracing
|
||||
}
|
||||
const (
|
||||
entryPointTypeName = "TracingEntryPoint"
|
||||
)
|
||||
|
||||
// NewEntryPoint creates a new middleware that the incoming request
|
||||
func (t *Tracing) NewEntryPoint(name string) negroni.Handler {
|
||||
log.Debug("Added entrypoint tracing middleware")
|
||||
return &entryPointMiddleware{Tracing: t, entryPoint: name}
|
||||
}
|
||||
// NewEntryPoint creates a new middleware that the incoming request.
|
||||
func NewEntryPoint(ctx context.Context, t *tracing.Tracing, entryPointName string, next http.Handler) http.Handler {
|
||||
middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug("Creating middleware")
|
||||
|
||||
func (e *entryPointMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
opNameFunc := generateEntryPointSpanName
|
||||
|
||||
ctx, _ := e.Extract(opentracing.HTTPHeaders, HTTPHeadersCarrier(r.Header))
|
||||
span := e.StartSpan(opNameFunc(r, e.entryPoint, e.SpanNameLimit), ext.RPCServerOption(ctx))
|
||||
ext.Component.Set(span, e.ServiceName)
|
||||
LogRequest(span, r)
|
||||
ext.SpanKindRPCServer.Set(span)
|
||||
|
||||
r = r.WithContext(opentracing.ContextWithSpan(r.Context(), span))
|
||||
|
||||
recorder := newStatusCodeRecoder(w, 200)
|
||||
next(recorder, r)
|
||||
|
||||
LogResponseCode(span, recorder.Status())
|
||||
span.Finish()
|
||||
}
|
||||
|
||||
// generateEntryPointSpanName will return a Span name of an appropriate lenth based on the 'spanLimit' argument. If needed, it will be truncated, but will not be less than 24 characters.
|
||||
func generateEntryPointSpanName(r *http.Request, entryPoint string, spanLimit int) string {
|
||||
name := fmt.Sprintf("Entrypoint %s %s", entryPoint, r.Host)
|
||||
|
||||
if spanLimit > 0 && len(name) > spanLimit {
|
||||
if spanLimit < EntryPointMaxLengthNumber {
|
||||
log.Warnf("SpanNameLimit is set to be less than required static number of characters, defaulting to %d + 3", EntryPointMaxLengthNumber)
|
||||
spanLimit = EntryPointMaxLengthNumber + 3
|
||||
}
|
||||
hash := computeHash(name)
|
||||
limit := (spanLimit - EntryPointMaxLengthNumber) / 2
|
||||
name = fmt.Sprintf("Entrypoint %s %s %s", truncateString(entryPoint, limit), truncateString(r.Host, limit), hash)
|
||||
return &entryPointMiddleware{
|
||||
entryPoint: entryPointName,
|
||||
Tracing: t,
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
||||
type entryPointMiddleware struct {
|
||||
*tracing.Tracing
|
||||
entryPoint string
|
||||
next http.Handler
|
||||
}
|
||||
|
||||
func (e *entryPointMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
spanCtx, _ := e.Extract(opentracing.HTTPHeaders, tracing.HTTPHeadersCarrier(req.Header))
|
||||
|
||||
span, req, finish := e.StartSpanf(req, ext.SpanKindRPCServerEnum, "EntryPoint", []string{e.entryPoint, req.Host}, " ", ext.RPCServerOption(spanCtx))
|
||||
defer finish()
|
||||
|
||||
ext.Component.Set(span, e.ServiceName)
|
||||
tracing.LogRequest(span, req)
|
||||
|
||||
req = req.WithContext(tracing.WithTracing(req.Context(), e.Tracing))
|
||||
|
||||
recorder := newStatusCodeRecoder(rw, http.StatusOK)
|
||||
e.next.ServeHTTP(recorder, req)
|
||||
|
||||
tracing.LogResponseCode(span, recorder.Status())
|
||||
}
|
||||
|
||||
// WrapEntryPointHandler Wraps tracing to alice.Constructor.
|
||||
func WrapEntryPointHandler(ctx context.Context, tracer *tracing.Tracing, entryPointName string) alice.Constructor {
|
||||
return func(next http.Handler) (http.Handler, error) {
|
||||
return NewEntryPoint(ctx, tracer, entryPointName, next), nil
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
|
|
@ -1,70 +1,87 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/tracing"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEntryPointMiddlewareServeHTTP(t *testing.T) {
|
||||
expectedTags := map[string]interface{}{
|
||||
"span.kind": ext.SpanKindRPCServerEnum,
|
||||
"http.method": "GET",
|
||||
"component": "",
|
||||
"http.url": "http://www.test.com",
|
||||
"http.host": "www.test.com",
|
||||
func TestEntryPointMiddleware(t *testing.T) {
|
||||
type expected struct {
|
||||
Tags map[string]interface{}
|
||||
OperationName string
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
entryPoint string
|
||||
tracing *Tracing
|
||||
expectedTags map[string]interface{}
|
||||
expectedName string
|
||||
desc string
|
||||
entryPoint string
|
||||
spanNameLimit int
|
||||
tracing *trackingBackenMock
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
desc: "no truncation test",
|
||||
entryPoint: "test",
|
||||
tracing: &Tracing{
|
||||
SpanNameLimit: 0,
|
||||
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
|
||||
desc: "no truncation test",
|
||||
entryPoint: "test",
|
||||
spanNameLimit: 0,
|
||||
tracing: &trackingBackenMock{
|
||||
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
|
||||
},
|
||||
expectedTags: expectedTags,
|
||||
expectedName: "Entrypoint test www.test.com",
|
||||
}, {
|
||||
desc: "basic test",
|
||||
entryPoint: "test",
|
||||
tracing: &Tracing{
|
||||
SpanNameLimit: 25,
|
||||
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
|
||||
expected: expected{
|
||||
Tags: map[string]interface{}{
|
||||
"span.kind": ext.SpanKindRPCServerEnum,
|
||||
"http.method": http.MethodGet,
|
||||
"component": "",
|
||||
"http.url": "http://www.test.com",
|
||||
"http.host": "www.test.com",
|
||||
},
|
||||
OperationName: "EntryPoint test www.test.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "basic test",
|
||||
entryPoint: "test",
|
||||
spanNameLimit: 25,
|
||||
tracing: &trackingBackenMock{
|
||||
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
|
||||
},
|
||||
expected: expected{
|
||||
Tags: map[string]interface{}{
|
||||
"span.kind": ext.SpanKindRPCServerEnum,
|
||||
"http.method": http.MethodGet,
|
||||
"component": "",
|
||||
"http.url": "http://www.test.com",
|
||||
"http.host": "www.test.com",
|
||||
},
|
||||
OperationName: "EntryPoint te... ww... 0c15301b",
|
||||
},
|
||||
expectedTags: expectedTags,
|
||||
expectedName: "Entrypoint te... ww... 39b97e58",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := &entryPointMiddleware{
|
||||
entryPoint: test.entryPoint,
|
||||
Tracing: test.tracing,
|
||||
}
|
||||
newTracing, err := tracing.NewTracing("", test.spanNameLimit, test.tracing)
|
||||
require.NoError(t, err)
|
||||
|
||||
next := func(http.ResponseWriter, *http.Request) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://www.test.com", nil)
|
||||
rw := httptest.NewRecorder()
|
||||
|
||||
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
span := test.tracing.tracer.(*MockTracer).Span
|
||||
|
||||
actual := span.Tags
|
||||
assert.Equal(t, test.expectedTags, actual)
|
||||
assert.Equal(t, test.expectedName, span.OpName)
|
||||
}
|
||||
tags := span.Tags
|
||||
assert.Equal(t, test.expected.Tags, tags)
|
||||
assert.Equal(t, test.expected.OperationName, span.OpName)
|
||||
})
|
||||
|
||||
e.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "http://www.test.com", nil), next)
|
||||
handler := NewEntryPoint(context.Background(), newTracing, test.entryPoint, next)
|
||||
handler.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,63 +1,58 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/tracing"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/urfave/negroni"
|
||||
)
|
||||
|
||||
const (
|
||||
forwarderTypeName = "TracingForwarder"
|
||||
)
|
||||
|
||||
type forwarderMiddleware struct {
|
||||
frontend string
|
||||
backend string
|
||||
opName string
|
||||
*Tracing
|
||||
router string
|
||||
service string
|
||||
next http.Handler
|
||||
}
|
||||
|
||||
// NewForwarderMiddleware creates a new forwarder middleware that traces the outgoing request
|
||||
func (t *Tracing) NewForwarderMiddleware(frontend, backend string) negroni.Handler {
|
||||
log.Debugf("Added outgoing tracing middleware %s", frontend)
|
||||
// NewForwarder creates a new forwarder middleware that traces the outgoing request.
|
||||
func NewForwarder(ctx context.Context, router, service string, next http.Handler) http.Handler {
|
||||
middlewares.GetLogger(ctx, "tracing", forwarderTypeName).
|
||||
Debugf("Added outgoing tracing middleware %s", service)
|
||||
|
||||
return &forwarderMiddleware{
|
||||
Tracing: t,
|
||||
frontend: frontend,
|
||||
backend: backend,
|
||||
opName: generateForwardSpanName(frontend, backend, t.SpanNameLimit),
|
||||
router: router,
|
||||
service: service,
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *forwarderMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
span, r, finish := StartSpan(r, f.opName, true)
|
||||
func (f *forwarderMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
tr, err := tracing.FromContext(req.Context())
|
||||
if err != nil {
|
||||
f.next.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
opParts := []string{f.service, f.router}
|
||||
span, req, finish := tr.StartSpanf(req, ext.SpanKindRPCClientEnum, "forward", opParts, "/")
|
||||
defer finish()
|
||||
span.SetTag("frontend.name", f.frontend)
|
||||
span.SetTag("backend.name", f.backend)
|
||||
ext.HTTPMethod.Set(span, r.Method)
|
||||
ext.HTTPUrl.Set(span, fmt.Sprintf("%s%s", r.URL.String(), r.RequestURI))
|
||||
span.SetTag("http.host", r.Host)
|
||||
|
||||
InjectRequestHeaders(r)
|
||||
span.SetTag("service.name", f.service)
|
||||
span.SetTag("router.name", f.router)
|
||||
ext.HTTPMethod.Set(span, req.Method)
|
||||
ext.HTTPUrl.Set(span, req.URL.String())
|
||||
span.SetTag("http.host", req.Host)
|
||||
|
||||
recorder := newStatusCodeRecoder(w, 200)
|
||||
tracing.InjectRequestHeaders(req)
|
||||
|
||||
next(recorder, r)
|
||||
recorder := newStatusCodeRecoder(rw, 200)
|
||||
|
||||
LogResponseCode(span, recorder.Status())
|
||||
}
|
||||
|
||||
// generateForwardSpanName will return a Span name of an appropriate lenth based on the 'spanLimit' argument. If needed, it will be truncated, but will not be less than 21 characters
|
||||
func generateForwardSpanName(frontend, backend string, spanLimit int) string {
|
||||
name := fmt.Sprintf("forward %s/%s", frontend, backend)
|
||||
|
||||
if spanLimit > 0 && len(name) > spanLimit {
|
||||
if spanLimit < ForwardMaxLengthNumber {
|
||||
log.Warnf("SpanNameLimit is set to be less than required static number of characters, defaulting to %d + 3", ForwardMaxLengthNumber)
|
||||
spanLimit = ForwardMaxLengthNumber + 3
|
||||
}
|
||||
hash := computeHash(name)
|
||||
limit := (spanLimit - ForwardMaxLengthNumber) / 2
|
||||
name = fmt.Sprintf("forward %s/%s/%s", truncateString(frontend, limit), truncateString(backend, limit), hash)
|
||||
}
|
||||
|
||||
return name
|
||||
f.next.ServeHTTP(recorder, req)
|
||||
|
||||
tracing.LogResponseCode(span, recorder.Status())
|
||||
}
|
||||
|
|
|
@ -1,93 +1,136 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/tracing"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTracingNewForwarderMiddleware(t *testing.T) {
|
||||
func TestNewForwarder(t *testing.T) {
|
||||
type expected struct {
|
||||
Tags map[string]interface{}
|
||||
OperationName string
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
tracer *Tracing
|
||||
frontend string
|
||||
backend string
|
||||
expected *forwarderMiddleware
|
||||
desc string
|
||||
spanNameLimit int
|
||||
tracing *trackingBackenMock
|
||||
service string
|
||||
router string
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
desc: "Simple Forward Tracer without truncation and hashing",
|
||||
tracer: &Tracing{
|
||||
SpanNameLimit: 101,
|
||||
desc: "Simple Forward Tracer without truncation and hashing",
|
||||
spanNameLimit: 101,
|
||||
tracing: &trackingBackenMock{
|
||||
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
|
||||
},
|
||||
frontend: "some-service.domain.tld",
|
||||
backend: "some-service.domain.tld",
|
||||
expected: &forwarderMiddleware{
|
||||
Tracing: &Tracing{
|
||||
SpanNameLimit: 101,
|
||||
service: "some-service.domain.tld",
|
||||
router: "some-service.domain.tld",
|
||||
expected: expected{
|
||||
Tags: map[string]interface{}{
|
||||
"http.host": "www.test.com",
|
||||
"http.method": "GET",
|
||||
"http.url": "http://www.test.com/toto",
|
||||
"service.name": "some-service.domain.tld",
|
||||
"router.name": "some-service.domain.tld",
|
||||
"span.kind": ext.SpanKindRPCClientEnum,
|
||||
},
|
||||
frontend: "some-service.domain.tld",
|
||||
backend: "some-service.domain.tld",
|
||||
opName: "forward some-service.domain.tld/some-service.domain.tld",
|
||||
OperationName: "forward some-service.domain.tld/some-service.domain.tld",
|
||||
},
|
||||
}, {
|
||||
desc: "Simple Forward Tracer with truncation and hashing",
|
||||
tracer: &Tracing{
|
||||
SpanNameLimit: 101,
|
||||
desc: "Simple Forward Tracer with truncation and hashing",
|
||||
spanNameLimit: 101,
|
||||
tracing: &trackingBackenMock{
|
||||
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
|
||||
},
|
||||
frontend: "some-service-100.slug.namespace.environment.domain.tld",
|
||||
backend: "some-service-100.slug.namespace.environment.domain.tld",
|
||||
expected: &forwarderMiddleware{
|
||||
Tracing: &Tracing{
|
||||
SpanNameLimit: 101,
|
||||
service: "some-service-100.slug.namespace.environment.domain.tld",
|
||||
router: "some-service-100.slug.namespace.environment.domain.tld",
|
||||
expected: expected{
|
||||
Tags: map[string]interface{}{
|
||||
"http.host": "www.test.com",
|
||||
"http.method": "GET",
|
||||
"http.url": "http://www.test.com/toto",
|
||||
"service.name": "some-service-100.slug.namespace.environment.domain.tld",
|
||||
"router.name": "some-service-100.slug.namespace.environment.domain.tld",
|
||||
"span.kind": ext.SpanKindRPCClientEnum,
|
||||
},
|
||||
frontend: "some-service-100.slug.namespace.environment.domain.tld",
|
||||
backend: "some-service-100.slug.namespace.environment.domain.tld",
|
||||
opName: "forward some-service-100.slug.namespace.enviro.../some-service-100.slug.namespace.enviro.../bc4a0d48",
|
||||
OperationName: "forward some-service-100.slug.namespace.enviro.../some-service-100.slug.namespace.enviro.../bc4a0d48",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Exactly 101 chars",
|
||||
tracer: &Tracing{
|
||||
SpanNameLimit: 101,
|
||||
desc: "Exactly 101 chars",
|
||||
spanNameLimit: 101,
|
||||
tracing: &trackingBackenMock{
|
||||
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
|
||||
},
|
||||
frontend: "some-service1.namespace.environment.domain.tld",
|
||||
backend: "some-service1.namespace.environment.domain.tld",
|
||||
expected: &forwarderMiddleware{
|
||||
Tracing: &Tracing{
|
||||
SpanNameLimit: 101,
|
||||
service: "some-service1.namespace.environment.domain.tld",
|
||||
router: "some-service1.namespace.environment.domain.tld",
|
||||
expected: expected{
|
||||
Tags: map[string]interface{}{
|
||||
"http.host": "www.test.com",
|
||||
"http.method": "GET",
|
||||
"http.url": "http://www.test.com/toto",
|
||||
"service.name": "some-service1.namespace.environment.domain.tld",
|
||||
"router.name": "some-service1.namespace.environment.domain.tld",
|
||||
"span.kind": ext.SpanKindRPCClientEnum,
|
||||
},
|
||||
frontend: "some-service1.namespace.environment.domain.tld",
|
||||
backend: "some-service1.namespace.environment.domain.tld",
|
||||
opName: "forward some-service1.namespace.environment.domain.tld/some-service1.namespace.environment.domain.tld",
|
||||
OperationName: "forward some-service1.namespace.environment.domain.tld/some-service1.namespace.environment.domain.tld",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "More than 101 chars",
|
||||
tracer: &Tracing{
|
||||
SpanNameLimit: 101,
|
||||
desc: "More than 101 chars",
|
||||
spanNameLimit: 101,
|
||||
tracing: &trackingBackenMock{
|
||||
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
|
||||
},
|
||||
frontend: "some-service1.frontend.namespace.environment.domain.tld",
|
||||
backend: "some-service1.backend.namespace.environment.domain.tld",
|
||||
expected: &forwarderMiddleware{
|
||||
Tracing: &Tracing{
|
||||
SpanNameLimit: 101,
|
||||
service: "some-service1.frontend.namespace.environment.domain.tld",
|
||||
router: "some-service1.backend.namespace.environment.domain.tld",
|
||||
expected: expected{
|
||||
Tags: map[string]interface{}{
|
||||
"http.host": "www.test.com",
|
||||
"http.method": "GET",
|
||||
"http.url": "http://www.test.com/toto",
|
||||
"service.name": "some-service1.frontend.namespace.environment.domain.tld",
|
||||
"router.name": "some-service1.backend.namespace.environment.domain.tld",
|
||||
"span.kind": ext.SpanKindRPCClientEnum,
|
||||
},
|
||||
frontend: "some-service1.frontend.namespace.environment.domain.tld",
|
||||
backend: "some-service1.backend.namespace.environment.domain.tld",
|
||||
opName: "forward some-service1.frontend.namespace.envir.../some-service1.backend.namespace.enviro.../fa49dd23",
|
||||
OperationName: "forward some-service1.frontend.namespace.envir.../some-service1.backend.namespace.enviro.../fa49dd23",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := test.tracer.NewForwarderMiddleware(test.frontend, test.backend)
|
||||
newTracing, err := tracing.NewTracing("", test.spanNameLimit, test.tracing)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
assert.True(t, len(test.expected.opName) <= test.tracer.SpanNameLimit)
|
||||
req := httptest.NewRequest(http.MethodGet, "http://www.test.com/toto", nil)
|
||||
req = req.WithContext(tracing.WithTracing(req.Context(), newTracing))
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
|
||||
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
span := test.tracing.tracer.(*MockTracer).Span
|
||||
|
||||
tags := span.Tags
|
||||
assert.Equal(t, test.expected.Tags, tags)
|
||||
assert.True(t, len(test.expected.OperationName) <= test.spanNameLimit,
|
||||
"the len of the operation name %q [len: %d] doesn't respect limit %d",
|
||||
test.expected.OperationName, len(test.expected.OperationName), test.spanNameLimit)
|
||||
assert.Equal(t, test.expected.OperationName, span.OpName)
|
||||
})
|
||||
|
||||
handler := NewForwarder(context.Background(), test.router, test.service, next)
|
||||
handler.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package jaeger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
jaegercfg "github.com/uber/jaeger-client-go/config"
|
||||
"github.com/uber/jaeger-client-go/zipkin"
|
||||
jaegermet "github.com/uber/jaeger-lib/metrics"
|
||||
)
|
||||
|
||||
// Name sets the name of this tracer
|
||||
const Name = "jaeger"
|
||||
|
||||
// Config provides configuration settings for a jaeger tracer
|
||||
type Config struct {
|
||||
SamplingServerURL string `description:"set the sampling server url." export:"false"`
|
||||
SamplingType string `description:"set the sampling type." export:"true"`
|
||||
SamplingParam float64 `description:"set the sampling parameter." export:"true"`
|
||||
LocalAgentHostPort string `description:"set jaeger-agent's host:port that the reporter will used." export:"false"`
|
||||
Gen128Bit bool `description:"generate 128 bit span IDs." export:"true"`
|
||||
Propagation string `description:"which propgation format to use (jaeger/b3)." export:"true"`
|
||||
}
|
||||
|
||||
// Setup sets up the tracer
|
||||
func (c *Config) Setup(componentName string) (opentracing.Tracer, io.Closer, error) {
|
||||
jcfg := jaegercfg.Configuration{
|
||||
Sampler: &jaegercfg.SamplerConfig{
|
||||
SamplingServerURL: c.SamplingServerURL,
|
||||
Type: c.SamplingType,
|
||||
Param: c.SamplingParam,
|
||||
},
|
||||
Reporter: &jaegercfg.ReporterConfig{
|
||||
LogSpans: true,
|
||||
LocalAgentHostPort: c.LocalAgentHostPort,
|
||||
},
|
||||
}
|
||||
|
||||
jMetricsFactory := jaegermet.NullFactory
|
||||
|
||||
opts := []jaegercfg.Option{
|
||||
jaegercfg.Logger(&jaegerLogger{}),
|
||||
jaegercfg.Metrics(jMetricsFactory),
|
||||
jaegercfg.Gen128Bit(c.Gen128Bit),
|
||||
}
|
||||
|
||||
switch c.Propagation {
|
||||
case "b3":
|
||||
p := zipkin.NewZipkinB3HTTPHeaderPropagator()
|
||||
opts = append(opts,
|
||||
jaegercfg.Injector(opentracing.HTTPHeaders, p),
|
||||
jaegercfg.Extractor(opentracing.HTTPHeaders, p),
|
||||
)
|
||||
case "jaeger", "":
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unknown propagation format: %s", c.Propagation)
|
||||
}
|
||||
|
||||
// Initialize tracer with a logger and a metrics factory
|
||||
closer, err := jcfg.InitGlobalTracer(
|
||||
componentName,
|
||||
opts...,
|
||||
)
|
||||
if err != nil {
|
||||
log.Warnf("Could not initialize jaeger tracer: %s", err.Error())
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Debug("Jaeger tracer configured")
|
||||
|
||||
return opentracing.GlobalTracer(), closer, nil
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package jaeger
|
||||
|
||||
import "github.com/containous/traefik/log"
|
||||
|
||||
// jaegerLogger is an implementation of the Logger interface that delegates to traefik log
|
||||
type jaegerLogger struct{}
|
||||
|
||||
func (l *jaegerLogger) Error(msg string) {
|
||||
log.Errorf("Tracing jaeger error: %s", msg)
|
||||
}
|
||||
|
||||
// Infof logs a message at debug priority
|
||||
func (l *jaegerLogger) Infof(msg string, args ...interface{}) {
|
||||
log.Debugf(msg, args...)
|
||||
}
|
|
@ -1,29 +1,43 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"io"
|
||||
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type MockTracer struct {
|
||||
Span *MockSpan
|
||||
}
|
||||
|
||||
// StartSpan belongs to the Tracer interface.
|
||||
func (n MockTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
|
||||
n.Span.OpName = operationName
|
||||
return n.Span
|
||||
}
|
||||
|
||||
// Inject belongs to the Tracer interface.
|
||||
func (n MockTracer) Inject(sp opentracing.SpanContext, format interface{}, carrier interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract belongs to the Tracer interface.
|
||||
func (n MockTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
|
||||
return nil, opentracing.ErrSpanContextNotFound
|
||||
}
|
||||
|
||||
// MockSpanContext
|
||||
type MockSpanContext struct{}
|
||||
|
||||
func (n MockSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {}
|
||||
|
||||
// MockSpan
|
||||
type MockSpan struct {
|
||||
OpName string
|
||||
Tags map[string]interface{}
|
||||
}
|
||||
|
||||
type MockSpanContext struct {
|
||||
}
|
||||
|
||||
// MockSpanContext:
|
||||
func (n MockSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {}
|
||||
|
||||
// MockSpan:
|
||||
func (n MockSpan) Context() opentracing.SpanContext { return MockSpanContext{} }
|
||||
func (n MockSpan) SetBaggageItem(key, val string) opentracing.Span {
|
||||
return MockSpan{Tags: make(map[string]interface{})}
|
||||
|
@ -46,88 +60,11 @@ func (n MockSpan) Reset() {
|
|||
n.Tags = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// StartSpan belongs to the Tracer interface.
|
||||
func (n MockTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
|
||||
n.Span.OpName = operationName
|
||||
return n.Span
|
||||
type trackingBackenMock struct {
|
||||
tracer opentracing.Tracer
|
||||
}
|
||||
|
||||
// Inject belongs to the Tracer interface.
|
||||
func (n MockTracer) Inject(sp opentracing.SpanContext, format interface{}, carrier interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract belongs to the Tracer interface.
|
||||
func (n MockTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
|
||||
return nil, opentracing.ErrSpanContextNotFound
|
||||
}
|
||||
|
||||
func TestTruncateString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
text string
|
||||
limit int
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "short text less than limit 10",
|
||||
text: "short",
|
||||
limit: 10,
|
||||
expected: "short",
|
||||
},
|
||||
{
|
||||
desc: "basic truncate with limit 10",
|
||||
text: "some very long pice of text",
|
||||
limit: 10,
|
||||
expected: "some ve...",
|
||||
},
|
||||
{
|
||||
desc: "truncate long FQDN to 39 chars",
|
||||
text: "some-service-100.slug.namespace.environment.domain.tld",
|
||||
limit: 39,
|
||||
expected: "some-service-100.slug.namespace.envi...",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := truncateString(test.text, test.limit)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
assert.True(t, len(actual) <= test.limit)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeHash(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
text string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "hashing",
|
||||
text: "some very long pice of text",
|
||||
expected: "0258ea1c",
|
||||
},
|
||||
{
|
||||
desc: "short text less than limit 10",
|
||||
text: "short",
|
||||
expected: "f9b0078b",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := computeHash(test.text)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
func (t *trackingBackenMock) Setup(componentName string) (opentracing.Tracer, io.Closer, error) {
|
||||
opentracing.SetGlobalTracer(t.tracer)
|
||||
return t.tracer, nil, nil
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/middlewares/tracing/datadog"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
)
|
||||
|
||||
// ForwardMaxLengthNumber defines the number of static characters in the Forwarding Span Trace name : 8 chars for 'forward ' + 8 chars for hash + 2 chars for '_'.
|
||||
const ForwardMaxLengthNumber = 18
|
||||
|
||||
// EntryPointMaxLengthNumber defines the number of static characters in the Entrypoint Span Trace name : 11 chars for 'Entrypoint ' + 8 chars for hash + 2 chars for '_'.
|
||||
const EntryPointMaxLengthNumber = 21
|
||||
|
||||
// TraceNameHashLength defines the number of characters to use from the head of the generated hash.
|
||||
const TraceNameHashLength = 8
|
||||
|
||||
// Tracing middleware
|
||||
type Tracing struct {
|
||||
Backend string `description:"Selects the tracking backend ('jaeger','zipkin', 'datadog')." export:"true"`
|
||||
ServiceName string `description:"Set the name for this service" export:"true"`
|
||||
SpanNameLimit int `description:"Set the maximum character limit for Span names (default 0 = no limit)" export:"true"`
|
||||
Jaeger *jaeger.Config `description:"Settings for jaeger"`
|
||||
Zipkin *zipkin.Config `description:"Settings for zipkin"`
|
||||
DataDog *datadog.Config `description:"Settings for DataDog"`
|
||||
|
||||
tracer opentracing.Tracer
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
// StartSpan delegates to opentracing.Tracer
|
||||
func (t *Tracing) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
|
||||
return t.tracer.StartSpan(operationName, opts...)
|
||||
}
|
||||
|
||||
// Inject delegates to opentracing.Tracer
|
||||
func (t *Tracing) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error {
|
||||
return t.tracer.Inject(sm, format, carrier)
|
||||
}
|
||||
|
||||
// Extract delegates to opentracing.Tracer
|
||||
func (t *Tracing) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
|
||||
return t.tracer.Extract(format, carrier)
|
||||
}
|
||||
|
||||
// Backend describes things we can use to setup tracing
|
||||
type Backend interface {
|
||||
Setup(serviceName string) (opentracing.Tracer, io.Closer, error)
|
||||
}
|
||||
|
||||
// Setup Tracing middleware
|
||||
func (t *Tracing) Setup() {
|
||||
var err error
|
||||
|
||||
switch t.Backend {
|
||||
case jaeger.Name:
|
||||
t.tracer, t.closer, err = t.Jaeger.Setup(t.ServiceName)
|
||||
case zipkin.Name:
|
||||
t.tracer, t.closer, err = t.Zipkin.Setup(t.ServiceName)
|
||||
case datadog.Name:
|
||||
t.tracer, t.closer, err = t.DataDog.Setup(t.ServiceName)
|
||||
default:
|
||||
log.Warnf("Unknown tracer %q", t.Backend)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Could not initialize %s tracing: %v", t.Backend, err)
|
||||
}
|
||||
}
|
||||
|
||||
// IsEnabled determines if tracing was successfully activated
|
||||
func (t *Tracing) IsEnabled() bool {
|
||||
if t == nil || t.tracer == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Close tracer
|
||||
func (t *Tracing) Close() {
|
||||
if t.closer != nil {
|
||||
err := t.closer.Close()
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogRequest used to create span tags from the request
|
||||
func LogRequest(span opentracing.Span, r *http.Request) {
|
||||
if span != nil && r != nil {
|
||||
ext.HTTPMethod.Set(span, r.Method)
|
||||
ext.HTTPUrl.Set(span, r.URL.String())
|
||||
span.SetTag("http.host", r.Host)
|
||||
}
|
||||
}
|
||||
|
||||
// LogResponseCode used to log response code in span
|
||||
func LogResponseCode(span opentracing.Span, code int) {
|
||||
if span != nil {
|
||||
ext.HTTPStatusCode.Set(span, uint16(code))
|
||||
if code >= 400 {
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSpan used to retrieve span from request context
|
||||
func GetSpan(r *http.Request) opentracing.Span {
|
||||
return opentracing.SpanFromContext(r.Context())
|
||||
}
|
||||
|
||||
// InjectRequestHeaders used to inject OpenTracing headers into the request
|
||||
func InjectRequestHeaders(r *http.Request) {
|
||||
if span := GetSpan(r); span != nil {
|
||||
err := opentracing.GlobalTracer().Inject(
|
||||
span.Context(),
|
||||
opentracing.HTTPHeaders,
|
||||
HTTPHeadersCarrier(r.Header))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogEventf logs an event to the span in the request context.
|
||||
func LogEventf(r *http.Request, format string, args ...interface{}) {
|
||||
if span := GetSpan(r); span != nil {
|
||||
span.LogKV("event", fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
// StartSpan starts a new span from the one in the request context
|
||||
func StartSpan(r *http.Request, operationName string, spanKinClient bool, opts ...opentracing.StartSpanOption) (opentracing.Span, *http.Request, func()) {
|
||||
span, ctx := opentracing.StartSpanFromContext(r.Context(), operationName, opts...)
|
||||
if spanKinClient {
|
||||
ext.SpanKindRPCClient.Set(span)
|
||||
}
|
||||
r = r.WithContext(ctx)
|
||||
return span, r, func() {
|
||||
span.Finish()
|
||||
}
|
||||
}
|
||||
|
||||
// SetError flags the span associated with this request as in error
|
||||
func SetError(r *http.Request) {
|
||||
if span := GetSpan(r); span != nil {
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
}
|
||||
|
||||
// SetErrorAndDebugLog flags the span associated with this request as in error and create a debug log.
|
||||
func SetErrorAndDebugLog(r *http.Request, format string, args ...interface{}) {
|
||||
SetError(r)
|
||||
log.Debugf(format, args...)
|
||||
LogEventf(r, format, args...)
|
||||
}
|
||||
|
||||
// SetErrorAndWarnLog flags the span associated with this request as in error and create a debug log.
|
||||
func SetErrorAndWarnLog(r *http.Request, format string, args ...interface{}) {
|
||||
SetError(r)
|
||||
log.Warnf(format, args...)
|
||||
LogEventf(r, format, args...)
|
||||
}
|
||||
|
||||
// truncateString reduces the length of the 'str' argument to 'num' - 3 and adds a '...' suffix to the tail.
|
||||
func truncateString(str string, num int) string {
|
||||
text := str
|
||||
if len(str) > num {
|
||||
if num > 3 {
|
||||
num -= 3
|
||||
}
|
||||
text = str[0:num] + "..."
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// computeHash returns the first TraceNameHashLength character of the sha256 hash for 'name' argument.
|
||||
func computeHash(name string) string {
|
||||
data := []byte(name)
|
||||
hash := sha256.New()
|
||||
if _, err := hash.Write(data); err != nil {
|
||||
// Impossible case
|
||||
log.Errorf("Fail to create Span name hash for %s: %v", name, err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", hash.Sum(nil))[:TraceNameHashLength]
|
||||
}
|
|
@ -1,66 +1,68 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/urfave/negroni"
|
||||
"github.com/containous/alice"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/tracing"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
)
|
||||
|
||||
// NewNegroniHandlerWrapper return a negroni.Handler struct
|
||||
func (t *Tracing) NewNegroniHandlerWrapper(name string, handler negroni.Handler, clientSpanKind bool) negroni.Handler {
|
||||
if t.IsEnabled() && handler != nil {
|
||||
return &NegroniHandlerWrapper{
|
||||
name: name,
|
||||
next: handler,
|
||||
clientSpanKind: clientSpanKind,
|
||||
// Tracable embeds tracing information.
|
||||
type Tracable interface {
|
||||
GetTracingInformation() (name string, spanKind ext.SpanKindEnum)
|
||||
}
|
||||
|
||||
// Wrap adds tracability to an alice.Constructor.
|
||||
func Wrap(ctx context.Context, constructor alice.Constructor) alice.Constructor {
|
||||
return func(next http.Handler) (http.Handler, error) {
|
||||
if constructor == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
// NewHTTPHandlerWrapper return a http.Handler struct
|
||||
func (t *Tracing) NewHTTPHandlerWrapper(name string, handler http.Handler, clientSpanKind bool) http.Handler {
|
||||
if t.IsEnabled() && handler != nil {
|
||||
return &HTTPHandlerWrapper{
|
||||
name: name,
|
||||
handler: handler,
|
||||
clientSpanKind: clientSpanKind,
|
||||
handler, err := constructor(next)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tracableHandler, ok := handler.(Tracable); ok {
|
||||
name, spanKind := tracableHandler.GetTracingInformation()
|
||||
log.FromContext(ctx).WithField(log.MiddlewareName, name).Debug("Adding tracing to middleware")
|
||||
return NewWrapper(handler, name, spanKind), nil
|
||||
}
|
||||
return handler, nil
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
// NegroniHandlerWrapper is used to wrap negroni handler middleware
|
||||
type NegroniHandlerWrapper struct {
|
||||
name string
|
||||
next negroni.Handler
|
||||
clientSpanKind bool
|
||||
// NewWrapper returns a http.Handler struct
|
||||
func NewWrapper(next http.Handler, name string, spanKind ext.SpanKindEnum) http.Handler {
|
||||
return &Wrapper{
|
||||
next: next,
|
||||
name: name,
|
||||
spanKind: spanKind,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NegroniHandlerWrapper) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
// Wrapper is used to wrap http handler middleware.
|
||||
type Wrapper struct {
|
||||
next http.Handler
|
||||
name string
|
||||
spanKind ext.SpanKindEnum
|
||||
}
|
||||
|
||||
func (w *Wrapper) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
_, err := tracing.FromContext(req.Context())
|
||||
if err != nil {
|
||||
w.next.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
var finish func()
|
||||
_, r, finish = StartSpan(r, t.name, t.clientSpanKind)
|
||||
_, req, finish = tracing.StartSpan(req, w.name, w.spanKind)
|
||||
defer finish()
|
||||
|
||||
if t.next != nil {
|
||||
t.next.ServeHTTP(rw, r, next)
|
||||
if w.next != nil {
|
||||
w.next.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPHandlerWrapper is used to wrap http handler middleware
|
||||
type HTTPHandlerWrapper struct {
|
||||
name string
|
||||
handler http.Handler
|
||||
clientSpanKind bool
|
||||
}
|
||||
|
||||
func (t *HTTPHandlerWrapper) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
var finish func()
|
||||
_, r, finish = StartSpan(r, t.name, t.clientSpanKind)
|
||||
defer finish()
|
||||
|
||||
if t.handler != nil {
|
||||
t.handler.ServeHTTP(rw, r)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
package zipkin
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
zipkin "github.com/openzipkin/zipkin-go-opentracing"
|
||||
)
|
||||
|
||||
// Name sets the name of this tracer
|
||||
const Name = "zipkin"
|
||||
|
||||
// Config provides configuration settings for a zipkin tracer
|
||||
type Config struct {
|
||||
HTTPEndpoint string `description:"HTTP Endpoint to report traces to." export:"false"`
|
||||
SameSpan bool `description:"Use Zipkin SameSpan RPC style traces." export:"true"`
|
||||
ID128Bit bool `description:"Use Zipkin 128 bit root span IDs." export:"true"`
|
||||
Debug bool `description:"Enable Zipkin debug." export:"true"`
|
||||
SampleRate float64 `description:"The rate between 0.0 and 1.0 of requests to trace." export:"true"`
|
||||
}
|
||||
|
||||
// Setup sets up the tracer
|
||||
func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error) {
|
||||
collector, err := zipkin.NewHTTPCollector(c.HTTPEndpoint)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
recorder := zipkin.NewRecorder(collector, c.Debug, "0.0.0.0:0", serviceName)
|
||||
tracer, err := zipkin.NewTracer(
|
||||
recorder,
|
||||
zipkin.ClientServerSameSpan(c.SameSpan),
|
||||
zipkin.TraceID128Bit(c.ID128Bit),
|
||||
zipkin.DebugMode(c.Debug),
|
||||
zipkin.WithSampler(zipkin.NewBoundarySampler(c.SampleRate, time.Now().Unix())),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Without this, child spans are getting the NOOP tracer
|
||||
opentracing.SetGlobalTracer(tracer)
|
||||
|
||||
log.Debug("Zipkin tracer configured")
|
||||
|
||||
return tracer, collector, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue