Skip to content
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

fix(#18): implement shutdown for tracer and meter #21

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
with-expecter: true
disable-version-string: true
inpackage: false
mockname: "{{.InterfaceName}}"
filename: "{{.InterfaceNameSnake}}.go"
outpkg: "mocks"
dir: "{{.InterfaceDirRelative}}/mocks"

packages:
"flamingo.me/opentelemetry":
interfaces:
Shutdowner:
17 changes: 9 additions & 8 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ Flamingo's `URLPrefixSampler` and config from `flamingo.opencensus.tracing.sampl
Before you can create your own spans, you have to initialize a tracer:

```go
tracer := otel.Tracer("my-app")
var tracer = otel.Tracer("my-app", trace.WithInstrumentationVersion("1.2.3"))
```

Now you can create a span based on a `context.Context`. This will automatically attach all tracing-relevant
information (e.g. trace-ID) to the span.

```go
func doSomething(ctx context.Context) {
ctx, span := tracer.Start(ctx, "my-span")
defer span.End()

// do some work to track with my-span
ctx, span := tracer.Start(ctx, "my-span")
defer span.End()
// do some work to track with my-span
}
```

Expand All @@ -70,14 +70,15 @@ official [OpenTelemetry documentation](https://opentelemetry.io/docs/instrumenta
To collect your own metrics, you have to initialize a meter:

```go
meter := otel.Meter("my-app")
var meter = otel.Meter("my-app", metric.WithInstrumentationVersion("1.2.3"))
```

Now you can create a new metric, e.g. a counter:

```go
counter, _ := meter.Int64Counter("my.count",
metric.WithDescription("count of something"),
counter, _ := meter.Int64Counter("my.count",
metric.WithDescription("count of something"),
metric.WithUnit("{something}")
)

counter.Add(ctx, 1)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ require (
github.com/redis/go-redis/v9 v9.6.1 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/zemirco/memorystore v0.0.0-20160308183530-ecd57e5134f6 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect
Expand Down
10 changes: 2 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
Expand Down Expand Up @@ -133,6 +131,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
Expand All @@ -143,18 +143,12 @@ github.com/zemirco/memorystore v0.0.0-20160308183530-ecd57e5134f6 h1:j+ZgVPhfLkC
github.com/zemirco/memorystore v0.0.0-20160308183530-ecd57e5134f6/go.mod h1:PLhuixMlky6sB4/LEnpp1//u2BcRF2pKUYXLMVyOrIc=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0 h1:2JydY5UiDpqvj2p7sO9bgHuhTy4hgTZ0ymehdq/Ob0Q=
go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0/go.mod h1:ch3a5QxOqVWxas4CzjCFFOOQe+7HgAXC/N1oVxS9DK4=
go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0 h1:KD+8SJvRaW9n0vE0UgkytT207J3CmV1hGf9GYYU73ns=
go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0/go.mod h1:/CsTuLR28IN3Vn13YEc72HljfHiGOMXiCbl4xiCSDhA=
go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts=
go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc=
go.opentelemetry.io/otel/bridge/opencensus v0.45.0 h1:kEOlv9Exuv3J8GCf1nLMHfrTPGnZOuIkN8YlRM14TtQ=
go.opentelemetry.io/otel/bridge/opencensus v0.45.0/go.mod h1:tkVMJeFOr43+zzwbxtIWsNcCCDT7rI5/c9rhMfMIENg=
go.opentelemetry.io/otel/bridge/opencensus v1.30.0 h1:F6WsF4aSV6g6IBiK4NZ8gWNHqFjtwxrEsUnrYEGyBSw=
go.opentelemetry.io/otel/bridge/opencensus v1.30.0/go.mod h1:3FOD7DbLQhWLxwnzbYoT1oHucvoQIqLXHez1u2KIxMc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4=
Expand Down
82 changes: 82 additions & 0 deletions mocks/shutdowner.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions module.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package opentelemetry

//go:generate go run github.com/vektra/mockery/v2@v2.45.1

import (
"context"
"fmt"
Expand Down Expand Up @@ -69,6 +71,8 @@ func (m *Module) Inject(
func (m *Module) Configure(injector *dingo.Injector) {
http.DefaultTransport = &correlationIDInjector{next: otelhttp.NewTransport(http.DefaultTransport)}

flamingo.BindEventSubscriber(injector).To(new(Listener))

m.initTraces()
m.initMetrics(injector)
}
Expand Down
28 changes: 28 additions & 0 deletions module_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package opentelemetry_test

import (
"testing"

"flamingo.me/dingo"
"flamingo.me/flamingo/v3/framework/config"
"flamingo.me/flamingo/v3/framework/flamingo"

"flamingo.me/opentelemetry"
)

type (
loggerModule struct{}
)

// Configure DI
func (m *loggerModule) Configure(injector *dingo.Injector) {
injector.Bind(new(flamingo.Logger)).To(new(flamingo.NullLogger))
}

func TestModule_Configure(t *testing.T) {
t.Parallel()

if err := config.TryModules(nil, new(loggerModule), new(opentelemetry.Module)); err != nil {
t.Error(err)
}
}
56 changes: 56 additions & 0 deletions shutdown.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package opentelemetry

import (
"context"

"flamingo.me/flamingo/v3/framework/flamingo"
"go.opentelemetry.io/otel"
)

type (
Shutdowner interface {
Shutdown(ctx context.Context) error
}

Listener struct {
logger flamingo.Logger
}
)

// Inject dependencies
func (l *Listener) Inject(
logger flamingo.Logger,
) *Listener {
l.logger = logger

return l
}

func (l *Listener) Notify(ctx context.Context, event flamingo.Event) {
if _, ok := event.(*flamingo.ShutdownEvent); ok {
tp := otel.GetTracerProvider()
if s, ok := tp.(Shutdowner); ok {
l.shutdown(ctx, s)
}

mp := otel.GetMeterProvider()
if s, ok := mp.(Shutdowner); ok {
l.shutdown(ctx, s)
}
}
}

func (l *Listener) shutdown(ctx context.Context, s Shutdowner) {
l.log().Debugf("Shutdown OpenTelemetry: %T", s)

err := s.Shutdown(ctx)
if err != nil {
l.log().Error("", err)
}
}

func (l *Listener) log() flamingo.Logger {
return l.logger.
WithField(flamingo.LogKeyModule, "opentelemetry").
WithField(flamingo.LogKeyCategory, "Shutdown")
}
77 changes: 77 additions & 0 deletions shutdown_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package opentelemetry_test

import (
"context"
"errors"
"testing"

"flamingo.me/flamingo/v3/framework/flamingo"
"go.opentelemetry.io/otel"
noopMetric "go.opentelemetry.io/otel/metric/noop"
noopTrace "go.opentelemetry.io/otel/trace/noop"

"flamingo.me/opentelemetry"
"flamingo.me/opentelemetry/mocks"
)

type (
tracerProvider struct {
noopTrace.TracerProvider
mocks.Shutdowner
}

meterProvider struct {
noopMetric.MeterProvider
mocks.Shutdowner
}
)

var errShutdown = errors.New("shutdown error")

func TestListener_Notify(t *testing.T) { //nolint:tparallel // no parallel subtests possible because of global state manipulation
t.Parallel()

type args struct {
event flamingo.Event
}

tests := []struct {
name string
args args
traceShutdownError error
meterShutdownError error
}{
{
name: "shutdown meter and tracer successfully",
args: args{
event: new(flamingo.ShutdownEvent),
},
traceShutdownError: nil,
meterShutdownError: nil,
},
{
name: "error on shutdown meter and tracer",
args: args{
event: new(flamingo.ShutdownEvent),
},
traceShutdownError: errShutdown,
meterShutdownError: errShutdown,
},
}

for _, tt := range tests { //nolint:paralleltest // no parallel test possible because of global state manipulation
t.Run(tt.name, func(t *testing.T) {
tp := new(tracerProvider)
tp.Shutdowner.EXPECT().Shutdown(context.Background()).Once().Return(tt.traceShutdownError)
otel.SetTracerProvider(tp)

mp := new(meterProvider)
mp.Shutdowner.EXPECT().Shutdown(context.Background()).Once().Return(tt.meterShutdownError)
otel.SetMeterProvider(mp)

l := new(opentelemetry.Listener).Inject(new(flamingo.NullLogger))

l.Notify(context.Background(), tt.args.event)
})
}
}
10 changes: 0 additions & 10 deletions version.go

This file was deleted.