Skip to content

Commit

Permalink
Add HTTP method and path to trace span operation name (fabiolb#715)
Browse files Browse the repository at this point in the history
* Add HTTP method and path to span operation name

Also, set HTTP method and URL span tags.

* Add tracing span name option as template
  • Loading branch information
hobochili committed Jan 29, 2020
1 parent 828d81f commit 15565de
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 9 deletions.
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ type Tracing struct {
Topic string
SamplerRate float64
SpanHost string
SpanName string
TraceID128Bit bool
}

Expand Down
1 change: 1 addition & 0 deletions config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ var defaultConfig = &Config{
Topic: "Fabiolb-Kafka-Topic",
SamplerRate: -1,
SpanHost: "localhost:9998",
SpanName: "",
TraceID128Bit: true,
},

Expand Down
1 change: 1 addition & 0 deletions config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ func load(cmdline, environ, envprefix []string, props *properties.Properties) (c
f.StringVar(&cfg.Tracing.CollectorType, "tracing.CollectorType", defaultConfig.Tracing.CollectorType, "OpenTrace Collector Type, one of [http, kafka]")
f.StringVar(&cfg.Tracing.ConnectString, "tracing.ConnectString", defaultConfig.Tracing.ConnectString, "OpenTrace Collector host:port")
f.StringVar(&cfg.Tracing.ServiceName, "tracing.ServiceName", defaultConfig.Tracing.ServiceName, "Service name to embed in OpenTrace span")
f.StringVar(&cfg.Tracing.SpanName, "tracing.SpanName", defaultConfig.Tracing.SpanName, "Span name template used to embed in OpenTrace span")
f.StringVar(&cfg.Tracing.Topic, "tracing.Topic", defaultConfig.Tracing.Topic, "OpenTrace Collector Kafka Topic")
f.Float64Var(&cfg.Tracing.SamplerRate, "tracing.SamplerRate", defaultConfig.Tracing.SamplerRate, "OpenTrace sample rate percentage in decimal form")
f.StringVar(&cfg.Tracing.SpanHost, "tracing.SpanHost", defaultConfig.Tracing.SpanHost, "Host:Port info to add to spans")
Expand Down
23 changes: 22 additions & 1 deletion fabio.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,28 @@
# tracing.ServiceName = Fabiolb


# tracing.SpanName configures the template used in reporting span information
#
# The value is expanded by the text/template package and provides
# the following variables:
#
# - Proto: the protocol version
# - Method: the HTTP method
# - Host: the host part of the URL
# - Scheme: the scheme of the requested URL
# - Path: the path of the requested URL
# - RawQuery: the encoded query values of the requested URL
#
# SpanName defaults to the value of tracing.ServiceName but can be
# overridden with this property.
#
# Example: tracing.SpanName = {{.Proto}} {{.Method}} {{.Path}}
#
# The default is
#
# tracing.SpanName =


# tracing.Topic sets the Topic String used if tracing.CollectorType is kafka and
# tracing.ConnectSting is set to a kafka broker
#
Expand All @@ -1313,4 +1335,3 @@
#
# The default is
# tracing.SpanHost = localhost:9998

4 changes: 2 additions & 2 deletions proxy/http_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type HTTPProxy struct {
// Logger is the access logger for the requests.
Logger logger.Logger

// TracerCfg is the Open Tracing configuration as provided during startup
// TracerCfg is the Open Tracing configuration as provided during startup
TracerCfg config.Tracing

// UUID returns a unique id in uuid format.
Expand All @@ -80,7 +80,7 @@ func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

//Create Span
span := trace.CreateSpan(r, p.TracerCfg.ServiceName)
span := trace.CreateSpan(r, &p.TracerCfg)
defer span.Finish()

t := p.Lookup(r)
Expand Down
36 changes: 33 additions & 3 deletions trace/trace.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package trace

import (
"bytes"
"fmt"
"log"
"net/http"
"os"
"strings"
"text/template"

"github.com/fabiolb/fabio/config"
opentracing "github.com/opentracing/opentracing-go"
Expand Down Expand Up @@ -63,18 +65,25 @@ func CreateTracer(recorder zipkin.SpanRecorder, samplerRate float64, traceID128B
return tracer
}

func CreateSpan(r *http.Request, serviceName string) opentracing.Span {
func CreateSpan(r *http.Request, cfg *config.Tracing) opentracing.Span {
globalTracer := opentracing.GlobalTracer()

name := cfg.ServiceName
if cfg.SpanName != "" {
name = spanName(cfg.SpanName, r)
}

// If headers contain trace data, create child span from parent; else, create root span
var span opentracing.Span
if globalTracer != nil {
spanCtx, err := globalTracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
if err != nil {
span = globalTracer.StartSpan(serviceName)
span = globalTracer.StartSpan(name)
} else {
span = globalTracer.StartSpan(serviceName, ext.RPCServerOption(spanCtx))
span = globalTracer.StartSpan(name, ext.RPCServerOption(spanCtx))
}
ext.HTTPMethod.Set(span, r.Method)
ext.HTTPUrl.Set(span, r.URL.String())
}

return span // caller must defer span.finish()
Expand All @@ -98,3 +107,24 @@ func InitializeTracer(traceConfig *config.Tracing) {
// Set the Zipkin Tracer created above to the GlobalTracer
opentracing.SetGlobalTracer(tracer)
}

// spanName returns the rendered span name from the configured template.
// If an error is encountered, it returns the unrendered template.
func spanName(tmplStr string, r *http.Request) string {
tmpl, err := template.New("name").Parse(tmplStr)
if err != nil {
return tmplStr
}

var name bytes.Buffer

data := struct {
Proto, Method, Host, Scheme, Path, RawQuery string
}{r.Proto, r.Method, r.Host, r.URL.Scheme, r.URL.Path, r.URL.RawQuery}

if err = tmpl.Execute(&name, data); err != nil {
return tmplStr
}

return name.String()
}
25 changes: 22 additions & 3 deletions trace/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestCreateSpanNoGlobalTracer(t *testing.T) {
opentracing.SetGlobalTracer(nil)
req, _ := http.NewRequest("GET", "http://example.com", nil)

if CreateSpan(req, testServiceName) != nil {
if CreateSpan(req, &config.Tracing{ServiceName: "fabiolb-test", SpanName: "{{.Method}} {{.Path}}"}) != nil {
t.Error("CreateSpan returned a non-nil result using a nil global tracer.")
t.Fail()
}
Expand All @@ -35,7 +35,7 @@ func TestCreateSpanWithNoParent(t *testing.T) {
opentracing.SetGlobalTracer(tracer)

req, _ := http.NewRequest("GET", "http://example.com", nil)
if CreateSpan(req, testServiceName) == nil {
if CreateSpan(req, &config.Tracing{ServiceName: "fabiolb-test", SpanName: "{{.Method}} {{.Path}}"}) == nil {
t.Error("Received nil span while a global tracer was set.")
t.FailNow()
}
Expand All @@ -53,7 +53,7 @@ func TestCreateSpanWithParent(t *testing.T) {
opentracing.HTTPHeadersCarrier(requestIn.Header),
)

if CreateSpan(requestIn, testServiceName+"-child") == nil {
if CreateSpan(requestIn, &config.Tracing{ServiceName: "fabiolb-test", SpanName: "{{.Method}} {{.Path}}"}) == nil {
t.Error("Received a nil span while a global tracer was set.")
t.FailNow()
}
Expand Down Expand Up @@ -128,3 +128,22 @@ func TestInjectHeadersWithParentSpan(t *testing.T) {
t.Fail()
}
}

func TestSpanName(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com/demo", nil)

if spanName("{{.Method}} {{.Host}} {{.Path}}", req) != "GET example.com /demo" {
t.Error("spanName did not properly render the supported template")
t.Fail()
}

if spanName("{{.Invalid", req) != "{{.Invalid" {
t.Error("spanName did not return the unrendered string of the invalid template")
t.Fail()
}

if spanName("{{.Unsupported}}", req) != "{{.Unsupported}}" {
t.Error("spanName did not return the unrendered string of the unsupported template")
t.Fail()
}
}

0 comments on commit 15565de

Please sign in to comment.