New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tracing.opentracing endpoint middleware options #1072
Merged
basvanbeek
merged 9 commits into
go-kit:master
from
alebabai:feature-tracing-opentracing
May 7, 2021
Merged
Changes from 8 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
377c235
refactor(tracing.opentracing): add endpoint tracing options
alebabai 038371b
test(tracing.opentracing): cover endpoint options
alebabai 185f2f8
update comments
alebabai af43370
refactor(tracing.opentracing): options improvements
alebabai a4dd947
refactor(tracing.opentracing): add TraceEndpoint function
alebabai 4af2b54
fix imports
alebabai b8df650
fix lint
alebabai 93f53b2
refactor(tracing.opentracing): use ContextWithSpan after applying tags
alebabai e5c83b1
refactor(tracing.opentracing): standardize
alebabai File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -4,52 +4,98 @@ import ( | |||||
"context" | ||||||
|
||||||
"github.com/opentracing/opentracing-go" | ||||||
otext "github.com/opentracing/opentracing-go/ext" | ||||||
"github.com/opentracing/opentracing-go/ext" | ||||||
|
||||||
"github.com/go-kit/kit/endpoint" | ||||||
) | ||||||
|
||||||
// TraceServer returns a Middleware that wraps the `next` Endpoint in an | ||||||
// TraceEndpoint returns a Middleware that wraps the `next` Endpoint in an | ||||||
// OpenTracing Span called `operationName`. | ||||||
// | ||||||
// If `ctx` already has a Span, it is re-used and the operation name is | ||||||
// overwritten. If `ctx` does not yet have a Span, one is created here. | ||||||
func TraceServer(tracer opentracing.Tracer, operationName string) endpoint.Middleware { | ||||||
return func(next endpoint.Endpoint) endpoint.Endpoint { | ||||||
return func(ctx context.Context, request interface{}) (interface{}, error) { | ||||||
serverSpan := opentracing.SpanFromContext(ctx) | ||||||
if serverSpan == nil { | ||||||
// All we can do is create a new root span. | ||||||
serverSpan = tracer.StartSpan(operationName) | ||||||
} else { | ||||||
serverSpan.SetOperationName(operationName) | ||||||
} | ||||||
defer serverSpan.Finish() | ||||||
otext.SpanKindRPCServer.Set(serverSpan) | ||||||
ctx = opentracing.ContextWithSpan(ctx, serverSpan) | ||||||
return next(ctx, request) | ||||||
} | ||||||
// If `ctx` already has a Span, child span is created from it. | ||||||
// If `ctx` doesn't yet have a Span, the new one is created. | ||||||
func TraceEndpoint(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware { | ||||||
cfg := &EndpointOptions{ | ||||||
Tags: make(opentracing.Tags), | ||||||
} | ||||||
|
||||||
for _, opt := range opts { | ||||||
opt(cfg) | ||||||
} | ||||||
} | ||||||
|
||||||
// TraceClient returns a Middleware that wraps the `next` Endpoint in an | ||||||
// OpenTracing Span called `operationName`. | ||||||
func TraceClient(tracer opentracing.Tracer, operationName string) endpoint.Middleware { | ||||||
return func(next endpoint.Endpoint) endpoint.Endpoint { | ||||||
return func(ctx context.Context, request interface{}) (interface{}, error) { | ||||||
var clientSpan opentracing.Span | ||||||
if cfg.GetOperationName != nil { | ||||||
if newOperationName := cfg.GetOperationName(ctx, operationName); newOperationName != "" { | ||||||
operationName = newOperationName | ||||||
} | ||||||
} | ||||||
|
||||||
var span opentracing.Span | ||||||
if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil { | ||||||
clientSpan = tracer.StartSpan( | ||||||
span = tracer.StartSpan( | ||||||
operationName, | ||||||
opentracing.ChildOf(parentSpan.Context()), | ||||||
) | ||||||
} else { | ||||||
clientSpan = tracer.StartSpan(operationName) | ||||||
span = tracer.StartSpan(operationName) | ||||||
} | ||||||
defer span.Finish() | ||||||
|
||||||
applyTags(span, cfg.Tags) | ||||||
if cfg.GetTags != nil { | ||||||
extraTags := cfg.GetTags(ctx) | ||||||
applyTags(span, extraTags) | ||||||
} | ||||||
defer clientSpan.Finish() | ||||||
otext.SpanKindRPCClient.Set(clientSpan) | ||||||
ctx = opentracing.ContextWithSpan(ctx, clientSpan) | ||||||
return next(ctx, request) | ||||||
|
||||||
ctx = opentracing.ContextWithSpan(ctx, span) | ||||||
|
||||||
response, err := next(ctx, request) | ||||||
if err := identifyError(response, err, cfg.IgnoreBusinessError); err != nil { | ||||||
ext.LogError(span, err) | ||||||
} | ||||||
|
||||||
return response, err | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
// TraceServer returns a Middleware that wraps the `next` Endpoint in an | ||||||
// OpenTracing Span called `operationName` with server span.kind tag.. | ||||||
func TraceServer(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware { | ||||||
opts = append(opts, WithTags(map[string]interface{}{ | ||||||
ext.SpanKindRPCServer.Key: ext.SpanKindRPCServer.Value, | ||||||
})) | ||||||
|
||||||
return TraceEndpoint(tracer, operationName, opts...) | ||||||
} | ||||||
|
||||||
// TraceClient returns a Middleware that wraps the `next` Endpoint in an | ||||||
// OpenTracing Span called `operationName` with client span.kind tag. | ||||||
func TraceClient(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware { | ||||||
opts = append(opts, WithTags(map[string]interface{}{ | ||||||
ext.SpanKindRPCServer.Key: ext.SpanKindRPCClient.Value, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks |
||||||
})) | ||||||
|
||||||
return TraceEndpoint(tracer, operationName, opts...) | ||||||
} | ||||||
|
||||||
func applyTags(span opentracing.Span, tags opentracing.Tags) { | ||||||
for key, value := range tags { | ||||||
span.SetTag(key, value) | ||||||
} | ||||||
} | ||||||
|
||||||
func identifyError(response interface{}, err error, ignoreBusinessError bool) error { | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
|
||||||
if !ignoreBusinessError { | ||||||
if res, ok := response.(endpoint.Failer); ok { | ||||||
return res.Failed() | ||||||
} | ||||||
} | ||||||
|
||||||
return nil | ||||||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package opentracing | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/opentracing/opentracing-go" | ||
) | ||
|
||
// EndpointOptions holds the options for tracing an endpoint | ||
type EndpointOptions struct { | ||
// IgnoreBusinessError if set to true will not treat a business error | ||
// identified through the endpoint.Failer interface as a span error. | ||
IgnoreBusinessError bool | ||
|
||
// GetOperationName is an optional function that can set the span operation name based on the existing one | ||
// for the endpoint and information in the context. | ||
// | ||
// If the function is nil, or the returned name is empty, the existing name for the endpoint is used. | ||
GetOperationName func(ctx context.Context, name string) string | ||
|
||
// Tags holds the default tags which will be set on span | ||
// creation by our Endpoint middleware. | ||
Tags opentracing.Tags | ||
|
||
// GetTags is an optional function that can extract tags | ||
// from the context and add them to the span. | ||
GetTags func(ctx context.Context) opentracing.Tags | ||
} | ||
|
||
// EndpointOption allows for functional options to endpoint tracing middleware. | ||
type EndpointOption func(*EndpointOptions) | ||
|
||
// WithOptions sets all configuration options at once by use of the EndpointOptions struct. | ||
func WithOptions(options EndpointOptions) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
*o = options | ||
} | ||
} | ||
|
||
// WithIgnoreBusinessError if set to true will not treat a business error | ||
// identified through the endpoint.Failer interface as a span error. | ||
func WithIgnoreBusinessError(ignoreBusinessError bool) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.IgnoreBusinessError = ignoreBusinessError | ||
} | ||
} | ||
|
||
// WithOperationNameFunc allows to set function that can set the span operation name based on the existing one | ||
// for the endpoint and information in the context. | ||
func WithOperationNameFunc(getOperationName func(ctx context.Context, name string) string) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.GetOperationName = getOperationName | ||
} | ||
} | ||
|
||
// WithTags adds default tags for the spans created by the Endpoint tracer. | ||
func WithTags(tags opentracing.Tags) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
if o.Tags == nil { | ||
o.Tags = make(opentracing.Tags) | ||
} | ||
|
||
for key, value := range tags { | ||
o.Tags[key] = value | ||
} | ||
} | ||
} | ||
|
||
// WithTagsFunc set the func to extracts additional tags from the context. | ||
func WithTagsFunc(getTags func(ctx context.Context) opentracing.Tags) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.GetTags = getTags | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would like more symmetry with the OpenCensus implementation and also include the handling of Go kit load balancer retry errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would also be nice that even if an ignore business error is set to true, we would still see it being tagged with "gokit.business.error", again in-line with the OpenCensus implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, you're right.
Implemented it in way as it was done in opencensus, now it support all the stuff.