Handle special values like Inf and NaN gracefuly
This commit is contained in:
parent
2aa3c3ae4f
commit
87aceba511
|
@ -71,3 +71,38 @@ func BenchmarkLogFields(b *testing.B) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkLogFieldType(b *testing.B) {
|
||||
types := map[string]func(e *Event) *Event{
|
||||
"Int": func(e *Event) *Event {
|
||||
return e.Int("int", 1)
|
||||
},
|
||||
"Float32": func(e *Event) *Event {
|
||||
return e.Float32("float", 1)
|
||||
},
|
||||
"Str": func(e *Event) *Event {
|
||||
return e.Str("str", "foo")
|
||||
},
|
||||
"Err": func(e *Event) *Event {
|
||||
return e.Err(errExample)
|
||||
},
|
||||
"Time": func(e *Event) *Event {
|
||||
return e.Time("time", time.Time{})
|
||||
},
|
||||
"Dur": func(e *Event) *Event {
|
||||
return e.Dur("dur", 1*time.Millisecond)
|
||||
},
|
||||
}
|
||||
logger := New(ioutil.Discard)
|
||||
b.ResetTimer()
|
||||
for name := range types {
|
||||
f := types[name]
|
||||
b.Run(name, func(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
f(logger.Info()).Msg("")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
31
field.go
31
field.go
|
@ -3,6 +3,7 @@ package zerolog
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -157,6 +158,9 @@ func appendErrorsKey(dst []byte, key string, errs []error) []byte {
|
|||
}
|
||||
|
||||
func appendError(dst []byte, err error) []byte {
|
||||
if err == nil {
|
||||
return dst
|
||||
}
|
||||
return appendErrorKey(dst, ErrorFieldName, err)
|
||||
}
|
||||
|
||||
|
@ -369,8 +373,23 @@ func appendUints64(dst []byte, key string, vals []uint64) []byte {
|
|||
return dst
|
||||
}
|
||||
|
||||
func appendFloat(dst []byte, val float64, bitSize int) []byte {
|
||||
// JSON does not permit NaN or Infinity. A typical JSON encoder would fail
|
||||
// with an error, but a logging library wants the data to get thru so we
|
||||
// make a tradeoff and store those types as string.
|
||||
switch {
|
||||
case math.IsNaN(val):
|
||||
return append(dst, `"NaN"`...)
|
||||
case math.IsInf(val, 1):
|
||||
return append(dst, `"+Inf"`...)
|
||||
case math.IsInf(val, -1):
|
||||
return append(dst, `"-Inf"`...)
|
||||
}
|
||||
return strconv.AppendFloat(dst, val, 'f', -1, bitSize)
|
||||
}
|
||||
|
||||
func appendFloat32(dst []byte, key string, val float32) []byte {
|
||||
return strconv.AppendFloat(appendKey(dst, key), float64(val), 'f', -1, 32)
|
||||
return appendFloat(appendKey(dst, key), float64(val), 32)
|
||||
}
|
||||
|
||||
func appendFloats32(dst []byte, key string, vals []float32) []byte {
|
||||
|
@ -378,10 +397,10 @@ func appendFloats32(dst []byte, key string, vals []float32) []byte {
|
|||
return append(appendKey(dst, key), '[', ']')
|
||||
}
|
||||
dst = append(appendKey(dst, key), '[')
|
||||
dst = strconv.AppendFloat(dst, float64(vals[0]), 'f', -1, 32)
|
||||
dst = appendFloat(dst, float64(vals[0]), 32)
|
||||
if len(vals) > 1 {
|
||||
for _, val := range vals[1:] {
|
||||
dst = strconv.AppendFloat(append(dst, ','), float64(val), 'f', -1, 32)
|
||||
dst = appendFloat(append(dst, ','), float64(val), 32)
|
||||
}
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
|
@ -389,7 +408,7 @@ func appendFloats32(dst []byte, key string, vals []float32) []byte {
|
|||
}
|
||||
|
||||
func appendFloat64(dst []byte, key string, val float64) []byte {
|
||||
return strconv.AppendFloat(appendKey(dst, key), val, 'f', -1, 32)
|
||||
return appendFloat(appendKey(dst, key), val, 64)
|
||||
}
|
||||
|
||||
func appendFloats64(dst []byte, key string, vals []float64) []byte {
|
||||
|
@ -397,10 +416,10 @@ func appendFloats64(dst []byte, key string, vals []float64) []byte {
|
|||
return append(appendKey(dst, key), '[', ']')
|
||||
}
|
||||
dst = append(appendKey(dst, key), '[')
|
||||
dst = strconv.AppendFloat(dst, vals[0], 'f', -1, 32)
|
||||
dst = appendFloat(dst, vals[0], 32)
|
||||
if len(vals) > 1 {
|
||||
for _, val := range vals[1:] {
|
||||
dst = strconv.AppendFloat(append(dst, ','), val, 'f', -1, 32)
|
||||
dst = appendFloat(append(dst, ','), val, 64)
|
||||
}
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package zerolog
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_appendFloat64(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input float64
|
||||
want []byte
|
||||
}{
|
||||
{"-Inf", math.Inf(-1), []byte(`"foo":"-Inf"`)},
|
||||
{"+Inf", math.Inf(1), []byte(`"foo":"+Inf"`)},
|
||||
{"NaN", math.NaN(), []byte(`"foo":"NaN"`)},
|
||||
{"0", 0, []byte(`"foo":0`)},
|
||||
{"-1.1", -1.1, []byte(`"foo":-1.1`)},
|
||||
{"1e20", 1e20, []byte(`"foo":100000000000000000000`)},
|
||||
{"1e21", 1e21, []byte(`"foo":1000000000000000000000`)},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := appendFloat32([]byte{}, "foo", float32(tt.input)); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("appendFloat32() = %s, want %s", got, tt.want)
|
||||
}
|
||||
if got := appendFloat64([]byte{}, "foo", tt.input); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("appendFloat32() = %s, want %s", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue