fix time, duration, slice types on logx.Field (#1868)
* chore: refine tests * fix #1866
This commit is contained in:
@@ -113,8 +113,36 @@ func Errorw(msg string, fields ...LogField) {
|
|||||||
// Field returns a LogField for the given key and value.
|
// Field returns a LogField for the given key and value.
|
||||||
func Field(key string, value interface{}) LogField {
|
func Field(key string, value interface{}) LogField {
|
||||||
switch val := value.(type) {
|
switch val := value.(type) {
|
||||||
|
case error:
|
||||||
|
return LogField{Key: key, Value: val.Error()}
|
||||||
|
case []error:
|
||||||
|
var errs []string
|
||||||
|
for _, err := range val {
|
||||||
|
errs = append(errs, err.Error())
|
||||||
|
}
|
||||||
|
return LogField{Key: key, Value: errs}
|
||||||
case time.Duration:
|
case time.Duration:
|
||||||
return LogField{Key: key, Value: fmt.Sprint(val)}
|
return LogField{Key: key, Value: fmt.Sprint(val)}
|
||||||
|
case []time.Duration:
|
||||||
|
var durs []string
|
||||||
|
for _, dur := range val {
|
||||||
|
durs = append(durs, fmt.Sprint(dur))
|
||||||
|
}
|
||||||
|
return LogField{Key: key, Value: durs}
|
||||||
|
case []time.Time:
|
||||||
|
var times []string
|
||||||
|
for _, t := range val {
|
||||||
|
times = append(times, fmt.Sprint(t))
|
||||||
|
}
|
||||||
|
return LogField{Key: key, Value: times}
|
||||||
|
case fmt.Stringer:
|
||||||
|
return LogField{Key: key, Value: val.String()}
|
||||||
|
case []fmt.Stringer:
|
||||||
|
var strs []string
|
||||||
|
for _, str := range val {
|
||||||
|
strs = append(strs, str.String())
|
||||||
|
}
|
||||||
|
return LogField{Key: key, Value: strs}
|
||||||
default:
|
default:
|
||||||
return LogField{Key: key, Value: val}
|
return LogField{Key: key, Value: val}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -92,6 +93,86 @@ func (mw *mockWriter) String() string {
|
|||||||
return mw.builder.String()
|
return mw.builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestField(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
f LogField
|
||||||
|
want map[string]interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "error",
|
||||||
|
f: Field("foo", errors.New("bar")),
|
||||||
|
want: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "errors",
|
||||||
|
f: Field("foo", []error{errors.New("bar"), errors.New("baz")}),
|
||||||
|
want: map[string]interface{}{
|
||||||
|
"foo": []interface{}{"bar", "baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strings",
|
||||||
|
f: Field("foo", []string{"bar", "baz"}),
|
||||||
|
want: map[string]interface{}{
|
||||||
|
"foo": []interface{}{"bar", "baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duration",
|
||||||
|
f: Field("foo", time.Second),
|
||||||
|
want: map[string]interface{}{
|
||||||
|
"foo": "1s",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "durations",
|
||||||
|
f: Field("foo", []time.Duration{time.Second, 2 * time.Second}),
|
||||||
|
want: map[string]interface{}{
|
||||||
|
"foo": []interface{}{"1s", "2s"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "times",
|
||||||
|
f: Field("foo", []time.Time{
|
||||||
|
time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC),
|
||||||
|
}),
|
||||||
|
want: map[string]interface{}{
|
||||||
|
"foo": []interface{}{"2020-01-01 00:00:00 +0000 UTC", "2020-01-02 00:00:00 +0000 UTC"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stringer",
|
||||||
|
f: Field("foo", ValStringer{val: "bar"}),
|
||||||
|
want: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stringers",
|
||||||
|
f: Field("foo", []fmt.Stringer{ValStringer{val: "bar"}, ValStringer{val: "baz"}}),
|
||||||
|
want: map[string]interface{}{
|
||||||
|
"foo": []interface{}{"bar", "baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
w := new(mockWriter)
|
||||||
|
old := writer.Swap(w)
|
||||||
|
defer writer.Store(old)
|
||||||
|
|
||||||
|
Infow("foo", test.f)
|
||||||
|
validateFields(t, w.String(), test.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFileLineFileMode(t *testing.T) {
|
func TestFileLineFileMode(t *testing.T) {
|
||||||
w := new(mockWriter)
|
w := new(mockWriter)
|
||||||
old := writer.Swap(w)
|
old := writer.Swap(w)
|
||||||
@@ -675,3 +756,18 @@ type ValStringer struct {
|
|||||||
func (v ValStringer) String() string {
|
func (v ValStringer) String() string {
|
||||||
return v.val
|
return v.val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateFields(t *testing.T, content string, fields map[string]interface{}) {
|
||||||
|
var m map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(content), &m); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range fields {
|
||||||
|
if reflect.TypeOf(v).Kind() == reflect.Slice {
|
||||||
|
assert.EqualValues(t, v, m[k])
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, v, m[k], content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user