diff --git a/api/key/key.go b/api/key/key.go index c05a2cd43..87e200bc0 100644 --- a/api/key/key.go +++ b/api/key/key.go @@ -15,6 +15,8 @@ package key import ( + "fmt" + "go.opentelemetry.io/otel/api/core" ) @@ -71,6 +73,12 @@ func String(k, v string) core.KeyValue { return New(k).String(v) } +// Stringer creates a new key-value pair with a passed name and a string +// value generated by the passed Stringer interface. +func Stringer(k string, v fmt.Stringer) core.KeyValue { + return New(k).String(v.String()) +} + // Int creates a new key-value pair instance with a passed name and // either an int32 or an int64 value, depending on whether the int // type is 32 or 64 bits wide. @@ -84,3 +92,34 @@ func Int(k string, v int) core.KeyValue { func Uint(k string, v uint) core.KeyValue { return New(k).Uint(v) } + +// Infer creates a new key-value pair instance with a passed name and +// automatic type inference. This is slower, and not type-safe. +func Infer(k string, value interface{}) core.KeyValue { + switch v := value.(type) { + case bool: + return Bool(k, v) + case int: + return Int(k, v) + case uint: + return Uint(k, v) + case int32: + return Int32(k, v) + case int64: + return Int64(k, v) + case uint32: + return Uint32(k, v) + case uint64: + return Uint64(k, v) + case float32: + return Float32(k, v) + case float64: + return Float64(k, v) + case string: + return String(k, v) + case fmt.Stringer: + return Stringer(k, v) + default: + return String(k, fmt.Sprint(v)) + } +} diff --git a/api/key/key_test.go b/api/key/key_test.go index b832808b7..6c76d5f41 100644 --- a/api/key/key_test.go +++ b/api/key/key_test.go @@ -15,6 +15,7 @@ package key_test import ( + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -119,3 +120,85 @@ func TestKeyValueConstructors(t *testing.T) { }) } } + +func TestInfer(t *testing.T) { + builder := &strings.Builder{} + builder.WriteString("foo") + for _, testcase := range []struct { + key string + value interface{} + wantType core.ValueType + wantValue interface{} + }{ + { + key: "bool type inferred", + value: true, + wantType: core.BOOL, + wantValue: true, + }, + { + key: "int64 type inferred", + value: int64(42), + wantType: core.INT64, + wantValue: int64(42), + }, + { + key: "uint64 type inferred", + value: uint64(42), + wantType: core.UINT64, + wantValue: uint64(42), + }, + { + key: "float64 type inferred", + value: float64(42.1), + wantType: core.FLOAT64, + wantValue: 42.1, + }, + { + key: "int32 type inferred", + value: int32(42), + wantType: core.INT32, + wantValue: int32(42), + }, + { + key: "uint32 type inferred", + value: uint32(42), + wantType: core.UINT32, + wantValue: uint32(42), + }, + { + key: "float32 type inferred", + value: float32(42.1), + wantType: core.FLOAT32, + wantValue: float32(42.1), + }, + { + key: "string type inferred", + value: "foo", + wantType: core.STRING, + wantValue: "foo", + }, + { + key: "stringer type inferred", + value: builder, + wantType: core.STRING, + wantValue: "foo", + }, + { + key: "unknown value serialized as %v", + value: nil, + wantType: core.STRING, + wantValue: "", + }, + } { + t.Logf("Running test case %s", testcase.key) + kv := key.Infer(testcase.key, testcase.value) + if kv.Value.Type() != testcase.wantType { + t.Errorf("wrong value type, got %#v, expected %#v", kv.Value.Type(), testcase.wantType) + } + got := kv.Value.AsInterface() + if diff := cmp.Diff(testcase.wantValue, got); diff != "" { + t.Errorf("+got, -want: %s", diff) + } + } +}