Skip to content

Commit e44e7cb

Browse files
committed
refactor(issue-342): simplify otel app builder middleware
1 parent 54afa31 commit e44e7cb

File tree

3 files changed

+176
-221
lines changed

3 files changed

+176
-221
lines changed

appbuilder/appbuilder_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,36 @@ func TestRecover(t *testing.T) {
1919
t.Run("will return an error", func(t *testing.T) {
2020
t.Run("if the underlying App returns an error", func(t *testing.T) {
2121
buildErr := errors.New("failed to build")
22-
builder := Recover(bedrock.AppBuilderFunc[config](func(ctx context.Context, cfg config) (bedrock.App, error) {
22+
builder := Recover(bedrock.AppBuilderFunc[struct{}](func(ctx context.Context, cfg struct{}) (bedrock.App, error) {
2323
return nil, buildErr
2424
}))
2525

26-
_, err := builder.Build(context.Background(), config{})
26+
_, err := builder.Build(context.Background(), struct{}{})
2727
if !assert.Equal(t, buildErr, err) {
2828
return
2929
}
3030
})
3131

3232
t.Run("if the underlying App panics with an error value", func(t *testing.T) {
3333
buildErr := errors.New("failed to build")
34-
builder := Recover(bedrock.AppBuilderFunc[config](func(ctx context.Context, cfg config) (bedrock.App, error) {
34+
builder := Recover(bedrock.AppBuilderFunc[struct{}](func(ctx context.Context, cfg struct{}) (bedrock.App, error) {
3535
panic(buildErr)
3636
return nil, nil
3737
}))
3838

39-
_, err := builder.Build(context.Background(), config{})
39+
_, err := builder.Build(context.Background(), struct{}{})
4040
if !assert.ErrorIs(t, err, buildErr) {
4141
return
4242
}
4343
})
4444

4545
t.Run("if the underlying App panics with a non-error value", func(t *testing.T) {
46-
builder := Recover(bedrock.AppBuilderFunc[config](func(ctx context.Context, cfg config) (bedrock.App, error) {
46+
builder := Recover(bedrock.AppBuilderFunc[struct{}](func(ctx context.Context, cfg struct{}) (bedrock.App, error) {
4747
panic("hello world")
4848
return nil, nil
4949
}))
5050

51-
_, err := builder.Build(context.Background(), config{})
51+
_, err := builder.Build(context.Background(), struct{}{})
5252

5353
var perr bedrock.PanicError
5454
if !assert.ErrorAs(t, err, &perr) {

appbuilder/otel.go

Lines changed: 37 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -9,88 +9,56 @@ import (
99
"context"
1010

1111
"github.com/z5labs/bedrock"
12-
12+
"github.com/z5labs/bedrock/app"
1313
"go.opentelemetry.io/otel"
14-
"go.opentelemetry.io/otel/log"
1514
"go.opentelemetry.io/otel/log/global"
16-
"go.opentelemetry.io/otel/metric"
17-
"go.opentelemetry.io/otel/propagation"
18-
"go.opentelemetry.io/otel/trace"
1915
)
2016

21-
// TextMapPropagatorInitializer
22-
type TextMapPropagatorInitializer interface {
23-
InitTextMapPropogator(context.Context) (propagation.TextMapPropagator, error)
17+
// OTelInitializer represents anything which can initialize the OTel SDK.
18+
type OTelInitializer interface {
19+
InitializeOTel(context.Context) error
2420
}
2521

26-
// TracerProviderInitializer
27-
type TracerProviderInitializer interface {
28-
InitTracerProvider(context.Context) (trace.TracerProvider, error)
29-
}
22+
// OTel is a [bedrock.AppBuilder] middleware which initializes the OTel SDK.
23+
// It also ensures that the OTel SDK is properly shutdown when the built [bedrock.App]
24+
// stops running.
25+
func OTel[T OTelInitializer](builder bedrock.AppBuilder[T]) bedrock.AppBuilder[T] {
26+
return bedrock.AppBuilderFunc[T](func(ctx context.Context, cfg T) (bedrock.App, error) {
27+
err := cfg.InitializeOTel(ctx)
28+
if err != nil {
29+
return nil, err
30+
}
3031

31-
// MeterProviderInitializer
32-
type MeterProviderInitializer interface {
33-
InitMeterProvider(context.Context) (metric.MeterProvider, error)
34-
}
32+
base, err := builder.Build(ctx, cfg)
33+
if err != nil {
34+
return nil, err
35+
}
3536

36-
// LoggerProviderInitializer
37-
type LoggerProviderInitializer interface {
38-
InitLoggerProvider(context.Context) (log.LoggerProvider, error)
37+
base = app.WithLifecycleHooks(base, app.Lifecycle{
38+
PostRun: app.ComposeLifecycleHooks(
39+
tryShutdown(otel.GetTracerProvider()),
40+
tryShutdown(otel.GetMeterProvider()),
41+
tryShutdown(global.GetLoggerProvider()),
42+
),
43+
})
44+
return base, nil
45+
})
3946
}
4047

41-
// OTelInitializer
42-
type OTelInitializer interface {
43-
TextMapPropagatorInitializer
44-
TracerProviderInitializer
45-
MeterProviderInitializer
46-
LoggerProviderInitializer
48+
type shutdowner interface {
49+
Shutdown(context.Context) error
4750
}
4851

49-
// OTel
50-
func OTel[T OTelInitializer](builder bedrock.AppBuilder[T]) bedrock.AppBuilder[T] {
51-
return bedrock.AppBuilderFunc[T](func(ctx context.Context, cfg T) (bedrock.App, error) {
52-
fs := []func(context.Context) error{
53-
func(ctx context.Context) error {
54-
tmp, err := cfg.InitTextMapPropogator(ctx)
55-
if err != nil || tmp == nil {
56-
return err
57-
}
58-
otel.SetTextMapPropagator(tmp)
59-
return nil
60-
},
61-
func(ctx context.Context) error {
62-
tp, err := cfg.InitTracerProvider(ctx)
63-
if err != nil || tp == nil {
64-
return err
65-
}
66-
otel.SetTracerProvider(tp)
67-
return nil
68-
},
69-
func(ctx context.Context) error {
70-
mp, err := cfg.InitMeterProvider(ctx)
71-
if err != nil || mp == nil {
72-
return err
73-
}
74-
otel.SetMeterProvider(mp)
75-
return nil
76-
},
77-
func(ctx context.Context) error {
78-
lp, err := cfg.InitLoggerProvider(ctx)
79-
if err != nil || lp == nil {
80-
return err
81-
}
82-
global.SetLoggerProvider(lp)
83-
return nil
84-
},
52+
func tryShutdown(v any) app.LifecycleHookFunc {
53+
return func(ctx context.Context) error {
54+
if v == nil {
55+
return nil
8556
}
8657

87-
for _, f := range fs {
88-
err := f(ctx)
89-
if err != nil {
90-
return nil, err
91-
}
58+
s, ok := v.(shutdowner)
59+
if !ok {
60+
return nil
9261
}
93-
94-
return builder.Build(ctx, cfg)
95-
})
62+
return s.Shutdown(ctx)
63+
}
9664
}

0 commit comments

Comments
 (0)