Example #1
0
// ExtractInboundSpan is a higher level version of extractInboundSpan().
// If the lower-level attempt to create a span from incoming request was
// successful (e.g. when then Tracer supports Zipkin-style trace IDs),
// then the application headers are only used to read the Baggage and add
// it to the existing span. Otherwise, the standard OpenTracing API supported
// by all tracers is used to deserialize the tracing context from the
// application headers and start a new server-side span.
// Once the span is started, it is wrapped in a new Context, which is returned.
func ExtractInboundSpan(ctx context.Context, call *InboundCall, headers map[string]string, tracer opentracing.Tracer) context.Context {
	var span = call.Response().span
	if span != nil {
		if headers != nil {
			// extract SpanContext from headers, but do not start another span with it,
			// just get the baggage and copy to the already created span
			carrier := tracingHeadersCarrier(headers)
			if sc, err := tracer.Extract(opentracing.TextMap, carrier); err == nil {
				sc.ForeachBaggageItem(func(k, v string) bool {
					span.SetBaggageItem(k, v)
					return true
				})
			}
			carrier.RemoveTracingKeys()
		}
	} else {
		var parent opentracing.SpanContext
		if headers != nil {
			carrier := tracingHeadersCarrier(headers)
			if p, err := tracer.Extract(opentracing.TextMap, carrier); err == nil {
				parent = p
			}
			carrier.RemoveTracingKeys()
		}
		span = tracer.StartSpan(call.MethodString(), ext.RPCServerOption(parent))
		ext.PeerService.Set(span, call.CallerName())
		span.SetTag("as", string(call.Format()))
		call.conn.setPeerHostPort(span)
		call.Response().span = span
	}
	return opentracing.ContextWithSpan(ctx, span)
}
Example #2
0
func TestRPCServerOption(t *testing.T) {
	tracer := mocktracer.New()
	parent := tracer.StartSpan("my-trace")
	parent.SetBaggageItem("bag", "gage")

	carrier := opentracing.HTTPHeadersCarrier{}
	err := tracer.Inject(parent.Context(), opentracing.HTTPHeaders, carrier)
	if err != nil {
		t.Fatal(err)
	}

	parCtx, err := tracer.Extract(opentracing.HTTPHeaders, carrier)
	if err != nil {
		t.Fatal(err)
	}

	tracer.StartSpan("my-child", ext.RPCServerOption(parCtx)).Finish()

	rawSpan := tracer.FinishedSpans()[0]
	assert.Equal(t, map[string]interface{}{
		"span.kind": ext.SpanKindRPCServerEnum,
	}, rawSpan.Tags())
	assert.Equal(t, map[string]string{
		"bag": "gage",
	}, rawSpan.Context().(mocktracer.MockSpanContext).Baggage)
}
Example #3
0
File: grpc.go Project: crezam/kit
// FromGRPCRequest returns a grpc RequestFunc that tries to join with an
// OpenTracing trace found in `req` and starts a new Span called
// `operationName` accordingly. If no trace could be found in `req`, the Span
// will be a trace root. The Span is incorporated in the returned Context and
// can be retrieved with opentracing.SpanFromContext(ctx).
func FromGRPCRequest(tracer opentracing.Tracer, operationName string, logger log.Logger) func(ctx context.Context, md *metadata.MD) context.Context {
	return func(ctx context.Context, md *metadata.MD) context.Context {
		var span opentracing.Span
		wireContext, err := tracer.Extract(opentracing.TextMap, metadataReaderWriter{md})
		if err != nil && err != opentracing.ErrSpanContextNotFound {
			logger.Log("err", err)
		}
		span = tracer.StartSpan(operationName, ext.RPCServerOption(wireContext))
		return opentracing.ContextWithSpan(ctx, span)
	}
}
Example #4
0
// FromHTTPRequest returns an http RequestFunc that tries to join with an
// OpenTracing trace found in `req` and starts a new Span called
// `operationName` accordingly. If no trace could be found in `req`, the Span
// will be a trace root. The Span is incorporated in the returned Context and
// can be retrieved with opentracing.SpanFromContext(ctx).
func FromHTTPRequest(tracer opentracing.Tracer, operationName string, logger log.Logger) kithttp.RequestFunc {
	return func(ctx context.Context, req *http.Request) context.Context {
		// Try to join to a trace propagated in `req`.
		var span opentracing.Span
		wireContext, err := tracer.Extract(
			opentracing.TextMap,
			opentracing.HTTPHeadersCarrier(req.Header),
		)
		if err != nil && err != opentracing.ErrSpanContextNotFound {
			logger.Log("err", err)
		}
		span = tracer.StartSpan(operationName, ext.RPCServerOption(wireContext))
		return opentracing.ContextWithSpan(ctx, span)
	}
}
Example #5
0
// extractInboundSpan attempts to create a new OpenTracing Span for inbound request
// using only trace IDs stored in the frame's tracing field. It only works if the
// tracer understand Zipkin-style trace IDs. If such attempt fails, another attempt
// will be made from the higher level function ExtractInboundSpan() once the
// application headers are read from the wire.
func (c *Connection) extractInboundSpan(callReq *callReq) opentracing.Span {
	spanCtx, err := c.Tracer().Extract(zipkinSpanFormat, &callReq.Tracing)
	if err != nil {
		if err != opentracing.ErrUnsupportedFormat && err != opentracing.ErrSpanContextNotFound {
			c.log.WithFields(ErrField(err)).Error("Failed to extract Zipkin-style span.")
		}
		return nil
	}
	if spanCtx == nil {
		return nil
	}
	operationName := "" // not known at this point, will be set later
	span := c.Tracer().StartSpan(operationName, ext.RPCServerOption(spanCtx))
	span.SetTag("as", callReq.Headers[ArgScheme])
	ext.PeerService.Set(span, callReq.Headers[CallerName])
	c.setPeerHostPort(span)
	return span
}
Example #6
0
func (h handler) createSpan(ctx context.Context, req *http.Request, treq *transport.Request, start time.Time) (context.Context, opentracing.Span) {
	// Extract opentracing etc baggage from headers
	// Annotate the inbound context with a trace span
	tracer := h.Deps.Tracer()
	carrier := opentracing.HTTPHeadersCarrier(req.Header)
	parentSpanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, carrier)
	// parentSpanCtx may be nil, ext.RPCServerOption handles a nil parent
	// gracefully.
	span := tracer.StartSpan(
		treq.Procedure,
		opentracing.StartTime(start),
		opentracing.Tags{
			"rpc.caller":    treq.Caller,
			"rpc.service":   treq.Service,
			"rpc.encoding":  treq.Encoding,
			"rpc.transport": "http",
		},
		ext.RPCServerOption(parentSpanCtx), // implies ChildOf
	)
	ext.PeerService.Set(span, treq.Caller)
	ctx = opentracing.ContextWithSpan(ctx, span)
	return ctx, span
}
Example #7
0
func main() {
	storage, err := tracer.NewGRPC("localhost:9999", &tracer.GRPCOptions{
		QueueSize:     1024,
		FlushInterval: 1 * time.Second,
	}, grpc.WithInsecure())
	if err != nil {
		log.Fatal(err)
	}
	t1 := tracer.NewTracer("frontend", storage, tracer.RandomID{})
	t2 := tracer.NewTracer("backend", storage, tracer.RandomID{})

	s1 := t1.StartSpan("frontend", ext.RPCServerOption(nil))
	ext.HTTPUrl.Set(s1, "/hello")
	ext.HTTPMethod.Set(s1, "GET")

	s2 := t1.StartSpan("backend.hello", opentracing.ChildOf(s1.Context()))
	ext.SpanKindRPCClient.Set(s2)
	ext.Component.Set(s2, "grpc")
	carrier := opentracing.TextMapCarrier{}
	if err := t1.Inject(s2.Context(), opentracing.TextMap, carrier); err != nil {
		log.Println(err)
	}

	c3, err := t2.Extract(opentracing.TextMap, carrier)
	if err != nil {
		log.Println(err)
	}
	s3 := t2.StartSpan("backend.hello", ext.RPCServerOption(c3))
	ext.Component.Set(s3, "grpc")

	s4 := t2.StartSpan("mysql", opentracing.ChildOf(s3.Context()))
	ext.SpanKindRPCClient.Set(s4)
	ext.Component.Set(s4, "mysql")
	s4.SetTag("sql.query", "SELECT * FROM table1")
	// The MySQL server is not instrumented, so we only get the client
	// span.
	s4.Finish()

	s5 := t2.StartSpan("redis", opentracing.ChildOf(s3.Context()))
	ext.SpanKindRPCClient.Set(s5)
	ext.Component.Set(s5, "redis")
	// The Redis server is not instrumented, so we only get the client
	// span.
	s5.Finish()

	s3.Finish()
	s2.Finish()

	s6 := t1.StartSpan("backend.ads", opentracing.ChildOf(s1.Context()))
	ext.SpanKindRPCClient.Set(s6)
	ext.Component.Set(s6, "grpc")
	carrier = opentracing.TextMapCarrier{}
	if err := t1.Inject(s6.Context(), opentracing.TextMap, carrier); err != nil {
		log.Println(err)
	}

	c7, err := t2.Extract(opentracing.TextMap, carrier)
	if err != nil {
		log.Println(err)
	}
	s7 := t2.StartSpan("backend.ads", ext.RPCServerOption(c7))
	ext.Component.Set(s7, "grpc")
	s7.Finish()
	s6.Finish()

	ext.HTTPStatusCode.Set(s1, 200)
	s1.Finish()

	// Wait for spans to be flushed. Production code wouldn't need
	// this.
	time.Sleep(2 * time.Second)
}