Skip to content

Commit

Permalink
[WIP] Add github.com/go-chi/chi instrumentation (#227)
Browse files Browse the repository at this point in the history
* Initial instrumentation layout

* Add changes to changelog

* Add initial config and instrumentation

The config is mostly copied from
#226

* Implement instrumentation

Remove unneeded Config methods.

* Add integration tests

* Shutdown TracerProvider in tests

* Update Middleware docs

* Fix example test
  • Loading branch information
MrAlias authored Dec 14, 2021
1 parent f051a59 commit ed64e83
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ updates:
directory: "/instrumentation/github.com/confluentinc/confluent-kafka-go/kafka/splunkkafka/test"
schedule:
interval: "daily"
- package-ecosystem: "gomod"
directory: "/instrumentation/github.com/go-chi/chi/splunkchi"
schedule:
interval: "daily"
- package-ecosystem: "gomod"
directory: "/instrumentation/github.com/go-chi/chi/splunkchi/test"
schedule:
interval: "daily"
- package-ecosystem: "gomod"
directory: "/instrumentation/github.com/go-sql-driver/mysql/splunkmysql"
schedule:
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add the
`github.com/signalfx/splunk-otel-go/instrumentation/k8s.io/client-go/splunkclient-go`
instrumentation for the `k8s.io/client-go` package. (#224)
- Add the
`github.com/signalfx/splunk-otel-go/instrumentation/github.com/go-chi/chi/splunkchi`
instrumentation for the `github.com/go-chi/chi` package. (#227)

### Changed

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Supported libraries are listed
Additional recommended Splunk specific instrumentations:
- [`splunkchi`](./instrumentation/github.com/go-chi/chi/splunkchi)
- [`splunkclient-go`](./instrumentation/k8s.io/client-go/splunkclient-go)
- [`splunkdns`](./instrumentation/github.com/miekg/dns/splunkdns)
- [`splunkgorm`](./instrumentation/github.com/jinzhu/gorm/splunkgorm)
Expand Down
10 changes: 10 additions & 0 deletions instrumentation/github.com/go-chi/chi/splunkchi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Splunk instrumentation for `github.com/go-chi/chi`

This package provides OpenTelemetry instrumentation for the
[github.com/go-chi/chi](https://github.com/go-chi/chi) package.

## Getting Started

This package is designed to be used as middleware for the
`github.com/go-chi/chi` package. See [example_test.go](./example_test.go) for
more information.
77 changes: 77 additions & 0 deletions instrumentation/github.com/go-chi/chi/splunkchi/chi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright Splunk Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package splunkchi provides OpenTelemetry instrumentation for the
// github.com/go-chi/chi package.
package splunkchi

import (
"net/http"

"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
"go.opentelemetry.io/otel/trace"

"github.com/signalfx/splunk-otel-go/instrumentation/internal"
)

// instrumentationName is the instrumentation library identifier for a Tracer.
const instrumentationName = "github.com/signalfx/splunk-otel-go/instrumentation/github.com/go-chi/chi/splunkchi"

// Middleware returns github.com/go-chi/chi middleware that traces served
// requests.
func Middleware(options ...Option) func(http.Handler) http.Handler {
o := append([]internal.Option{
internal.OptionFunc(func(c *internal.Config) {
c.DefaultStartOpts = append(c.DefaultStartOpts, trace.WithSpanKind(trace.SpanKindServer))
}),
}, localToInternal(options)...)
cfg := internal.NewConfig(instrumentationName, o...)

return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Allows us to track the ultimate status.
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)

tracer := cfg.ResolveTracer(r.Context())
carrier := propagation.HeaderCarrier(r.Header)
ctx := cfg.Propagator.Extract(r.Context(), carrier)
// The full handler chain needs to be complete before we are sure
// what path is being requested. Delay full naming and annotation
// until then.
name := "HTTP " + r.Method
ctx, span := tracer.Start(ctx, name, cfg.DefaultStartOpts...)
defer span.End()
r = r.WithContext(ctx)

next.ServeHTTP(ww, r)

path := chi.RouteContext(r.Context()).RoutePattern()
attrs := semconv.HTTPServerAttributesFromHTTPRequest("", path, r)
attrs = append(attrs, semconv.HTTPAttributesFromHTTPStatusCode(ww.Status())...)
attrs = append(attrs, semconv.NetAttributesFromHTTPRequest("tcp", r)...)
attrs = append(attrs, semconv.EndUserAttributesFromHTTPRequest(r)...)
span.SetAttributes(attrs...)

if path != "" {
span.SetName(name + " " + path)
}

code, desc := semconv.SpanStatusFromHTTPStatusCode(ww.Status())
span.SetStatus(code, desc)
})
}
}
37 changes: 37 additions & 0 deletions instrumentation/github.com/go-chi/chi/splunkchi/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright Splunk Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package splunkchi_test

import (
"net/http"

"github.com/go-chi/chi"

"github.com/signalfx/splunk-otel-go/instrumentation/github.com/go-chi/chi/splunkchi"
)

func Example() {
router := chi.NewRouter()
router.Use(splunkchi.Middleware())
router.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("Hello World!\n"))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
})
if err := http.ListenAndServe(":8080", router); err != nil {
panic(err)
}
}
12 changes: 12 additions & 0 deletions instrumentation/github.com/go-chi/chi/splunkchi/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/signalfx/splunk-otel-go/instrumentation/github.com/go-chi/chi/splunkchi

go 1.16

require (
github.com/go-chi/chi v1.5.4
github.com/signalfx/splunk-otel-go v0.0.0-00010101000000-000000000000
go.opentelemetry.io/otel v1.2.0
go.opentelemetry.io/otel/trace v1.2.0
)

replace github.com/signalfx/splunk-otel-go => ../../../../../
23 changes: 23 additions & 0 deletions instrumentation/github.com/go-chi/chi/splunkchi/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/contrib/propagators/b3 v1.2.0/go.mod h1:kO8hNKCfa1YmQJ0lM7pzfJGvbXEipn/S7afbOfaw2Kc=
go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ=
go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I=
go.opentelemetry.io/otel/exporters/jaeger v1.2.0/go.mod h1:KJLFbEMKTNPIfOxcg/WikIozEoKcPgJRz3Ce1vLlM8E=
go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U=
go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0=
go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
62 changes: 62 additions & 0 deletions instrumentation/github.com/go-chi/chi/splunkchi/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright Splunk Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package splunkchi

import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"

"github.com/signalfx/splunk-otel-go/instrumentation/internal"
)

func localToInternal(opts []Option) []internal.Option {
out := make([]internal.Option, len(opts))
for i, o := range opts {
out[i] = internal.OptionFunc(func(c *internal.Config) { o.apply(c) })
}
return out
}

// Option applies options to a configuration.
type Option interface {
apply(*internal.Config)
}

type optionConv struct {
iOpt internal.Option
}

func (o optionConv) apply(c *internal.Config) {
o.iOpt.Apply(c)
}

// WithTracerProvider returns an Option that sets the TracerProvider used for
// a configuration.
func WithTracerProvider(tp trace.TracerProvider) Option {
return optionConv{iOpt: internal.WithTracerProvider(tp)}
}

// WithAttributes returns an Option that appends attr to the attributes set
// for every span created.
func WithAttributes(attr []attribute.KeyValue) Option {
return optionConv{iOpt: internal.WithAttributes(attr)}
}

// WithPropagator returns an Option that sets p as the TextMapPropagator used
// when propagating a span context.
func WithPropagator(p propagation.TextMapPropagator) Option {
return optionConv{iOpt: internal.WithPropagator(p)}
}
Loading

0 comments on commit ed64e83

Please sign in to comment.