Skip to content

Commit e23a072

Browse files
Merge 52db8d5 into 839e505
2 parents 839e505 + 52db8d5 commit e23a072

File tree

14 files changed

+1495
-0
lines changed

14 files changed

+1495
-0
lines changed

.github/dependabot.yml

+20
Original file line numberDiff line numberDiff line change
@@ -456,3 +456,23 @@ updates:
456456
schedule:
457457
interval: "weekly"
458458
day: "sunday"
459+
-
460+
package-ecosystem: "gomod"
461+
directory: "/instrumentation/github.com/go-kit/kit/otelkit"
462+
labels:
463+
- dependencies
464+
- go
465+
- "Skip Changelog"
466+
schedule:
467+
interval: "weekly"
468+
day: "sunday"
469+
-
470+
package-ecosystem: "gomod"
471+
directory: "/instrumentation/github.com/go-kit/kit/otelkit/example"
472+
labels:
473+
- dependencies
474+
- go
475+
- "Skip Changelog"
476+
schedule:
477+
interval: "weekly"
478+
day: "sunday"

instrumentation/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The following instrumentation packages are provided for popular Go packages and
1212
| [github.com/bradfitz/gomemcache](./github.com/bradfitz/gomemcache/memcache/otelmemcache) | ||
1313
| [github.com/emicklei/go-restful](./github.com/emicklei/go-restful/otelrestful) | ||
1414
| [github.com/gin-gonic/gin](./github.com/gin-gonic/gin/otelgin) | ||
15+
| [github.com/go-kit/kit](./github.com/go-kit/kit/otelkit) | ||
1516
| [github.com/gocql/gocql](./github.com/gocql/gocql/otelgocql) |||
1617
| [github.com/gorilla/mux](./github.com/gorilla/mux/otelmux) | ||
1718
| [github.com/labstack/echo](./github.com/labstack/echo/otelecho) | ||
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Based on https://github.com/go-kit/kit/blob/3796a6b25f5c6c545454d3ed7187c4ced258083d/tracing/opencensus/endpoint_options.go
16+
17+
package otelkit
18+
19+
import (
20+
"context"
21+
22+
"go.opentelemetry.io/otel/attribute"
23+
"go.opentelemetry.io/otel/trace"
24+
)
25+
26+
// config holds the options for tracing an endpoint.
27+
type config struct {
28+
// TracerProvider provides access to instrumentation Tracers.
29+
TracerProvider trace.TracerProvider
30+
31+
// IgnoreBusinessError if set to true will not treat a business error
32+
// identified through the endpoint.Failer interface as a span error.
33+
IgnoreBusinessError bool
34+
35+
// Operation identifies the current operation and serves as a span name.
36+
Operation string
37+
38+
// GetOperation is an optional function that can set the span name based on the existing operation
39+
// for the endpoint and information in the context.
40+
//
41+
// If the function is nil, or the returned operation is empty, the existing operation for the endpoint is used.
42+
GetOperation func(ctx context.Context, operation string) string
43+
44+
// Attributes holds the default attributes for each span created by this middleware.
45+
Attributes []attribute.KeyValue
46+
47+
// GetAttributes is an optional function that can extract trace attributes
48+
// from the context and add them to the span.
49+
GetAttributes func(ctx context.Context) []attribute.KeyValue
50+
}
51+
52+
// Option configures an EndpointMiddleware.
53+
type Option func(*config)
54+
55+
// WithTracerProvider specifies a tracer provider to use for creating a tracer.
56+
// If none is specified, the global provider is used.
57+
func WithTracerProvider(provider trace.TracerProvider) Option {
58+
return func(o *config) {
59+
o.TracerProvider = provider
60+
}
61+
}
62+
63+
// WithIgnoreBusinessError if set to true will not treat a business error
64+
// identified through the endpoint.Failer interface as a span error.
65+
func WithIgnoreBusinessError(val bool) Option {
66+
return func(o *config) {
67+
o.IgnoreBusinessError = val
68+
}
69+
}
70+
71+
// WithOperation sets an operation name for an endpoint.
72+
// Use this when you register a middleware for each endpoint.
73+
func WithOperation(operation string) Option {
74+
return func(o *config) {
75+
o.Operation = operation
76+
}
77+
}
78+
79+
// WithOperationGetter sets an operation name getter function in config.
80+
func WithOperationGetter(fn func(ctx context.Context, name string) string) Option {
81+
return func(o *config) {
82+
o.GetOperation = fn
83+
}
84+
}
85+
86+
// WithAttributes sets the default attributes for the spans created by the Endpoint tracer.
87+
func WithAttributes(attrs ...attribute.KeyValue) Option {
88+
return func(o *config) {
89+
o.Attributes = attrs
90+
}
91+
}
92+
93+
// WithAttributeGetter extracts additional attributes from the context.
94+
func WithAttributeGetter(fn func(ctx context.Context) []attribute.KeyValue) Option {
95+
return func(o *config) {
96+
o.GetAttributes = fn
97+
}
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package otelkit instruments the github.com/go-kit/kit package.
16+
//
17+
// Compared to other instrumentation libraries provided by go-kit itself,
18+
// this package only provides instrumentation for the endpoint layer.
19+
// For instrumenting the transport layer,
20+
// look at the instrumentation libraries provided by go.opentelemetry.io/contrib.
21+
// Learn more about go-kit's layers at https://gokit.io/faq/#architecture-and-design.
22+
package otelkit // import "go.opentelemetry.io/contrib/instrumentation/github.com/go-kit/kit/otelkit"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Based on https://github.com/go-kit/kit/blob/3796a6b25f5c6c545454d3ed7187c4ced258083d/tracing/opencensus/endpoint.go
16+
17+
package otelkit
18+
19+
import (
20+
"context"
21+
22+
"go.opentelemetry.io/otel"
23+
"go.opentelemetry.io/otel/attribute"
24+
"go.opentelemetry.io/otel/codes"
25+
"go.opentelemetry.io/otel/trace"
26+
27+
otelcontrib "go.opentelemetry.io/contrib"
28+
29+
"github.com/go-kit/kit/endpoint"
30+
"github.com/go-kit/kit/sd/lb"
31+
)
32+
33+
const (
34+
tracerName = "go.opentelemetry.io/contrib/instrumentation/github.com/go-kit/kit/otelkit"
35+
36+
// defaultSpanName is the default endpoint span name to use.
37+
defaultSpanName = "gokit/endpoint"
38+
)
39+
40+
// EndpointMiddleware returns an Endpoint middleware, tracing a Go kit endpoint.
41+
// This endpoint middleware should be used in combination with a Go kit Transport
42+
// tracing middleware, generic OpenTelemetry transport middleware or custom before
43+
// and after transport functions.
44+
func EndpointMiddleware(options ...Option) endpoint.Middleware {
45+
cfg := &config{}
46+
47+
for _, o := range options {
48+
o(cfg)
49+
}
50+
51+
if cfg.TracerProvider == nil {
52+
cfg.TracerProvider = otel.GetTracerProvider()
53+
}
54+
55+
tracer := cfg.TracerProvider.Tracer(
56+
tracerName,
57+
trace.WithInstrumentationVersion(otelcontrib.SemVersion()),
58+
)
59+
60+
return func(next endpoint.Endpoint) endpoint.Endpoint {
61+
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
62+
operation := cfg.Operation
63+
if cfg.GetOperation != nil {
64+
if newOperation := cfg.GetOperation(ctx, operation); newOperation != "" {
65+
operation = newOperation
66+
}
67+
}
68+
69+
spanName := operation
70+
if spanName == "" {
71+
spanName = defaultSpanName
72+
}
73+
74+
opts := []trace.SpanOption{
75+
trace.WithAttributes(cfg.Attributes...),
76+
trace.WithSpanKind(trace.SpanKindServer),
77+
}
78+
79+
if cfg.GetAttributes != nil {
80+
opts = append(opts, trace.WithAttributes(cfg.GetAttributes(ctx)...))
81+
}
82+
83+
ctx, span := tracer.Start(ctx, spanName, opts...)
84+
defer span.End()
85+
86+
defer func() {
87+
if err != nil {
88+
if lberr, ok := err.(lb.RetryError); ok {
89+
// Handle errors originating from lb.Retry.
90+
for idx, rawErr := range lberr.RawErrors {
91+
span.RecordError(rawErr, trace.WithAttributes(
92+
attribute.Int("gokit.lb.retry.count", idx+1),
93+
))
94+
}
95+
96+
span.RecordError(lberr.Final)
97+
98+
return
99+
}
100+
101+
// generic error
102+
span.RecordError(err)
103+
104+
return
105+
}
106+
107+
// Test for business error. Business errors are often
108+
// successful requests carrying a business failure that
109+
// the client can act upon and therefore do not count
110+
// as failed requests.
111+
if res, ok := response.(endpoint.Failer); ok && res.Failed() != nil {
112+
span.RecordError(res.Failed())
113+
114+
if cfg.IgnoreBusinessError {
115+
span.SetStatus(codes.Unset, "")
116+
}
117+
118+
return
119+
}
120+
121+
// no errors identified
122+
}()
123+
124+
response, err = next(ctx, request)
125+
126+
return
127+
}
128+
}
129+
}

0 commit comments

Comments
 (0)