-
Notifications
You must be signed in to change notification settings - Fork 176
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0712076
commit c4e9aa5
Showing
7 changed files
with
427 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
run: | ||
skip-dirs: | ||
- .gen | ||
- pkg/kitx/tracing/opentelemetry | ||
|
||
skip-files: | ||
- ".*_gen\\.go$" | ||
|
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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// Package opentelemetry provides Go kit integration to the OpenTelemetry project. | ||
// | ||
// OpenTelemetry makes robust, portable telemetry a built-in feature of cloud-native software. | ||
package opentelemetry |
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,105 @@ | ||
package opentelemetry | ||
|
||
import ( | ||
"context" | ||
"strconv" | ||
|
||
"go.opentelemetry.io/otel/api/core" | ||
"go.opentelemetry.io/otel/api/global" | ||
"go.opentelemetry.io/otel/api/key" | ||
"go.opentelemetry.io/otel/api/trace" | ||
"google.golang.org/grpc/codes" | ||
|
||
"github.com/go-kit/kit/endpoint" | ||
"github.com/go-kit/kit/sd/lb" | ||
) | ||
|
||
// TraceEndpointDefaultName is the default endpoint span name to use. | ||
const TraceEndpointDefaultName = "gokit/endpoint" | ||
|
||
// TraceEndpoint returns an Endpoint middleware, tracing a Go kit endpoint. | ||
// This endpoint tracer should be used in combination with a Go kit Transport | ||
// tracing middleware, generic OpenTelemetry transport middleware or custom before | ||
// and after transport functions as service propagation of SpanContext is not | ||
// provided in this middleware. | ||
func TraceEndpoint(options ...EndpointOption) endpoint.Middleware { | ||
cfg := &EndpointOptions{} | ||
|
||
global.Tracer("") | ||
|
||
for _, o := range options { | ||
o(cfg) | ||
} | ||
|
||
if cfg.Tracer == nil { | ||
cfg.Tracer = global.Tracer("") | ||
} | ||
|
||
if cfg.DefaultName == "" { | ||
cfg.DefaultName = TraceEndpointDefaultName | ||
} | ||
|
||
return func(next endpoint.Endpoint) endpoint.Endpoint { | ||
return func(ctx context.Context, request interface{}) (response interface{}, err error) { | ||
name := cfg.DefaultName | ||
|
||
if cfg.GetName != nil { | ||
if newName := cfg.GetName(ctx, name); newName != "" { | ||
name = newName | ||
} | ||
} | ||
|
||
ctx, span := cfg.Tracer.Start( | ||
ctx, name, | ||
trace.WithAttributes(cfg.Attributes...), | ||
trace.WithAttributes(cfg.getAttributes(ctx)...), | ||
) | ||
defer span.End() | ||
|
||
defer func() { | ||
if err != nil { | ||
if lberr, ok := err.(lb.RetryError); ok { | ||
// handle errors originating from lb.Retry | ||
attrs := make([]core.KeyValue, 0, len(lberr.RawErrors)) | ||
for idx, rawErr := range lberr.RawErrors { | ||
attrs = append(attrs, key.String("gokit.retry.error."+strconv.Itoa(idx+1), rawErr.Error())) | ||
} | ||
|
||
span.SetAttributes(attrs...) | ||
span.SetStatus(codes.Unknown, lberr.Final.Error()) | ||
|
||
return | ||
} | ||
|
||
// generic error | ||
span.SetStatus(codes.Unknown, err.Error()) | ||
|
||
return | ||
} | ||
|
||
// test for business error | ||
if res, ok := response.(endpoint.Failer); ok && res.Failed() != nil { | ||
span.SetAttributes(key.String("gokit.business.error", res.Failed().Error())) | ||
|
||
if cfg.IgnoreBusinessError { | ||
// status ok | ||
|
||
return | ||
} | ||
|
||
// treating business error as real error in span. | ||
span.SetStatus(codes.Unknown, res.Failed().Error()) | ||
|
||
return | ||
} | ||
|
||
// no errors identified | ||
// status ok | ||
}() | ||
|
||
response, err = next(ctx, request) | ||
|
||
return | ||
} | ||
} | ||
} |
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,102 @@ | ||
package opentelemetry | ||
|
||
import ( | ||
"context" | ||
|
||
"go.opentelemetry.io/otel/api/core" | ||
"go.opentelemetry.io/otel/api/trace" | ||
) | ||
|
||
// EndpointOptions holds the options for tracing an endpoint | ||
type EndpointOptions struct { | ||
// Tracer (if specified) is used for starting new spans. | ||
// Falls back to a global tracer with an empty name. | ||
// | ||
// See https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#obtaining-a-tracer | ||
Tracer trace.Tracer | ||
|
||
// DefaultName is used as a fallback if GetName is not specified. | ||
DefaultName string | ||
|
||
// IgnoreBusinessError if set to true will not treat a business error | ||
// identified through the endpoint.Failer interface as a span error. | ||
IgnoreBusinessError bool | ||
|
||
// Attributes holds the default attributes which will be set on span | ||
// creation by our Endpoint middleware. | ||
Attributes []core.KeyValue | ||
|
||
// GetName is an optional function that can set the span name based on the existing name | ||
// 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. | ||
GetName func(ctx context.Context, name string) string | ||
|
||
// GetAttributes is an optional function that can extract trace attributes | ||
// from the context and add them to the span. | ||
GetAttributes func(ctx context.Context) []core.KeyValue | ||
} | ||
|
||
func (o EndpointOptions) getAttributes(ctx context.Context) []core.KeyValue { | ||
if o.GetAttributes == nil { | ||
return nil | ||
} | ||
|
||
return o.GetAttributes(ctx) | ||
} | ||
|
||
// EndpointOption allows for functional options to our OpenTelemetry endpoint | ||
// tracing middleware. | ||
type EndpointOption func(*EndpointOptions) | ||
|
||
// WithEndpointConfig sets all configuration options at once by use of the | ||
// EndpointOptions struct. | ||
func WithEndpointConfig(options EndpointOptions) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
*o = options | ||
} | ||
} | ||
|
||
// WithTracer sets the tracer. | ||
func WithTracer(tracer trace.Tracer) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.Tracer = tracer | ||
} | ||
} | ||
|
||
// WithDefaultName sets the default name. | ||
func WithDefaultName(defaultName string) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.DefaultName = defaultName | ||
} | ||
} | ||
|
||
// WithEndpointAttributes sets the default attributes for the spans created by | ||
// the Endpoint tracer. | ||
func WithEndpointAttributes(attrs ...core.KeyValue) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.Attributes = attrs | ||
} | ||
} | ||
|
||
// WithIgnoreBusinessError if set to true will not treat a business error | ||
// identified through the endpoint.Failer interface as a span error. | ||
func WithIgnoreBusinessError(val bool) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.IgnoreBusinessError = val | ||
} | ||
} | ||
|
||
// WithSpanName extracts additional attributes from the request context. | ||
func WithSpanName(fn func(ctx context.Context, name string) string) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.GetName = fn | ||
} | ||
} | ||
|
||
// WithSpanAttributes extracts additional attributes from the request context. | ||
func WithSpanAttributes(fn func(ctx context.Context) []core.KeyValue) EndpointOption { | ||
return func(o *EndpointOptions) { | ||
o.GetAttributes = fn | ||
} | ||
} |
Oops, something went wrong.