Skip to content
Open
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
7 changes: 7 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ type InstrumentAttributesGetter func(ctx context.Context, method Method, query s
// InstrumentErrorAttributesGetter provides additional error-related attributes while recording metrics to instruments.
type InstrumentErrorAttributesGetter func(err error) []attribute.KeyValue

// OperationNameSetter allows controlling the operation name provided to metric instruments.
type OperationNameSetter func(ctx context.Context, method Method, query string) string
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use OperationNameFormatter to align with the naming of SpanNameFormatter?

I also think this method can be used for spans.


// SpanFilter is a function that determines whether a span should be created for a given SQL operation.
// It returns true if the span should be created, or false to skip span creation.
type SpanFilter func(ctx context.Context, method Method, query string, args []driver.NamedValue) bool
Expand Down Expand Up @@ -92,6 +95,10 @@ type config struct {

InstrumentErrorAttributesGetter InstrumentErrorAttributesGetter

// OperationNameSetter will be called to produce the operation name for metric instruments.
// If nil, the default behavior uses the method name.
OperationNameSetter OperationNameSetter

// DisableSkipErrMeasurement, if set to true, will suppress driver.ErrSkip as an error status in metrics.
// The metric measurement will be recorded as status=ok.
// Default is false
Expand Down
8 changes: 8 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,11 @@ func WithInstrumentErrorAttributesGetter(instrumentErrorAttributesGetter Instrum
cfg.InstrumentErrorAttributesGetter = instrumentErrorAttributesGetter
})
}

// WithOperationNameSetter takes OperationNameSetter that will be called to produce the operation name for metric instruments.
// If not set, the default behavior uses the method name.
func WithOperationNameSetter(operationNameSetter OperationNameSetter) Option {
return OptionFunc(func(cfg *config) {
cfg.OperationNameSetter = operationNameSetter
})
}
15 changes: 15 additions & 0 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ func TestOptions(t *testing.T) {
return []attribute.KeyValue{attribute.String("errorKey", "errorVal")}
}

dummyOperationNameSetter := func(_ context.Context, _ Method, _ string) string {
return "custom_operation"
}

testCases := []struct {
name string
options []Option
Expand Down Expand Up @@ -109,6 +113,11 @@ func TestOptions(t *testing.T) {
options: []Option{WithInstrumentErrorAttributesGetter(dummyErrorAttributesGetter)},
expectedConfig: config{InstrumentErrorAttributesGetter: dummyErrorAttributesGetter},
},
{
name: "WithOperationNameSetter",
options: []Option{WithOperationNameSetter(dummyOperationNameSetter)},
expectedConfig: config{OperationNameSetter: dummyOperationNameSetter},
},
{
name: "WithAttributes multiple calls should accumulate",
options: []Option{
Expand Down Expand Up @@ -207,6 +216,12 @@ func TestOptions(t *testing.T) {
tc.expectedConfig.InstrumentErrorAttributesGetter(assert.AnError),
cfg.InstrumentErrorAttributesGetter(assert.AnError),
)
case tc.expectedConfig.OperationNameSetter != nil:
assert.Equal(
t,
tc.expectedConfig.OperationNameSetter(context.Background(), "", ""),
cfg.OperationNameSetter(context.Background(), "", ""),
)
default:
assert.Equal(t, tc.expectedConfig, cfg)
}
Expand Down
22 changes: 16 additions & 6 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,14 @@ func recordLegacyLatency(
duration time.Duration,
attributes []attribute.KeyValue,
method Method,
query string,
err error,
) {
attributes = append(attributes, queryMethodKey.String(string(method)))
operationName := string(method)
if cfg.OperationNameSetter != nil {
operationName = cfg.OperationNameSetter(ctx, method, query)
}
attributes = append(attributes, queryMethodKey.String(operationName))

if err != nil {
if cfg.DisableSkipErrMeasurement && errors.Is(err, driver.ErrSkip) {
Expand All @@ -94,9 +99,14 @@ func recordDuration(
duration time.Duration,
attributes []attribute.KeyValue,
method Method,
query string,
err error,
) {
attributes = append(attributes, semconv.DBOperationName(string(method)))
operationName := string(method)
if cfg.OperationNameSetter != nil {
operationName = cfg.OperationNameSetter(ctx, method, query)
}
attributes = append(attributes, semconv.DBOperationName(operationName))
if err != nil && (!cfg.DisableSkipErrMeasurement || !errors.Is(err, driver.ErrSkip)) {
attributes = append(attributes, internalsemconv.ErrorTypeAttributes(err)...)
}
Expand Down Expand Up @@ -147,13 +157,13 @@ func recordMetric(

switch cfg.SemConvStabilityOptIn {
case internalsemconv.OTelSemConvStabilityOptInStable:
recordDuration(ctx, instruments, cfg, duration, attributes, method, err)
recordDuration(ctx, instruments, cfg, duration, attributes, method, query, err)
case internalsemconv.OTelSemConvStabilityOptInDup:
// Intentionally emit both legacy and new metrics for backward compatibility.
recordLegacyLatency(ctx, instruments, cfg, duration, slices.Clone(attributes), method, err)
recordDuration(ctx, instruments, cfg, duration, attributes, method, err)
recordLegacyLatency(ctx, instruments, cfg, duration, slices.Clone(attributes), method, query, err)
recordDuration(ctx, instruments, cfg, duration, attributes, method, query, err)
case internalsemconv.OTelSemConvStabilityOptInNone:
recordLegacyLatency(ctx, instruments, cfg, duration, attributes, method, err)
recordLegacyLatency(ctx, instruments, cfg, duration, attributes, method, query, err)
}
}
}
Expand Down
Loading