From 06974cccf72fbb5bd19537b6e1293167a9ed5d62 Mon Sep 17 00:00:00 2001 From: zepatrik Date: Thu, 12 Sep 2024 11:55:37 +0200 Subject: [PATCH 1/2] feat(otelx): nullable attribute helpers --- otelx/attribute.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/otelx/attribute.go b/otelx/attribute.go index 61911a1e..8e64f254 100644 --- a/otelx/attribute.go +++ b/otelx/attribute.go @@ -3,7 +3,12 @@ package otelx -import "go.opentelemetry.io/otel/attribute" +import ( + "database/sql" + "fmt" + + "go.opentelemetry.io/otel/attribute" +) func StringAttrs(attrs map[string]string) []attribute.KeyValue { s := []attribute.KeyValue{} @@ -12,3 +17,39 @@ func StringAttrs(attrs map[string]string) []attribute.KeyValue { } return s } + +func AutoInt[I int | int32 | int64](k string, v I) attribute.KeyValue { + // Internally, the OpenTelemetry SDK uses int64 for all integer values anyway. + return attribute.Int64(k, int64(v)) +} + +func Nullable[V any, VN *V | sql.Null[V], A func(string, V) attribute.KeyValue](a A, k string, v VN) attribute.KeyValue { + switch v := any(v).(type) { + case *V: + if v == nil { + return attribute.String(k, "") + } + return a(k, *v) + case sql.Null[V]: + if !v.Valid { + return attribute.String(k, "") + } + return a(k, v.V) + } + return attribute.String(k, "unsupported type") +} + +func NullString[V *string | sql.Null[string]](k string, v V) attribute.KeyValue { + return Nullable(attribute.String, k, v) +} + +func NullStringer(k string, v fmt.Stringer) attribute.KeyValue { + if v == nil { + return attribute.String(k, "") + } + return attribute.String(k, v.String()) +} + +func NullInt[I int | int32 | int64, V *I | sql.Null[I]](k string, v V) attribute.KeyValue { + return Nullable[I](AutoInt, k, v) +} From 15b0ff4b869c18bfa9555d27d1e4ee091f3f9872 Mon Sep 17 00:00:00 2001 From: zepatrik Date: Thu, 12 Sep 2024 12:00:10 +0200 Subject: [PATCH 2/2] chore: minor improvements --- otelx/attribute.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/otelx/attribute.go b/otelx/attribute.go index 8e64f254..81db5b46 100644 --- a/otelx/attribute.go +++ b/otelx/attribute.go @@ -10,6 +10,8 @@ import ( "go.opentelemetry.io/otel/attribute" ) +const nullString = "" + func StringAttrs(attrs map[string]string) []attribute.KeyValue { s := []attribute.KeyValue{} for k, v := range attrs { @@ -27,16 +29,17 @@ func Nullable[V any, VN *V | sql.Null[V], A func(string, V) attribute.KeyValue]( switch v := any(v).(type) { case *V: if v == nil { - return attribute.String(k, "") + return attribute.String(k, nullString) } return a(k, *v) case sql.Null[V]: if !v.Valid { - return attribute.String(k, "") + return attribute.String(k, nullString) } return a(k, v.V) } - return attribute.String(k, "unsupported type") + // This should never happen, as the type switch above is exhaustive to the generic type VN. + return attribute.String(k, fmt.Sprintf("", v)) } func NullString[V *string | sql.Null[string]](k string, v V) attribute.KeyValue { @@ -45,7 +48,7 @@ func NullString[V *string | sql.Null[string]](k string, v V) attribute.KeyValue func NullStringer(k string, v fmt.Stringer) attribute.KeyValue { if v == nil { - return attribute.String(k, "") + return attribute.String(k, nullString) } return attribute.String(k, v.String()) }