Handle special values like Inf and NaN gracefuly

This commit is contained in:
Olivier Poitrey 2017-07-25 16:55:47 -07:00
parent 2aa3c3ae4f
commit 87aceba511
3 changed files with 93 additions and 6 deletions

View File

@ -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("")
}
})
})
}
}

View File

@ -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, ']')

33
field_test.go Normal file
View File

@ -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)
}
})
}
}