diff --git a/bridges/otellogrus/hook.go b/bridges/otellogrus/hook.go index 5f14ae138c5..75894fadc06 100644 --- a/bridges/otellogrus/hook.go +++ b/bridges/otellogrus/hook.go @@ -40,6 +40,7 @@ package otellogrus // import "go.opentelemetry.io/contrib/bridges/otellogrus" import ( "fmt" + "reflect" "github.com/sirupsen/logrus" @@ -188,27 +189,67 @@ func convertFields(fields logrus.Fields) []log.KeyValue { i := 0 for k, v := range fields { - kvs[i] = convertKeyValue(k, v) + kvs[i] = log.KeyValue{ + Key: k, + Value: convertValue(v), + } i++ } return kvs } -func convertKeyValue(k string, v interface{}) log.KeyValue { +func convertValue(v interface{}) log.Value { switch v := v.(type) { case bool: - return log.Bool(k, v) + return log.BoolValue(v) case []byte: - return log.Bytes(k, v) + return log.BytesValue(v) case float64: - return log.Float64(k, v) + return log.Float64Value(v) case int: - return log.Int(k, v) + return log.IntValue(v) case int64: - return log.Int64(k, v) + return log.Int64Value(v) case string: - return log.String(k, v) - default: - return log.String(k, fmt.Sprintf("%s", v)) + return log.StringValue(v) + } + + t := reflect.TypeOf(v) + if t == nil { + return log.Value{} + } + val := reflect.ValueOf(v) + switch t.Kind() { + case reflect.Struct: + return log.StringValue(fmt.Sprintf("%+v", v)) + case reflect.Slice, reflect.Array: + items := make([]log.Value, 0, val.Len()) + for i := 0; i < val.Len(); i++ { + items = append(items, convertValue(val.Index(i).Interface())) + } + return log.SliceValue(items...) + case reflect.Map: + kvs := make([]log.KeyValue, 0, val.Len()) + for _, k := range val.MapKeys() { + var key string + // If the key is a struct, use %+v to print the struct fields. + if k.Kind() == reflect.Struct { + key = fmt.Sprintf("%+v", k.Interface()) + } else { + key = fmt.Sprintf("%v", k.Interface()) + } + kvs = append(kvs, log.KeyValue{ + Key: key, + Value: convertValue(val.MapIndex(k).Interface()), + }) + } + return log.MapValue(kvs...) + case reflect.Ptr, reflect.Interface: + if val.IsNil() { + return log.Value{} + } + return convertValue(val.Elem().Interface()) } + + return log.StringValue(fmt.Sprintf("unhandled attribute type: (%s) %+v", t, v)) } diff --git a/bridges/otellogrus/hook_test.go b/bridges/otellogrus/hook_test.go index 9524b627a89..586ebb77284 100644 --- a/bridges/otellogrus/hook_test.go +++ b/bridges/otellogrus/hook_test.go @@ -133,53 +133,85 @@ func TestConvertFields(t *testing.T) { wantKeyValue []log.KeyValue }{ { - name: "with a boolean", - + name: "with a boolean", fields: logrus.Fields{"hello": true}, wantKeyValue: []log.KeyValue{ log.Bool("hello", true), }, }, { - name: "with a bytes array", - + name: "with a bytes array", fields: logrus.Fields{"hello": []byte("world")}, wantKeyValue: []log.KeyValue{ log.Bytes("hello", []byte("world")), }, }, { - name: "with a float64", - + name: "with a float64", fields: logrus.Fields{"hello": 6.5}, wantKeyValue: []log.KeyValue{ log.Float64("hello", 6.5), }, }, { - name: "with an int", - + name: "with an int", fields: logrus.Fields{"hello": 42}, wantKeyValue: []log.KeyValue{ log.Int("hello", 42), }, }, { - name: "with an int64", - + name: "with an int64", fields: logrus.Fields{"hello": int64(42)}, wantKeyValue: []log.KeyValue{ log.Int64("hello", 42), }, }, { - name: "with a string", - + name: "with a string", fields: logrus.Fields{"hello": "world"}, wantKeyValue: []log.KeyValue{ log.String("hello", "world"), }, }, + { + name: "with nil", + fields: logrus.Fields{"hello": nil}, + wantKeyValue: []log.KeyValue{ + log.KeyValue{Key: "hello", Value: log.Value{}}, + }, + }, + { + name: "with a struct", + fields: logrus.Fields{"hello": struct{ Name string }{Name: "foobar"}}, + wantKeyValue: []log.KeyValue{ + log.String("hello", "{Name:foobar}"), + }, + }, + { + name: "with a slice", + fields: logrus.Fields{"hello": []string{"foo", "bar"}}, + wantKeyValue: []log.KeyValue{ + log.Slice("hello", + log.StringValue("foo"), + log.StringValue("bar"), + ), + }, + }, + { + name: "with a map", + fields: logrus.Fields{"hello": map[string]int{"answer": 42}}, + wantKeyValue: []log.KeyValue{ + log.Map("hello", log.Int("answer", 42)), + }, + }, + { + name: "with a pointer to struct", + fields: logrus.Fields{"hello": &struct{ Name string }{Name: "foobar"}}, + wantKeyValue: []log.KeyValue{ + log.String("hello", "{Name:foobar}"), + }, + }, } { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, convertFields(tt.fields), tt.wantKeyValue)