Skip to content

Commit 8eb2506

Browse files
authored
feat: add trancing to service hooks (#3325)
1 parent e7647db commit 8eb2506

File tree

7 files changed

+254
-32
lines changed

7 files changed

+254
-32
lines changed

app/common/customer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log/slog"
66

77
"github.com/google/wire"
8+
"go.opentelemetry.io/otel/trace"
89

910
"github.com/openmeterio/openmeter/app/config"
1011
"github.com/openmeterio/openmeter/openmeter/billing"
@@ -61,6 +62,7 @@ type CustomerSubjectHook customerservicehooks.SubjectCustomerHook
6162
func NewCustomerSubjectServiceHook(
6263
config config.CustomerConfiguration,
6364
logger *slog.Logger,
65+
tracer trace.Tracer,
6466
subjectService subject.Service,
6567
customerService customer.Service,
6668
customerOverrideService billing.CustomerOverrideService,
@@ -74,6 +76,7 @@ func NewCustomerSubjectServiceHook(
7476
Customer: customerService,
7577
CustomerOverride: customerOverrideService,
7678
Logger: logger,
79+
Tracer: tracer,
7780
IgnoreErrors: config.IgnoreErrors,
7881
})
7982
if err != nil {

app/common/subject.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log/slog"
77

88
"github.com/google/wire"
9+
"go.opentelemetry.io/otel/trace"
910

1011
"github.com/openmeterio/openmeter/app/config"
1112
"github.com/openmeterio/openmeter/openmeter/customer"
@@ -65,10 +66,12 @@ func NewSubjectCustomerHook(
6566
subject subject.Service,
6667
customer customer.Service,
6768
logger *slog.Logger,
69+
tracer trace.Tracer,
6870
) (subjecthooks.CustomerSubjectHook, error) {
6971
h, err := subjecthooks.NewCustomerSubjectHook(subjecthooks.CustomerSubjectHookConfig{
7072
Subject: subject,
7173
Logger: logger,
74+
Tracer: tracer,
7275
})
7376
if err != nil {
7477
return nil, fmt.Errorf("failed to create customer subject hook: %w", err)

cmd/server/wire_gen.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openmeter/customer/service/hooks/subjectcustomer.go

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212
"strings"
1313

1414
"github.com/samber/lo"
15+
"go.opentelemetry.io/otel/attribute"
16+
otelcodes "go.opentelemetry.io/otel/codes"
17+
"go.opentelemetry.io/otel/trace"
1518

1619
"github.com/openmeterio/openmeter/openmeter/app"
1720
appstripeentity "github.com/openmeterio/openmeter/openmeter/app/stripe/entity"
@@ -34,6 +37,7 @@ type subjectCustomerHook struct {
3437

3538
provisioner *CustomerProvisioner
3639
logger *slog.Logger
40+
tracer trace.Tracer
3741

3842
ignoreErrors bool
3943
}
@@ -56,11 +60,33 @@ func (s subjectCustomerHook) provision(ctx context.Context, sub *subject.Subject
5660
}
5761

5862
func (s subjectCustomerHook) PostCreate(ctx context.Context, sub *subject.Subject) error {
59-
return s.provision(ctx, sub)
63+
ctx, span := s.tracer.Start(ctx, "subject_customer_hook.post_create")
64+
defer span.End()
65+
66+
err := s.provision(ctx, sub)
67+
if err != nil {
68+
span.SetStatus(otelcodes.Error, "failed to provision customer for subject")
69+
span.RecordError(err)
70+
} else {
71+
span.SetStatus(otelcodes.Ok, "customer provisioned for subject")
72+
}
73+
74+
return err
6075
}
6176

6277
func (s subjectCustomerHook) PostUpdate(ctx context.Context, sub *subject.Subject) error {
63-
return s.provision(ctx, sub)
78+
ctx, span := s.tracer.Start(ctx, "subject_customer_hook.post_update")
79+
defer span.End()
80+
81+
err := s.provision(ctx, sub)
82+
if err != nil {
83+
span.SetStatus(otelcodes.Error, "failed to provision customer for subject")
84+
span.RecordError(err)
85+
} else {
86+
span.SetStatus(otelcodes.Ok, "customer provisioned for subject")
87+
}
88+
89+
return err
6490
}
6591

6692
func NewSubjectCustomerHook(config SubjectCustomerHookConfig) (SubjectCustomerHook, error) {
@@ -72,6 +98,7 @@ func NewSubjectCustomerHook(config SubjectCustomerHookConfig) (SubjectCustomerHo
7298
Customer: config.Customer,
7399
CustomerOverride: config.CustomerOverride,
74100
Logger: config.Logger,
101+
Tracer: config.Tracer,
75102
})
76103
if err != nil {
77104
return nil, fmt.Errorf("failed to initialize customer provisioner: %w", err)
@@ -80,6 +107,7 @@ func NewSubjectCustomerHook(config SubjectCustomerHookConfig) (SubjectCustomerHo
80107
return &subjectCustomerHook{
81108
provisioner: provisioner,
82109
logger: config.Logger.With("subsystem", "subject_customer_provisioner"),
110+
tracer: config.Tracer,
83111
ignoreErrors: config.IgnoreErrors,
84112
}, nil
85113
}
@@ -88,6 +116,7 @@ type SubjectCustomerHookConfig struct {
88116
Customer customer.Service
89117
CustomerOverride billing.CustomerOverrideService
90118
Logger *slog.Logger
119+
Tracer trace.Tracer
91120

92121
// IgnoreErrors if set to true makes the hooks ignore (not returning error)
93122
IgnoreErrors bool
@@ -108,6 +137,10 @@ func (c SubjectCustomerHookConfig) Validate() error {
108137
errs = append(errs, fmt.Errorf("logger is required"))
109138
}
110139

140+
if c.Tracer == nil {
141+
errs = append(errs, fmt.Errorf("tracer is required"))
142+
}
143+
111144
return errors.Join(errs...)
112145
}
113146

@@ -149,6 +182,7 @@ type CustomerProvisionerConfig struct {
149182
Customer customer.Service
150183
CustomerOverride billing.CustomerOverrideService
151184
Logger *slog.Logger
185+
Tracer trace.Tracer
152186
}
153187

154188
func (c CustomerProvisionerConfig) Validate() error {
@@ -166,6 +200,10 @@ func (c CustomerProvisionerConfig) Validate() error {
166200
errs = append(errs, fmt.Errorf("logger is required"))
167201
}
168202

203+
if c.Tracer == nil {
204+
errs = append(errs, fmt.Errorf("tracer is required"))
205+
}
206+
169207
return errors.Join(errs...)
170208
}
171209

@@ -178,13 +216,15 @@ func NewCustomerProvisioner(config CustomerProvisionerConfig) (*CustomerProvisio
178216
customer: config.Customer,
179217
customerOverride: config.CustomerOverride,
180218
logger: config.Logger.With("subsystem", "customer.provisioner"),
219+
tracer: config.Tracer,
181220
}, nil
182221
}
183222

184223
type CustomerProvisioner struct {
185224
customer customer.Service
186225
customerOverride billing.CustomerOverrideService
187226
logger *slog.Logger
227+
tracer trace.Tracer
188228
}
189229

190230
var ErrCustomerKeyConflict = errors.New("customer key conflict")
@@ -236,6 +276,26 @@ func (p CustomerProvisioner) EnsureCustomer(ctx context.Context, sub *subject.Su
236276
return nil, errors.New("failed to provision customer for subject: subject is nil")
237277
}
238278

279+
var err error
280+
281+
ctx, span := p.tracer.Start(ctx, "customer_provisioner.ensure_customer")
282+
defer func() {
283+
if err != nil {
284+
span.SetStatus(otelcodes.Error, err.Error())
285+
span.RecordError(err)
286+
} else {
287+
span.SetStatus(otelcodes.Ok, "customer provisioned for subject")
288+
}
289+
290+
span.End()
291+
}()
292+
293+
span.SetAttributes(
294+
attribute.String("subject.id", sub.Id),
295+
attribute.String("subject.key", sub.Key),
296+
attribute.String("subject.stripe_customer_id", lo.FromPtrOr(sub.StripeCustomerId, "nil")),
297+
)
298+
239299
var keyConflict bool
240300

241301
cus, err := p.getCustomerForSubject(ctx, sub)
@@ -263,6 +323,11 @@ func (p CustomerProvisioner) EnsureCustomer(ctx context.Context, sub *subject.Su
263323
}
264324

265325
if cus != nil {
326+
span.AddEvent("found customer", trace.WithAttributes(
327+
attribute.String("customer.id", cus.ID),
328+
attribute.String("customer.key", lo.FromPtrOr(cus.Key, "nil")),
329+
))
330+
266331
if CmpSubjectCustomer(sub, cus) {
267332
return cus, nil
268333
}
@@ -313,11 +378,13 @@ func (p CustomerProvisioner) EnsureCustomer(ctx context.Context, sub *subject.Su
313378
customerID.Namespace, customerID.ID, err)
314379
}
315380

381+
span.AddEvent("updated customer")
382+
316383
return cus, nil
317384
}
318385

319386
// Create Customer for Subject in case there is none to be found
320-
return p.customer.CreateCustomer(
387+
cus, err = p.customer.CreateCustomer(
321388
subjectservicehooks.NewContextWithSkipSubjectCustomer(ctx),
322389
customer.CreateCustomerInput{
323390
Namespace: sub.Namespace,
@@ -341,6 +408,17 @@ func (p CustomerProvisioner) EnsureCustomer(ctx context.Context, sub *subject.Su
341408
Annotation: &annotations,
342409
},
343410
})
411+
if err != nil {
412+
return nil, fmt.Errorf("failed to create customer for subject [namespace=%s subject.key=%s]: %w",
413+
sub.Namespace, sub.Key, err)
414+
}
415+
416+
span.AddEvent("created customer", trace.WithAttributes(
417+
attribute.String("customer.id", cus.ID),
418+
attribute.String("customer.key", lo.FromPtrOr(cus.Key, "nil")),
419+
))
420+
421+
return cus, err
344422
}
345423

346424
type InvalidPaymentAppError struct {
@@ -353,6 +431,25 @@ func (e InvalidPaymentAppError) Error() string {
353431
}
354432

355433
func (p CustomerProvisioner) EnsureStripeCustomer(ctx context.Context, customerID customer.CustomerID, stripeCustomerID string) error {
434+
var err error
435+
436+
ctx, span := p.tracer.Start(ctx, "customer_provisioner.ensure_stripe_customer")
437+
defer func() {
438+
if err != nil {
439+
span.SetStatus(otelcodes.Error, err.Error())
440+
span.RecordError(err)
441+
} else {
442+
span.SetStatus(otelcodes.Ok, "stripe customer provisioned")
443+
}
444+
445+
span.End()
446+
}()
447+
448+
span.SetAttributes(
449+
attribute.String("customer.id", customerID.ID),
450+
attribute.String("customer.namespace", customerID.Namespace),
451+
)
452+
356453
customerOverride, err := p.customerOverride.GetCustomerOverride(ctx, billing.GetCustomerOverrideInput{
357454
Customer: customerID,
358455
Expand: billing.CustomerOverrideExpand{
@@ -366,6 +463,11 @@ func (p CustomerProvisioner) EnsureStripeCustomer(ctx context.Context, customerI
366463

367464
profile := customerOverride.MergedProfile
368465

466+
span.AddEvent("fetched customer billing profile", trace.WithAttributes(
467+
attribute.String("profile.id", profile.ID),
468+
attribute.String("profile.namespace", profile.Namespace),
469+
))
470+
369471
if profile.Apps == nil {
370472
return fmt.Errorf("failed to setup stripe customer id for customer [namespace=%s customer.id=%s]: apps profile is nil",
371473
customerID.Namespace, customerID.ID)
@@ -389,6 +491,11 @@ func (p CustomerProvisioner) EnsureStripeCustomer(ctx context.Context, customerI
389491
customerID.Namespace, customerID.ID, err)
390492
}
391493

494+
span.AddEvent("updated stripe customer data", trace.WithAttributes(
495+
attribute.String("app.id", profile.Apps.Payment.GetID().ID),
496+
attribute.String("app.namespace", profile.Apps.Payment.GetID().Namespace),
497+
))
498+
392499
return nil
393500
}
394501

openmeter/customer/service/hooks/subjectcustomer_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/samber/lo"
1212
"github.com/stretchr/testify/assert"
1313
"github.com/stretchr/testify/require"
14+
"go.opentelemetry.io/otel/trace"
15+
"go.opentelemetry.io/otel/trace/noop"
1416

1517
"github.com/openmeterio/openmeter/openmeter/customer"
1618
customeradapter "github.com/openmeterio/openmeter/openmeter/customer/adapter"
@@ -42,6 +44,7 @@ func TestCustomerProvisioner_EnsureCustomer(t *testing.T) {
4244
customer: env.CustomerService,
4345
customerOverride: nil,
4446
logger: env.Logger,
47+
tracer: env.Tracer,
4548
}
4649

4750
ctx := t.Context()
@@ -274,6 +277,7 @@ var NewTestNamespace = NewTestULID
274277

275278
type TestEnv struct {
276279
Logger *slog.Logger
280+
Tracer trace.Tracer
277281
SubjectService subject.Service
278282
CustomerService customer.Service
279283

@@ -319,6 +323,8 @@ func NewTestEnv(t *testing.T) *TestEnv {
319323
// Init logger
320324
logger := testutils.NewDiscardLogger(t)
321325

326+
tracer := noop.NewTracerProvider().Tracer("test_env")
327+
322328
// Init database
323329
db := testutils.InitPostgresDB(t)
324330
client := db.EntDriver.Client()
@@ -357,6 +363,7 @@ func NewTestEnv(t *testing.T) *TestEnv {
357363

358364
return &TestEnv{
359365
Logger: logger,
366+
Tracer: tracer,
360367
SubjectService: subjectService,
361368
CustomerService: customerService,
362369
Client: client,

0 commit comments

Comments
 (0)