diff --git a/main.go b/main.go index 9d8b13bd5..48195d2cc 100644 --- a/main.go +++ b/main.go @@ -120,7 +120,7 @@ func main() { initRuntime(cfg) initBackend(cfg) //Init OpenTracing if Enabled in the Properties File Tracing.TracingEnabled - initOpenTracing(cfg) + trace.InitializeTracer(&cfg.Tracing) startAdmin(cfg) @@ -384,16 +384,6 @@ func initBackend(cfg *config.Config) { } } -// OpenTracing Init -func initOpenTracing(cfg *config.Config) { - // If fabio.properties file has tracing.TracingEnabled set to true the init tracing - if cfg.Tracing.TracingEnabled { - trace.InitializeTracer(cfg.Tracing.CollectorType, cfg.Tracing.ConnectString, cfg.Tracing.ServiceName, cfg.Tracing.Topic, cfg.Tracing.SamplerRate, cfg.Tracing.SpanHost) - - } - -} - func watchBackend(cfg *config.Config, first chan bool) { var ( last string diff --git a/trace/trace.go b/trace/trace.go index bed1a49ca..2b1cbf5f1 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -1,11 +1,13 @@ package trace import ( + "fmt" "log" "net/http" "os" "strings" + "github.com/fabiolb/fabio/config" opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" zipkin "github.com/openzipkin/zipkin-go-opentracing" @@ -20,24 +22,26 @@ func InjectHeaders(span opentracing.Span, req *http.Request) { ) } -func CreateCollector(collectorType string, connectString string, topic string) zipkin.Collector { +func CreateCollector(collectorType, connectString, topic string) zipkin.Collector { var collector zipkin.Collector var err error - if collectorType == "http" { + switch collectorType { + case "http": collector, err = zipkin.NewHTTPCollector(connectString) - } else if collectorType == "kafka" { + case "kafka": // TODO set logger? kafkaHosts := strings.Split(connectString, ",") collector, err = zipkin.NewKafkaCollector( kafkaHosts, zipkin.KafkaTopic(topic), ) + default: + err = fmt.Errorf("Unknown collector type.") } if err != nil { - log.Printf("Unable to create Zipkin %s collector: %+v", collectorType, err) - os.Exit(-1) + log.Fatalf("Unable to create Zipkin %s collector: %v", collectorType, err) } return collector @@ -76,18 +80,21 @@ func CreateSpan(r *http.Request, serviceName string) opentracing.Span { return span // caller must defer span.finish() } -func InitializeTracer(collectorType string, connectString string, serviceName string, topic string, samplerRate float64, addressPort string) { - log.Printf("Tracing initializing - type: %s, connection string: %s, service name: %s, topic: %s, samplerRate: %v", collectorType, connectString, serviceName, topic, samplerRate) +// InitializeTracer initializes OpenTracing support if Tracing.TracingEnabled +// is set in the config. +func InitializeTracer(traceConfig *config.Tracing) { + if !traceConfig.TracingEnabled { + return + } + + log.Printf("Tracing initializing - type: %s, connection string: %s, service name: %s, topic: %s, samplerRate: %v", + traceConfig.CollectorType, traceConfig.ConnectString, traceConfig.ServiceName, traceConfig.Topic, traceConfig.SamplerRate) // Create a new Zipkin Collector, Recorder, and Tracer - collector := CreateCollector(collectorType, connectString, topic) - recorder := zipkin.NewRecorder(collector, false, addressPort, serviceName) - tracer := CreateTracer(recorder, samplerRate) + collector := CreateCollector(traceConfig.CollectorType, traceConfig.ConnectString, traceConfig.Topic) + recorder := zipkin.NewRecorder(collector, false, traceConfig.SpanHost, traceConfig.ServiceName) + tracer := CreateTracer(recorder, traceConfig.SamplerRate) // Set the Zipkin Tracer created above to the GlobalTracer opentracing.SetGlobalTracer(tracer) - - log.Printf("\n\nTRACER: %v\n\n", tracer) - log.Printf("\n\nCOLLECTOR: %v\n\n", collector) - log.Printf("\n\nRECORDER: %v\n\n", recorder) } diff --git a/trace/trace_test.go b/trace/trace_test.go index 46cffdc0f..5b5e676a2 100644 --- a/trace/trace_test.go +++ b/trace/trace_test.go @@ -1,54 +1,130 @@ package trace import ( - opentracing "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/ext" - mocktracer "github.com/opentracing/opentracing-go/mocktracer" "net/http" "testing" + + "github.com/fabiolb/fabio/config" + opentracing "github.com/opentracing/opentracing-go" + mocktracer "github.com/opentracing/opentracing-go/mocktracer" + zipkin "github.com/openzipkin/zipkin-go-opentracing" + zipkintypes "github.com/openzipkin/zipkin-go-opentracing/types" ) -func mimicTracerInject(req *http.Request) { - // TODO maybe replace this will a call to opentracing.GlobalTracer().Inject() - req.Header.Add("X-B3-TraceId", "1234562345678") - req.Header.Add("X-B3-SpanId", "123456789") - req.Header.Add("X-B3-ParentSpanId", "123456789") - req.Header.Add("X-B3-Flags", "1") +const testServiceName = "TEST-SERVICE" + +func TestCreateCollectorHTTP(t *testing.T) { + if CreateCollector("http", "", "") == nil { + t.Error("CreateCollector returned nil value.") + t.FailNow() + } } -// go test -v ./trace -func TestInjectHeaders(t *testing.T) { - serviceName := "TESTING" +func TestCreateSpanNoGlobalTracer(t *testing.T) { + opentracing.SetGlobalTracer(nil) + req, _ := http.NewRequest("GET", "http://example.com", nil) - req, err := http.NewRequest("GET", "http://example.com", nil) - if err != nil { - t.Error("Error when creating new request.") + if CreateSpan(req, testServiceName) != nil { + t.Error("CreateSpan returned a non-nil result using a nil global tracer.") t.Fail() } - mimicTracerInject(req) +} +func TestCreateSpanWithNoParent(t *testing.T) { + tracer, _ := zipkin.NewTracer(nil) + opentracing.SetGlobalTracer(tracer) + + req, _ := http.NewRequest("GET", "http://example.com", nil) + if CreateSpan(req, testServiceName) == nil { + t.Error("Received nil span while a global tracer was set.") + t.FailNow() + } +} + +func TestCreateSpanWithParent(t *testing.T) { mt := mocktracer.New() opentracing.SetGlobalTracer(mt) globalTracer := opentracing.GlobalTracer() + parentSpan := globalTracer.StartSpan(testServiceName) + + requestIn, _ := http.NewRequest("GET", "http://example.com", nil) + mt.Inject(parentSpan.Context(), + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(requestIn.Header), + ) + + if CreateSpan(requestIn, testServiceName+"-child") == nil { + t.Error("Received a nil span while a global tracer was set.") + t.FailNow() + } +} - var span opentracing.Span - if globalTracer != nil { - spanCtx, err := globalTracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header)) - if err != nil { - span = globalTracer.StartSpan(serviceName) - } else { - span = globalTracer.StartSpan(serviceName, ext.RPCServerOption(spanCtx)) - } +func TestInitializeTracer(t *testing.T) { + opentracing.SetGlobalTracer(nil) + InitializeTracer(&config.Tracing{TracingEnabled: true}) + if opentracing.GlobalTracer() == nil { + t.Error("InitializeTracer set a nil tracer.") + t.FailNow() } +} + +func TestInitializeTracerWhileDisabled(t *testing.T) { + opentracing.SetGlobalTracer(nil) + InitializeTracer(&config.Tracing{TracingEnabled: false}) + if opentracing.GlobalTracer() != nil { + t.Error("InitializeTracer set a tracer while tracing was disabled.") + t.FailNow() + } +} +func TestInjectHeaders(t *testing.T) { + mt := mocktracer.New() + opentracing.SetGlobalTracer(mt) + globalTracer := opentracing.GlobalTracer() + span := globalTracer.StartSpan(testServiceName) + + req, _ := http.NewRequest("GET", "http://example.com", nil) InjectHeaders(span, req) + if req.Header.Get("Mockpfx-Ids-Traceid") == "" { + t.Error("Inject did not set the Traceid in the request.") + t.Fail() + } + if req.Header.Get("Mockpfx-Ids-Spanid") == "" { + t.Error("Inject did not set the Spanid in the request.") + t.Fail() + } +} + +func TestInjectHeadersWithParentSpan(t *testing.T) { + parentSpanId := uint64(12345) + parentSpanContext := zipkin.SpanContext{ + SpanID: parentSpanId, + TraceID: zipkintypes.TraceID{High: uint64(1234), Low: uint64(4321)}, + } + + tracer, _ := zipkin.NewTracer(nil) + opentracing.SetGlobalTracer(tracer) + globalTracer := opentracing.GlobalTracer() + childSpan := globalTracer.StartSpan(testServiceName+"-CHILD", opentracing.ChildOf(parentSpanContext)) + + req, _ := http.NewRequest("GET", "http://example.com", nil) + InjectHeaders(childSpan, req) + if req.Header.Get("X-B3-Traceid") == "" { - t.Error("Zipkin headers not set in request.") + t.Error("Inject did not set the Traceid in the request.") + t.Fail() + } + if req.Header.Get("X-B3-Spanid") == "" { + t.Error("Inject did not set the Spanid in the request.") + t.Fail() + } + if req.Header.Get("X-B3-Parentspanid") != "0000000000003039" { + t.Error("Inject did not set the correct Parentspanid in the request.") t.Fail() } - if req.Header.Get("X-B3-Traceid") != "1234562345678" { - t.Error("Zipkin headers do not match the values set.") + if req.Header.Get("x-B3-Traceid") != "00000000000004d200000000000010e1" { + t.Error("Inject did not reuse the Traceid from the parent span") t.Fail() } }