1
0
mirror of https://github.com/rs/zerolog synced 2026-06-08 17:13:30 +00:00

Compare commits

..

6 Commits

Author SHA1 Message Date
Olivier Poitrey 90fdb63d84 Add missing console file 2017-08-05 19:59:04 -07:00
Olivier Poitrey a83efb6080 Add a ConsoleWriter to output pretty log on the console during dev 2017-08-05 19:47:55 -07:00
Olivier Poitrey 89ff8dbc5f Small comment fix 2017-07-26 23:42:12 -07:00
Olivier Poitrey 614d88bbf8 Add support for typed array. 2017-07-26 00:30:03 -07:00
Olivier Poitrey 6cdd9977c4 Refactor JSON encoding code 2017-07-25 22:05:32 -07:00
Olivier Poitrey fdbdb09630 Add support for custom object marshaling 2017-07-25 18:41:05 -07:00
19 changed files with 1253 additions and 668 deletions
+16 -2
View File
@@ -8,16 +8,18 @@ Zerolog's API is designed to provide both a great developer experience and stunn
The uber's [zap](https://godoc.org/go.uber.org/zap) library pioneered this approach. Zerolog is taking this concept to the next level with simpler to use API and even better performance.
To keep the code base and the API simple, zerolog focuses on JSON logging only. As [suggested on reddit](https://www.reddit.com/r/golang/comments/6c9k7n/zerolog_is_now_faster_than_zap/), you may use tools like [humanlog](https://github.com/aybabtme/humanlog) to pretty print JSON on the console during development.
To keep the code base and the API simple, zerolog focuses on JSON logging only. Pretty logging on the console is made possible using the provided `zerolog.ConsoleWriter`.
## Features
* Blazing fast
* Low to zero allocation
* Level logging
* Sampling
* Contextual fields
* `context.Context` integration
* `net/http` helpers
* Pretty logging for development
## Usage
@@ -96,6 +98,18 @@ if e := log.Debug(); e.Enabled() {
// Output: {"level":"info","time":1494567715,"message":"routed message"}
```
### Pretty logging
```go
if isConsole {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
}
log.Info().Str("foo", "bar").Msg("Hello world")
// Output: 1494567715 |INFO| Hello world foo=bar
```
### Sub dictionary
```go
+169
View File
@@ -0,0 +1,169 @@
package zerolog
import (
"sync"
"time"
"github.com/rs/zerolog/internal/json"
)
var arrayPool = &sync.Pool{
New: func() interface{} {
return &Array{
buf: make([]byte, 0, 500),
}
},
}
type Array struct {
buf []byte
}
// Arr creates an array to be added to an Event or Context.
func Arr() *Array {
a := arrayPool.Get().(*Array)
a.buf = a.buf[:0]
return a
}
func (*Array) MarshalZerologArray(*Array) {
}
func (a *Array) write(dst []byte) []byte {
if len(a.buf) == 0 {
dst = append(dst, `[]`...)
} else {
a.buf[0] = '['
dst = append(append(dst, a.buf...), ']')
}
arrayPool.Put(a)
return dst
}
// Object marshals an object that implement the LogObjectMarshaler
// interface and append it to the array.
func (a *Array) Object(obj LogObjectMarshaler) *Array {
a.buf = append(a.buf, ',')
e := Dict()
obj.MarshalZerologObject(e)
e.buf = append(e.buf, '}')
a.buf = append(a.buf, e.buf...)
return a
}
// Str append the val as a string to the array.
func (a *Array) Str(val string) *Array {
a.buf = json.AppendString(append(a.buf, ','), val)
return a
}
// Bytes append the val as a string to the array.
func (a *Array) Bytes(val []byte) *Array {
a.buf = json.AppendBytes(append(a.buf, ','), val)
return a
}
// Err append the err as a string to the array.
func (a *Array) Err(err error) *Array {
a.buf = json.AppendError(append(a.buf, ','), err)
return a
}
// Bool append the val as a bool to the array.
func (a *Array) Bool(b bool) *Array {
a.buf = json.AppendBool(append(a.buf, ','), b)
return a
}
// Int append i as a int to the array.
func (a *Array) Int(i int) *Array {
a.buf = json.AppendInt(append(a.buf, ','), i)
return a
}
// Int8 append i as a int8 to the array.
func (a *Array) Int8(i int8) *Array {
a.buf = json.AppendInt8(append(a.buf, ','), i)
return a
}
// Int16 append i as a int16 to the array.
func (a *Array) Int16(i int16) *Array {
a.buf = json.AppendInt16(append(a.buf, ','), i)
return a
}
// Int32 append i as a int32 to the array.
func (a *Array) Int32(i int32) *Array {
a.buf = json.AppendInt32(append(a.buf, ','), i)
return a
}
// Int64 append i as a int64 to the array.
func (a *Array) Int64(i int64) *Array {
a.buf = json.AppendInt64(append(a.buf, ','), i)
return a
}
// Uint append i as a uint to the array.
func (a *Array) Uint(i uint) *Array {
a.buf = json.AppendUint(append(a.buf, ','), i)
return a
}
// Uint8 append i as a uint8 to the array.
func (a *Array) Uint8(i uint8) *Array {
a.buf = json.AppendUint8(append(a.buf, ','), i)
return a
}
// Uint16 append i as a uint16 to the array.
func (a *Array) Uint16(i uint16) *Array {
a.buf = json.AppendUint16(append(a.buf, ','), i)
return a
}
// Uint32 append i as a uint32 to the array.
func (a *Array) Uint32(i uint32) *Array {
a.buf = json.AppendUint32(append(a.buf, ','), i)
return a
}
// Uint64 append i as a uint64 to the array.
func (a *Array) Uint64(i uint64) *Array {
a.buf = json.AppendUint64(append(a.buf, ','), i)
return a
}
// Float32 append f as a float32 to the array.
func (a *Array) Float32(f float32) *Array {
a.buf = json.AppendFloat32(append(a.buf, ','), f)
return a
}
// Float64 append f as a float64 to the array.
func (a *Array) Float64(f float64) *Array {
a.buf = json.AppendFloat64(append(a.buf, ','), f)
return a
}
// Time append t formated as string using zerolog.TimeFieldFormat.
func (a *Array) Time(t time.Time) *Array {
a.buf = json.AppendTime(append(a.buf, ','), t, TimeFieldFormat)
return a
}
// Dur append d to the array.
func (a *Array) Dur(d time.Duration) *Array {
a.buf = json.AppendDuration(append(a.buf, ','), d, DurationFieldUnit, DurationFieldInteger)
return a
}
// Interface append i marshaled using reflection.
func (a *Array) Interface(i interface{}) *Array {
if obj, ok := i.(LogObjectMarshaler); ok {
return a.Object(obj)
}
a.buf = json.AppendInterface(append(a.buf, ','), i)
return a
}
+30
View File
@@ -0,0 +1,30 @@
package zerolog
import (
"testing"
"time"
)
func TestArray(t *testing.T) {
a := Arr().
Bool(true).
Int(1).
Int8(2).
Int16(3).
Int32(4).
Int64(5).
Uint(6).
Uint8(7).
Uint16(8).
Uint32(9).
Uint64(10).
Float32(11).
Float64(12).
Str("a").
Time(time.Time{}).
Dur(0)
want := `[true,1,2,3,4,5,6,7,8,9,10,11,12,"a","0001-01-01T00:00:00Z",0]`
if got := string(a.write([]byte{})); got != want {
t.Errorf("Array.write()\ngot: %s\nwant: %s", got, want)
}
}
+53 -7
View File
@@ -72,12 +72,19 @@ func BenchmarkLogFields(b *testing.B) {
})
}
type obj struct {
Pub string
Tag string `json:"tag"`
priv int
}
func (o obj) MarshalZerologObject(e *Event) {
e.Str("Pub", o.Pub).
Str("Tag", o.Tag).
Int("priv", o.priv)
}
func BenchmarkLogFieldType(b *testing.B) {
type obj struct {
Pub string
Tag string `json:"tag"`
priv int
}
bools := []bool{true, false, true, false, true, false, true, false, true, false}
ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
floats := []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
@@ -95,7 +102,34 @@ func BenchmarkLogFieldType(b *testing.B) {
time.Unix(8, 0),
time.Unix(9, 0),
}
o := obj{"a", "a", 0}
interfaces := []struct {
Pub string
Tag string `json:"tag"`
priv int
}{
{"a", "a", 0},
{"a", "a", 0},
{"a", "a", 0},
{"a", "a", 0},
{"a", "a", 0},
{"a", "a", 0},
{"a", "a", 0},
{"a", "a", 0},
{"a", "a", 0},
{"a", "a", 0},
}
objects := []obj{
obj{"a", "a", 0},
obj{"a", "a", 0},
obj{"a", "a", 0},
obj{"a", "a", 0},
obj{"a", "a", 0},
obj{"a", "a", 0},
obj{"a", "a", 0},
obj{"a", "a", 0},
obj{"a", "a", 0},
obj{"a", "a", 0},
}
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
types := map[string]func(e *Event) *Event{
"Bool": func(e *Event) *Event {
@@ -141,7 +175,19 @@ func BenchmarkLogFieldType(b *testing.B) {
return e.Durs("k", durations)
},
"Interface": func(e *Event) *Event {
return e.Interface("k", o)
return e.Interface("k", interfaces[0])
},
"Interfaces": func(e *Event) *Event {
return e.Interface("k", interfaces)
},
"Interface(Object)": func(e *Event) *Event {
return e.Interface("k", objects[0])
},
"Interface(Objects)": func(e *Event) *Event {
return e.Interface("k", objects)
},
"Object": func(e *Event) *Event {
return e.Object("k", objects[0])
},
}
logger := New(ioutil.Discard)
+117
View File
@@ -0,0 +1,117 @@
package zerolog
import (
"bytes"
"encoding/json"
"fmt"
"io"
"sort"
"strconv"
"strings"
"sync"
)
const (
cReset = 0
cBold = 1
cRed = 31
cGreen = 32
cYellow = 33
cBlue = 34
cMagenta = 35
cCyan = 36
cGray = 37
cDarkGray = 90
)
var consoleBufPool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(make([]byte, 0, 100))
},
}
// ConsoleWriter reads a JSON object per write operation and output an
// optionally colored human readable version on the Out writer.
type ConsoleWriter struct {
Out io.Writer
NoColor bool
}
func (w ConsoleWriter) Write(p []byte) (n int, err error) {
var event map[string]interface{}
err = json.Unmarshal(p, &event)
if err != nil {
return
}
buf := consoleBufPool.Get().(*bytes.Buffer)
defer consoleBufPool.Put(buf)
lvlColor := cReset
level := "????"
if l, ok := event[LevelFieldName].(string); ok {
if !w.NoColor {
lvlColor = levelColor(l)
}
level = strings.ToUpper(l)[0:4]
}
fmt.Fprintf(buf, "%s |%s| %s",
colorize(event[TimestampFieldName], cDarkGray, !w.NoColor),
colorize(level, lvlColor, !w.NoColor),
colorize(event[MessageFieldName], cReset, !w.NoColor))
fields := make([]string, 0, len(event))
for field := range event {
switch field {
case LevelFieldName, TimestampFieldName, MessageFieldName:
continue
}
fields = append(fields, field)
}
sort.Strings(fields)
for _, field := range fields {
fmt.Fprintf(buf, " %s=", colorize(field, cCyan, !w.NoColor))
switch value := event[field].(type) {
case string:
if needsQuote(value) {
buf.WriteString(strconv.Quote(value))
} else {
buf.WriteString(value)
}
default:
fmt.Fprint(buf, value)
}
}
buf.WriteByte('\n')
buf.WriteTo(w.Out)
n = len(p)
return
}
func colorize(s interface{}, color int, enabled bool) string {
if !enabled {
return fmt.Sprintf("%v", s)
}
return fmt.Sprintf("\x1b[%dm%v\x1b[0m", color, s)
}
func levelColor(level string) int {
switch level {
case "debug":
return cGray
case "info":
return cGreen
case "warn":
return cYellow
case "error", "fatal", "panic":
return cRed
default:
return cReset
}
}
func needsQuote(s string) bool {
for i := range s {
if s[i] < 0x20 || s[i] > 0x7e || s[i] == '\\' || s[i] == '"' {
return true
}
}
return false
}
+78 -39
View File
@@ -1,6 +1,11 @@
package zerolog
import "time"
import (
"io/ioutil"
"time"
"github.com/rs/zerolog/internal/json"
)
// Context configures a new sub-logger with contextual fields.
type Context struct {
@@ -21,201 +26,235 @@ func (c Context) Fields(fields map[string]interface{}) Context {
// Dict adds the field key with the dict to the logger context.
func (c Context) Dict(key string, dict *Event) Context {
dict.buf = append(dict.buf, '}')
c.l.context = append(appendKey(c.l.context, key), dict.buf...)
c.l.context = append(json.AppendKey(c.l.context, key), dict.buf...)
eventPool.Put(dict)
return c
}
// Array adds the field key with an array to the event context.
// Use zerolog.Arr() to create the array or pass a type that
// implement the LogArrayMarshaler interface.
func (c Context) Array(key string, arr LogArrayMarshaler) Context {
c.l.context = json.AppendKey(c.l.context, key)
if arr, ok := arr.(*Array); ok {
c.l.context = arr.write(c.l.context)
return c
}
var a *Array
if aa, ok := arr.(*Array); ok {
a = aa
} else {
a = Arr()
arr.MarshalZerologArray(a)
}
c.l.context = a.write(c.l.context)
return c
}
// Object marshals an object that implement the LogObjectMarshaler interface.
func (c Context) Object(key string, obj LogObjectMarshaler) Context {
e := newEvent(levelWriterAdapter{ioutil.Discard}, 0, true)
e.Object(key, obj)
e.buf[0] = ',' // A new event starts as an object, we want to embed it.
c.l.context = append(c.l.context, e.buf...)
eventPool.Put(e)
return c
}
// Str adds the field key with val as a string to the logger context.
func (c Context) Str(key, val string) Context {
c.l.context = appendString(c.l.context, key, val)
c.l.context = json.AppendString(json.AppendKey(c.l.context, key), val)
return c
}
// Strs adds the field key with val as a string to the logger context.
func (c Context) Strs(key string, vals []string) Context {
c.l.context = appendStrings(c.l.context, key, vals)
c.l.context = json.AppendStrings(json.AppendKey(c.l.context, key), vals)
return c
}
// Bytes adds the field key with val as a []byte to the logger context.
func (c Context) Bytes(key string, val []byte) Context {
c.l.context = appendBytes(c.l.context, key, val)
c.l.context = json.AppendBytes(json.AppendKey(c.l.context, key), val)
return c
}
// AnErr adds the field key with err as a string to the logger context.
func (c Context) AnErr(key string, err error) Context {
c.l.context = appendErrorKey(c.l.context, key, err)
if err != nil {
c.l.context = json.AppendError(json.AppendKey(c.l.context, key), err)
}
return c
}
// Errs adds the field key with errs as an array of strings to the logger context.
func (c Context) Errs(key string, errs []error) Context {
c.l.context = appendErrorsKey(c.l.context, key, errs)
c.l.context = json.AppendErrors(json.AppendKey(c.l.context, key), errs)
return c
}
// Err adds the field "error" with err as a string to the logger context.
// To customize the key name, change zerolog.ErrorFieldName.
func (c Context) Err(err error) Context {
c.l.context = appendError(c.l.context, err)
if err != nil {
c.l.context = json.AppendError(json.AppendKey(c.l.context, ErrorFieldName), err)
}
return c
}
// Bool adds the field key with val as a bool to the logger context.
func (c Context) Bool(key string, b bool) Context {
c.l.context = appendBool(c.l.context, key, b)
c.l.context = json.AppendBool(json.AppendKey(c.l.context, key), b)
return c
}
// Bools adds the field key with val as a []bool to the logger context.
func (c Context) Bools(key string, b []bool) Context {
c.l.context = appendBools(c.l.context, key, b)
c.l.context = json.AppendBools(json.AppendKey(c.l.context, key), b)
return c
}
// Int adds the field key with i as a int to the logger context.
func (c Context) Int(key string, i int) Context {
c.l.context = appendInt(c.l.context, key, i)
c.l.context = json.AppendInt(json.AppendKey(c.l.context, key), i)
return c
}
// Ints adds the field key with i as a []int to the logger context.
func (c Context) Ints(key string, i []int) Context {
c.l.context = appendInts(c.l.context, key, i)
c.l.context = json.AppendInts(json.AppendKey(c.l.context, key), i)
return c
}
// Int8 adds the field key with i as a int8 to the logger context.
func (c Context) Int8(key string, i int8) Context {
c.l.context = appendInt8(c.l.context, key, i)
c.l.context = json.AppendInt8(json.AppendKey(c.l.context, key), i)
return c
}
// Ints8 adds the field key with i as a []int8 to the logger context.
func (c Context) Ints8(key string, i []int8) Context {
c.l.context = appendInts8(c.l.context, key, i)
c.l.context = json.AppendInts8(json.AppendKey(c.l.context, key), i)
return c
}
// Int16 adds the field key with i as a int16 to the logger context.
func (c Context) Int16(key string, i int16) Context {
c.l.context = appendInt16(c.l.context, key, i)
c.l.context = json.AppendInt16(json.AppendKey(c.l.context, key), i)
return c
}
// Ints16 adds the field key with i as a []int16 to the logger context.
func (c Context) Ints16(key string, i []int16) Context {
c.l.context = appendInts16(c.l.context, key, i)
c.l.context = json.AppendInts16(json.AppendKey(c.l.context, key), i)
return c
}
// Int32 adds the field key with i as a int32 to the logger context.
func (c Context) Int32(key string, i int32) Context {
c.l.context = appendInt32(c.l.context, key, i)
c.l.context = json.AppendInt32(json.AppendKey(c.l.context, key), i)
return c
}
// Ints32 adds the field key with i as a []int32 to the logger context.
func (c Context) Ints32(key string, i []int32) Context {
c.l.context = appendInts32(c.l.context, key, i)
c.l.context = json.AppendInts32(json.AppendKey(c.l.context, key), i)
return c
}
// Int64 adds the field key with i as a int64 to the logger context.
func (c Context) Int64(key string, i int64) Context {
c.l.context = appendInt64(c.l.context, key, i)
c.l.context = json.AppendInt64(json.AppendKey(c.l.context, key), i)
return c
}
// Ints64 adds the field key with i as a []int64 to the logger context.
func (c Context) Ints64(key string, i []int64) Context {
c.l.context = appendInts64(c.l.context, key, i)
c.l.context = json.AppendInts64(json.AppendKey(c.l.context, key), i)
return c
}
// Uint adds the field key with i as a uint to the logger context.
func (c Context) Uint(key string, i uint) Context {
c.l.context = appendUint(c.l.context, key, i)
c.l.context = json.AppendUint(json.AppendKey(c.l.context, key), i)
return c
}
// Uints adds the field key with i as a []uint to the logger context.
func (c Context) Uints(key string, i []uint) Context {
c.l.context = appendUints(c.l.context, key, i)
c.l.context = json.AppendUints(json.AppendKey(c.l.context, key), i)
return c
}
// Uint8 adds the field key with i as a uint8 to the logger context.
func (c Context) Uint8(key string, i uint8) Context {
c.l.context = appendUint8(c.l.context, key, i)
c.l.context = json.AppendUint8(json.AppendKey(c.l.context, key), i)
return c
}
// Uints8 adds the field key with i as a []uint8 to the logger context.
func (c Context) Uints8(key string, i []uint8) Context {
c.l.context = appendUints8(c.l.context, key, i)
c.l.context = json.AppendUints8(json.AppendKey(c.l.context, key), i)
return c
}
// Uint16 adds the field key with i as a uint16 to the logger context.
func (c Context) Uint16(key string, i uint16) Context {
c.l.context = appendUint16(c.l.context, key, i)
c.l.context = json.AppendUint16(json.AppendKey(c.l.context, key), i)
return c
}
// Uints16 adds the field key with i as a []uint16 to the logger context.
func (c Context) Uints16(key string, i []uint16) Context {
c.l.context = appendUints16(c.l.context, key, i)
c.l.context = json.AppendUints16(json.AppendKey(c.l.context, key), i)
return c
}
// Uint32 adds the field key with i as a uint32 to the logger context.
func (c Context) Uint32(key string, i uint32) Context {
c.l.context = appendUint32(c.l.context, key, i)
c.l.context = json.AppendUint32(json.AppendKey(c.l.context, key), i)
return c
}
// Uints32 adds the field key with i as a []uint32 to the logger context.
func (c Context) Uints32(key string, i []uint32) Context {
c.l.context = appendUints32(c.l.context, key, i)
c.l.context = json.AppendUints32(json.AppendKey(c.l.context, key), i)
return c
}
// Uint64 adds the field key with i as a uint64 to the logger context.
func (c Context) Uint64(key string, i uint64) Context {
c.l.context = appendUint64(c.l.context, key, i)
c.l.context = json.AppendUint64(json.AppendKey(c.l.context, key), i)
return c
}
// Uints64 adds the field key with i as a []uint64 to the logger context.
func (c Context) Uints64(key string, i []uint64) Context {
c.l.context = appendUints64(c.l.context, key, i)
c.l.context = json.AppendUints64(json.AppendKey(c.l.context, key), i)
return c
}
// Float32 adds the field key with f as a float32 to the logger context.
func (c Context) Float32(key string, f float32) Context {
c.l.context = appendFloat32(c.l.context, key, f)
c.l.context = json.AppendFloat32(json.AppendKey(c.l.context, key), f)
return c
}
// Floats32 adds the field key with f as a []float32 to the logger context.
func (c Context) Floats32(key string, f []float32) Context {
c.l.context = appendFloats32(c.l.context, key, f)
c.l.context = json.AppendFloats32(json.AppendKey(c.l.context, key), f)
return c
}
// Float64 adds the field key with f as a float64 to the logger context.
func (c Context) Float64(key string, f float64) Context {
c.l.context = appendFloat64(c.l.context, key, f)
c.l.context = json.AppendFloat64(json.AppendKey(c.l.context, key), f)
return c
}
// Floats64 adds the field key with f as a []float64 to the logger context.
func (c Context) Floats64(key string, f []float64) Context {
c.l.context = appendFloats64(c.l.context, key, f)
c.l.context = json.AppendFloats64(json.AppendKey(c.l.context, key), f)
return c
}
@@ -232,30 +271,30 @@ func (c Context) Timestamp() Context {
// Time adds the field key with t formated as string using zerolog.TimeFieldFormat.
func (c Context) Time(key string, t time.Time) Context {
c.l.context = appendTime(c.l.context, key, t)
c.l.context = json.AppendTime(json.AppendKey(c.l.context, key), t, TimeFieldFormat)
return c
}
// Times adds the field key with t formated as string using zerolog.TimeFieldFormat.
func (c Context) Times(key string, t []time.Time) Context {
c.l.context = appendTimes(c.l.context, key, t)
c.l.context = json.AppendTimes(json.AppendKey(c.l.context, key), t, TimeFieldFormat)
return c
}
// Dur adds the fields key with d divided by unit and stored as a float.
func (c Context) Dur(key string, d time.Duration) Context {
c.l.context = appendDuration(c.l.context, key, d)
c.l.context = json.AppendDuration(json.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger)
return c
}
// Durs adds the fields key with d divided by unit and stored as a float.
func (c Context) Durs(key string, d []time.Duration) Context {
c.l.context = appendDurations(c.l.context, key, d)
c.l.context = json.AppendDurations(json.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger)
return c
}
// Interface adds the field key with obj marshaled using reflection.
func (c Context) Interface(key string, i interface{}) Context {
c.l.context = appendInterface(c.l.context, key, i)
c.l.context = json.AppendInterface(json.AppendKey(c.l.context, key), i)
return c
}
+110 -43
View File
@@ -6,6 +6,8 @@ import (
"os"
"sync"
"time"
"github.com/rs/zerolog/internal/json"
)
var eventPool = &sync.Pool{
@@ -26,6 +28,18 @@ type Event struct {
done func(msg string)
}
// LogObjectMarshaler provides a strongly-typed and encoding-agnostic interface
// to be implemented by types used with Event/Context's Object methods.
type LogObjectMarshaler interface {
MarshalZerologObject(e *Event)
}
// LogArrayMarshaler provides a strongly-typed and encoding-agnostic interface
// to be implemented by types used with Event/Context's Array methods.
type LogArrayMarshaler interface {
MarshalZerologArray(a *Array)
}
func newEvent(w LevelWriter, level Level, enabled bool) *Event {
if !enabled {
return &Event{}
@@ -64,7 +78,7 @@ func (e *Event) Msg(msg string) {
return
}
if msg != "" {
e.buf = appendString(e.buf, MessageFieldName, msg)
e.buf = json.AppendString(json.AppendKey(e.buf, MessageFieldName), msg)
}
if e.done != nil {
defer e.done(msg)
@@ -84,7 +98,7 @@ func (e *Event) Msgf(format string, v ...interface{}) {
}
msg := fmt.Sprintf(format, v...)
if msg != "" {
e.buf = appendString(e.buf, MessageFieldName, msg)
e.buf = json.AppendString(json.AppendKey(e.buf, MessageFieldName), msg)
}
if e.done != nil {
defer e.done(msg)
@@ -109,7 +123,7 @@ func (e *Event) Dict(key string, dict *Event) *Event {
if !e.enabled {
return e
}
e.buf = append(append(appendKey(e.buf, key), dict.buf...), '}')
e.buf = append(append(json.AppendKey(e.buf, key), dict.buf...), '}')
eventPool.Put(dict)
return e
}
@@ -121,12 +135,55 @@ func Dict() *Event {
return newEvent(levelWriterAdapter{ioutil.Discard}, 0, true)
}
// Array adds the field key with an array to the event context.
// Use zerolog.Arr() to create the array or pass a type that
// implement the LogArrayMarshaler interface.
func (e *Event) Array(key string, arr LogArrayMarshaler) *Event {
if !e.enabled {
return e
}
e.buf = json.AppendKey(e.buf, key)
var a *Array
if aa, ok := arr.(*Array); ok {
a = aa
} else {
a = Arr()
arr.MarshalZerologArray(a)
}
e.buf = a.write(e.buf)
return e
}
func (e *Event) appendObject(obj LogObjectMarshaler) {
pos := len(e.buf)
obj.MarshalZerologObject(e)
if pos < len(e.buf) {
// As MarshalZerologObject will use event API, the first field will be
// preceded by a comma. If at least one field has been added (buf grew),
// we replace this coma by the opening bracket.
e.buf[pos] = '{'
} else {
e.buf = append(e.buf, '{')
}
e.buf = append(e.buf, '}')
}
// Object marshals an object that implement the LogObjectMarshaler interface.
func (e *Event) Object(key string, obj LogObjectMarshaler) *Event {
if !e.enabled {
return e
}
e.buf = json.AppendKey(e.buf, key)
e.appendObject(obj)
return e
}
// Str adds the field key with val as a string to the *Event context.
func (e *Event) Str(key, val string) *Event {
if !e.enabled {
return e
}
e.buf = appendString(e.buf, key, val)
e.buf = json.AppendString(json.AppendKey(e.buf, key), val)
return e
}
@@ -135,16 +192,19 @@ func (e *Event) Strs(key string, vals []string) *Event {
if !e.enabled {
return e
}
e.buf = appendStrings(e.buf, key, vals)
e.buf = json.AppendStrings(json.AppendKey(e.buf, key), vals)
return e
}
// Bytes adds the field key with val as a []byte to the *Event context.
// Bytes adds the field key with val as a string to the *Event context.
//
// Runes outside of normal ASCII ranges will be hex-encoded in the resulting
// JSON.
func (e *Event) Bytes(key string, val []byte) *Event {
if !e.enabled {
return e
}
e.buf = appendBytes(e.buf, key, val)
e.buf = json.AppendBytes(json.AppendKey(e.buf, key), val)
return e
}
@@ -154,7 +214,9 @@ func (e *Event) AnErr(key string, err error) *Event {
if !e.enabled {
return e
}
e.buf = appendErrorKey(e.buf, key, err)
if err != nil {
e.buf = json.AppendError(json.AppendKey(e.buf, key), err)
}
return e
}
@@ -164,7 +226,7 @@ func (e *Event) Errs(key string, errs []error) *Event {
if !e.enabled {
return e
}
e.buf = appendErrorsKey(e.buf, key, errs)
e.buf = json.AppendErrors(json.AppendKey(e.buf, key), errs)
return e
}
@@ -175,7 +237,9 @@ func (e *Event) Err(err error) *Event {
if !e.enabled {
return e
}
e.buf = appendError(e.buf, err)
if err != nil {
e.buf = json.AppendError(json.AppendKey(e.buf, ErrorFieldName), err)
}
return e
}
@@ -184,7 +248,7 @@ func (e *Event) Bool(key string, b bool) *Event {
if !e.enabled {
return e
}
e.buf = appendBool(e.buf, key, b)
e.buf = json.AppendBool(json.AppendKey(e.buf, key), b)
return e
}
@@ -193,7 +257,7 @@ func (e *Event) Bools(key string, b []bool) *Event {
if !e.enabled {
return e
}
e.buf = appendBools(e.buf, key, b)
e.buf = json.AppendBools(json.AppendKey(e.buf, key), b)
return e
}
@@ -202,7 +266,7 @@ func (e *Event) Int(key string, i int) *Event {
if !e.enabled {
return e
}
e.buf = appendInt(e.buf, key, i)
e.buf = json.AppendInt(json.AppendKey(e.buf, key), i)
return e
}
@@ -211,7 +275,7 @@ func (e *Event) Ints(key string, i []int) *Event {
if !e.enabled {
return e
}
e.buf = appendInts(e.buf, key, i)
e.buf = json.AppendInts(json.AppendKey(e.buf, key), i)
return e
}
@@ -220,7 +284,7 @@ func (e *Event) Int8(key string, i int8) *Event {
if !e.enabled {
return e
}
e.buf = appendInt8(e.buf, key, i)
e.buf = json.AppendInt8(json.AppendKey(e.buf, key), i)
return e
}
@@ -229,7 +293,7 @@ func (e *Event) Ints8(key string, i []int8) *Event {
if !e.enabled {
return e
}
e.buf = appendInts8(e.buf, key, i)
e.buf = json.AppendInts8(json.AppendKey(e.buf, key), i)
return e
}
@@ -238,7 +302,7 @@ func (e *Event) Int16(key string, i int16) *Event {
if !e.enabled {
return e
}
e.buf = appendInt16(e.buf, key, i)
e.buf = json.AppendInt16(json.AppendKey(e.buf, key), i)
return e
}
@@ -247,7 +311,7 @@ func (e *Event) Ints16(key string, i []int16) *Event {
if !e.enabled {
return e
}
e.buf = appendInts16(e.buf, key, i)
e.buf = json.AppendInts16(json.AppendKey(e.buf, key), i)
return e
}
@@ -256,7 +320,7 @@ func (e *Event) Int32(key string, i int32) *Event {
if !e.enabled {
return e
}
e.buf = appendInt32(e.buf, key, i)
e.buf = json.AppendInt32(json.AppendKey(e.buf, key), i)
return e
}
@@ -265,7 +329,7 @@ func (e *Event) Ints32(key string, i []int32) *Event {
if !e.enabled {
return e
}
e.buf = appendInts32(e.buf, key, i)
e.buf = json.AppendInts32(json.AppendKey(e.buf, key), i)
return e
}
@@ -274,7 +338,7 @@ func (e *Event) Int64(key string, i int64) *Event {
if !e.enabled {
return e
}
e.buf = appendInt64(e.buf, key, i)
e.buf = json.AppendInt64(json.AppendKey(e.buf, key), i)
return e
}
@@ -283,7 +347,7 @@ func (e *Event) Ints64(key string, i []int64) *Event {
if !e.enabled {
return e
}
e.buf = appendInts64(e.buf, key, i)
e.buf = json.AppendInts64(json.AppendKey(e.buf, key), i)
return e
}
@@ -292,7 +356,7 @@ func (e *Event) Uint(key string, i uint) *Event {
if !e.enabled {
return e
}
e.buf = appendUint(e.buf, key, i)
e.buf = json.AppendUint(json.AppendKey(e.buf, key), i)
return e
}
@@ -301,7 +365,7 @@ func (e *Event) Uints(key string, i []uint) *Event {
if !e.enabled {
return e
}
e.buf = appendUints(e.buf, key, i)
e.buf = json.AppendUints(json.AppendKey(e.buf, key), i)
return e
}
@@ -310,7 +374,7 @@ func (e *Event) Uint8(key string, i uint8) *Event {
if !e.enabled {
return e
}
e.buf = appendUint8(e.buf, key, i)
e.buf = json.AppendUint8(json.AppendKey(e.buf, key), i)
return e
}
@@ -319,7 +383,7 @@ func (e *Event) Uints8(key string, i []uint8) *Event {
if !e.enabled {
return e
}
e.buf = appendUints8(e.buf, key, i)
e.buf = json.AppendUints8(json.AppendKey(e.buf, key), i)
return e
}
@@ -328,7 +392,7 @@ func (e *Event) Uint16(key string, i uint16) *Event {
if !e.enabled {
return e
}
e.buf = appendUint16(e.buf, key, i)
e.buf = json.AppendUint16(json.AppendKey(e.buf, key), i)
return e
}
@@ -337,7 +401,7 @@ func (e *Event) Uints16(key string, i []uint16) *Event {
if !e.enabled {
return e
}
e.buf = appendUints16(e.buf, key, i)
e.buf = json.AppendUints16(json.AppendKey(e.buf, key), i)
return e
}
@@ -346,7 +410,7 @@ func (e *Event) Uint32(key string, i uint32) *Event {
if !e.enabled {
return e
}
e.buf = appendUint32(e.buf, key, i)
e.buf = json.AppendUint32(json.AppendKey(e.buf, key), i)
return e
}
@@ -355,7 +419,7 @@ func (e *Event) Uints32(key string, i []uint32) *Event {
if !e.enabled {
return e
}
e.buf = appendUints32(e.buf, key, i)
e.buf = json.AppendUints32(json.AppendKey(e.buf, key), i)
return e
}
@@ -364,7 +428,7 @@ func (e *Event) Uint64(key string, i uint64) *Event {
if !e.enabled {
return e
}
e.buf = appendUint64(e.buf, key, i)
e.buf = json.AppendUint64(json.AppendKey(e.buf, key), i)
return e
}
@@ -373,7 +437,7 @@ func (e *Event) Uints64(key string, i []uint64) *Event {
if !e.enabled {
return e
}
e.buf = appendUints64(e.buf, key, i)
e.buf = json.AppendUints64(json.AppendKey(e.buf, key), i)
return e
}
@@ -382,7 +446,7 @@ func (e *Event) Float32(key string, f float32) *Event {
if !e.enabled {
return e
}
e.buf = appendFloat32(e.buf, key, f)
e.buf = json.AppendFloat32(json.AppendKey(e.buf, key), f)
return e
}
@@ -391,7 +455,7 @@ func (e *Event) Floats32(key string, f []float32) *Event {
if !e.enabled {
return e
}
e.buf = appendFloats32(e.buf, key, f)
e.buf = json.AppendFloats32(json.AppendKey(e.buf, key), f)
return e
}
@@ -400,7 +464,7 @@ func (e *Event) Float64(key string, f float64) *Event {
if !e.enabled {
return e
}
e.buf = appendFloat64(e.buf, key, f)
e.buf = json.AppendFloat64(json.AppendKey(e.buf, key), f)
return e
}
@@ -409,7 +473,7 @@ func (e *Event) Floats64(key string, f []float64) *Event {
if !e.enabled {
return e
}
e.buf = appendFloats64(e.buf, key, f)
e.buf = json.AppendFloats64(json.AppendKey(e.buf, key), f)
return e
}
@@ -419,7 +483,7 @@ func (e *Event) Timestamp() *Event {
if !e.enabled {
return e
}
e.buf = appendTimestamp(e.buf)
e.buf = json.AppendTime(json.AppendKey(e.buf, TimestampFieldName), TimestampFunc(), TimeFieldFormat)
return e
}
@@ -428,7 +492,7 @@ func (e *Event) Time(key string, t time.Time) *Event {
if !e.enabled {
return e
}
e.buf = appendTime(e.buf, key, t)
e.buf = json.AppendTime(json.AppendKey(e.buf, key), t, TimeFieldFormat)
return e
}
@@ -437,7 +501,7 @@ func (e *Event) Times(key string, t []time.Time) *Event {
if !e.enabled {
return e
}
e.buf = appendTimes(e.buf, key, t)
e.buf = json.AppendTimes(json.AppendKey(e.buf, key), t, TimeFieldFormat)
return e
}
@@ -448,7 +512,7 @@ func (e *Event) Dur(key string, d time.Duration) *Event {
if !e.enabled {
return e
}
e.buf = appendDuration(e.buf, key, d)
e.buf = json.AppendDuration(json.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger)
return e
}
@@ -459,7 +523,7 @@ func (e *Event) Durs(key string, d []time.Duration) *Event {
if !e.enabled {
return e
}
e.buf = appendDurations(e.buf, key, d)
e.buf = json.AppendDurations(json.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger)
return e
}
@@ -474,7 +538,7 @@ func (e *Event) TimeDiff(key string, t time.Time, start time.Time) *Event {
if t.After(start) {
d = t.Sub(start)
}
e.buf = appendDuration(e.buf, key, d)
e.buf = json.AppendDuration(json.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger)
return e
}
@@ -483,6 +547,9 @@ func (e *Event) Interface(key string, i interface{}) *Event {
if !e.enabled {
return e
}
e.buf = appendInterface(e.buf, key, i)
if obj, ok := i.(LogObjectMarshaler); ok {
return e.Object(key, obj)
}
e.buf = json.AppendInterface(json.AppendKey(e.buf, key), i)
return e
}
-519
View File
@@ -1,519 +0,0 @@
package zerolog
import (
"encoding/json"
"fmt"
"math"
"sort"
"strconv"
"time"
)
func appendFields(dst []byte, fields map[string]interface{}) []byte {
keys := make([]string, 0, len(fields))
for key, _ := range fields {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
switch val := fields[key].(type) {
case string:
dst = appendString(dst, key, val)
case []byte:
dst = appendBytes(dst, key, val)
case error:
dst = appendErrorKey(dst, key, val)
case []error:
dst = appendErrorsKey(dst, key, val)
case bool:
dst = appendBool(dst, key, val)
case int:
dst = appendInt(dst, key, val)
case int8:
dst = appendInt8(dst, key, val)
case int16:
dst = appendInt16(dst, key, val)
case int32:
dst = appendInt32(dst, key, val)
case int64:
dst = appendInt64(dst, key, val)
case uint:
dst = appendUint(dst, key, val)
case uint8:
dst = appendUint8(dst, key, val)
case uint16:
dst = appendUint16(dst, key, val)
case uint32:
dst = appendUint32(dst, key, val)
case uint64:
dst = appendUint64(dst, key, val)
case float32:
dst = appendFloat32(dst, key, val)
case float64:
dst = appendFloat64(dst, key, val)
case time.Time:
dst = appendTime(dst, key, val)
case time.Duration:
dst = appendDuration(dst, key, val)
case []string:
dst = appendStrings(dst, key, val)
case []bool:
dst = appendBools(dst, key, val)
case []int:
dst = appendInts(dst, key, val)
case []int8:
dst = appendInts8(dst, key, val)
case []int16:
dst = appendInts16(dst, key, val)
case []int32:
dst = appendInts32(dst, key, val)
case []int64:
dst = appendInts64(dst, key, val)
case []uint:
dst = appendUints(dst, key, val)
// case []uint8:
// dst = appendUints8(dst, key, val)
case []uint16:
dst = appendUints16(dst, key, val)
case []uint32:
dst = appendUints32(dst, key, val)
case []uint64:
dst = appendUints64(dst, key, val)
case []float32:
dst = appendFloats32(dst, key, val)
case []float64:
dst = appendFloats64(dst, key, val)
case []time.Time:
dst = appendTimes(dst, key, val)
case []time.Duration:
dst = appendDurations(dst, key, val)
case nil:
dst = append(appendKey(dst, key), "null"...)
default:
dst = appendInterface(dst, key, val)
}
}
return dst
}
func appendKey(dst []byte, key string) []byte {
if len(dst) > 1 {
dst = append(dst, ',')
}
dst = appendJSONString(dst, key)
return append(dst, ':')
}
func appendString(dst []byte, key, val string) []byte {
return appendJSONString(appendKey(dst, key), val)
}
func appendStrings(dst []byte, key string, vals []string) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = appendJSONString(dst, vals[0])
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = appendJSONString(append(dst, ','), val)
}
}
dst = append(dst, ']')
return dst
}
func appendBytes(dst []byte, key string, val []byte) []byte {
return appendJSONBytes(appendKey(dst, key), val)
}
func appendErrorKey(dst []byte, key string, err error) []byte {
if err == nil {
return dst
}
return appendJSONString(appendKey(dst, key), err.Error())
}
func appendErrorsKey(dst []byte, key string, errs []error) []byte {
if len(errs) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
if errs[0] != nil {
dst = appendJSONString(dst, errs[0].Error())
} else {
dst = append(dst, "null"...)
}
if len(errs) > 1 {
for _, err := range errs[1:] {
if err == nil {
dst = append(dst, ",null"...)
continue
}
dst = appendJSONString(append(dst, ','), err.Error())
}
}
dst = append(dst, ']')
return dst
}
func appendError(dst []byte, err error) []byte {
if err == nil {
return dst
}
return appendErrorKey(dst, ErrorFieldName, err)
}
func appendBool(dst []byte, key string, val bool) []byte {
return strconv.AppendBool(appendKey(dst, key), val)
}
func appendBools(dst []byte, key string, vals []bool) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendBool(dst, vals[0])
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendBool(append(dst, ','), val)
}
}
dst = append(dst, ']')
return dst
}
func appendInt(dst []byte, key string, val int) []byte {
return strconv.AppendInt(appendKey(dst, key), int64(val), 10)
}
func appendInts(dst []byte, key string, vals []int) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendInt(dst, int64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendInt8(dst []byte, key string, val int8) []byte {
return strconv.AppendInt(appendKey(dst, key), int64(val), 10)
}
func appendInts8(dst []byte, key string, vals []int8) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendInt(dst, int64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendInt16(dst []byte, key string, val int16) []byte {
return strconv.AppendInt(appendKey(dst, key), int64(val), 10)
}
func appendInts16(dst []byte, key string, vals []int16) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendInt(dst, int64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendInt32(dst []byte, key string, val int32) []byte {
return strconv.AppendInt(appendKey(dst, key), int64(val), 10)
}
func appendInts32(dst []byte, key string, vals []int32) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendInt(dst, int64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendInt64(dst []byte, key string, val int64) []byte {
return strconv.AppendInt(appendKey(dst, key), val, 10)
}
func appendInts64(dst []byte, key string, vals []int64) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendInt(dst, vals[0], 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), val, 10)
}
}
dst = append(dst, ']')
return dst
}
func appendUint(dst []byte, key string, val uint) []byte {
return strconv.AppendUint(appendKey(dst, key), uint64(val), 10)
}
func appendUints(dst []byte, key string, vals []uint) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendUint8(dst []byte, key string, val uint8) []byte {
return strconv.AppendUint(appendKey(dst, key), uint64(val), 10)
}
func appendUints8(dst []byte, key string, vals []uint8) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendUint16(dst []byte, key string, val uint16) []byte {
return strconv.AppendUint(appendKey(dst, key), uint64(val), 10)
}
func appendUints16(dst []byte, key string, vals []uint16) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendUint32(dst []byte, key string, val uint32) []byte {
return strconv.AppendUint(appendKey(dst, key), uint64(val), 10)
}
func appendUints32(dst []byte, key string, vals []uint32) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendUint64(dst []byte, key string, val uint64) []byte {
return strconv.AppendUint(appendKey(dst, key), uint64(val), 10)
}
func appendUints64(dst []byte, key string, vals []uint64) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendUint(dst, vals[0], 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), val, 10)
}
}
dst = append(dst, ']')
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 appendFloat(appendKey(dst, key), float64(val), 32)
}
func appendFloats32(dst []byte, key string, vals []float32) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = appendFloat(dst, float64(vals[0]), 32)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = appendFloat(append(dst, ','), float64(val), 32)
}
}
dst = append(dst, ']')
return dst
}
func appendFloat64(dst []byte, key string, val float64) []byte {
return appendFloat(appendKey(dst, key), val, 64)
}
func appendFloats64(dst []byte, key string, vals []float64) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = appendFloat(dst, vals[0], 32)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = appendFloat(append(dst, ','), val, 64)
}
}
dst = append(dst, ']')
return dst
}
func appendTime(dst []byte, key string, t time.Time) []byte {
if TimeFieldFormat == "" {
return appendInt64(dst, key, t.Unix())
}
return append(t.AppendFormat(append(appendKey(dst, key), '"'), TimeFieldFormat), '"')
}
func appendTimes(dst []byte, key string, vals []time.Time) []byte {
if TimeFieldFormat == "" {
return appendUnixTimes(dst, key, vals)
}
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = append(vals[0].AppendFormat(append(dst, '"'), TimeFieldFormat), '"')
if len(vals) > 1 {
for _, t := range vals[1:] {
dst = append(t.AppendFormat(append(dst, ',', '"'), TimeFieldFormat), '"')
}
}
dst = append(dst, ']')
return dst
}
func appendUnixTimes(dst []byte, key string, vals []time.Time) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendInt(dst, vals[0].Unix(), 10)
if len(vals) > 1 {
for _, t := range vals[1:] {
dst = strconv.AppendInt(dst, t.Unix(), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendTimestamp(dst []byte) []byte {
return appendTime(dst, TimestampFieldName, TimestampFunc())
}
func appendDuration(dst []byte, key string, d time.Duration) []byte {
if DurationFieldInteger {
return appendInt64(dst, key, int64(d/DurationFieldUnit))
}
return appendFloat64(dst, key, float64(d)/float64(DurationFieldUnit))
}
func appendDurations(dst []byte, key string, vals []time.Duration) []byte {
if DurationFieldInteger {
return appendIntDurations(dst, key, vals)
}
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendFloat(dst, float64(vals[0])/float64(DurationFieldUnit), 'f', -1, 32)
if len(vals) > 1 {
for _, d := range vals[1:] {
dst = strconv.AppendFloat(append(dst, ','), float64(d)/float64(DurationFieldUnit), 'f', -1, 32)
}
}
dst = append(dst, ']')
return dst
}
func appendIntDurations(dst []byte, key string, vals []time.Duration) []byte {
if len(vals) == 0 {
return append(appendKey(dst, key), '[', ']')
}
dst = append(appendKey(dst, key), '[')
dst = strconv.AppendInt(dst, int64(vals[0]/DurationFieldUnit), 10)
if len(vals) > 1 {
for _, d := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(d/DurationFieldUnit), 10)
}
}
dst = append(dst, ']')
return dst
}
func appendInterface(dst []byte, key string, i interface{}) []byte {
marshaled, err := json.Marshal(i)
if err != nil {
return appendString(dst, key, fmt.Sprintf("marshaling error: %v", err))
}
return append(appendKey(dst, key), marshaled...)
}
-33
View File
@@ -1,33 +0,0 @@
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)
}
})
}
}
+96
View File
@@ -0,0 +1,96 @@
package zerolog
import (
"sort"
"time"
"github.com/rs/zerolog/internal/json"
)
func appendFields(dst []byte, fields map[string]interface{}) []byte {
keys := make([]string, 0, len(fields))
for key := range fields {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
dst = json.AppendKey(dst, key)
switch val := fields[key].(type) {
case string:
dst = json.AppendString(dst, val)
case []byte:
dst = json.AppendBytes(dst, val)
case error:
dst = json.AppendError(dst, val)
case []error:
dst = json.AppendErrors(dst, val)
case bool:
dst = json.AppendBool(dst, val)
case int:
dst = json.AppendInt(dst, val)
case int8:
dst = json.AppendInt8(dst, val)
case int16:
dst = json.AppendInt16(dst, val)
case int32:
dst = json.AppendInt32(dst, val)
case int64:
dst = json.AppendInt64(dst, val)
case uint:
dst = json.AppendUint(dst, val)
case uint8:
dst = json.AppendUint8(dst, val)
case uint16:
dst = json.AppendUint16(dst, val)
case uint32:
dst = json.AppendUint32(dst, val)
case uint64:
dst = json.AppendUint64(dst, val)
case float32:
dst = json.AppendFloat32(dst, val)
case float64:
dst = json.AppendFloat64(dst, val)
case time.Time:
dst = json.AppendTime(dst, val, TimeFieldFormat)
case time.Duration:
dst = json.AppendDuration(dst, val, DurationFieldUnit, DurationFieldInteger)
case []string:
dst = json.AppendStrings(dst, val)
case []bool:
dst = json.AppendBools(dst, val)
case []int:
dst = json.AppendInts(dst, val)
case []int8:
dst = json.AppendInts8(dst, val)
case []int16:
dst = json.AppendInts16(dst, val)
case []int32:
dst = json.AppendInts32(dst, val)
case []int64:
dst = json.AppendInts64(dst, val)
case []uint:
dst = json.AppendUints(dst, val)
// case []uint8:
// dst = appendUints8(dst, val)
case []uint16:
dst = json.AppendUints16(dst, val)
case []uint32:
dst = json.AppendUints32(dst, val)
case []uint64:
dst = json.AppendUints64(dst, val)
case []float32:
dst = json.AppendFloats32(dst, val)
case []float64:
dst = json.AppendFloats64(dst, val)
case []time.Time:
dst = json.AppendTimes(dst, val, TimeFieldFormat)
case []time.Duration:
dst = json.AppendDurations(dst, val, DurationFieldUnit, DurationFieldInteger)
case nil:
dst = append(dst, "null"...)
default:
dst = json.AppendInterface(dst, val)
}
}
return dst
}
+39
View File
@@ -0,0 +1,39 @@
package json
func AppendKey(dst []byte, key string) []byte {
if len(dst) > 1 {
dst = append(dst, ',')
}
dst = AppendString(dst, key)
return append(dst, ':')
}
func AppendError(dst []byte, err error) []byte {
if err == nil {
return append(dst, `null`...)
}
return AppendString(dst, err.Error())
}
func AppendErrors(dst []byte, errs []error) []byte {
if len(errs) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
if errs[0] != nil {
dst = AppendString(dst, errs[0].Error())
} else {
dst = append(dst, "null"...)
}
if len(errs) > 1 {
for _, err := range errs[1:] {
if err == nil {
dst = append(dst, ",null"...)
continue
}
dst = AppendString(append(dst, ','), err.Error())
}
}
dst = append(dst, ']')
return dst
}
+26 -11
View File
@@ -1,10 +1,25 @@
package zerolog
package json
import "unicode/utf8"
const hex = "0123456789abcdef"
// appendJSONString encodes the input string to json and appends
func AppendStrings(dst []byte, vals []string) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = AppendString(dst, vals[0])
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = AppendString(append(dst, ','), val)
}
}
dst = append(dst, ']')
return dst
}
// AppendString encodes the input string to json and appends
// the encoded string to the input byte slice.
//
// The operation loops though each byte in the string looking
@@ -13,7 +28,7 @@ const hex = "0123456789abcdef"
// entirety to the byte slice.
// If we encounter a byte that does need encoding, switch up
// the operation and perform a byte-by-byte read-encode-append.
func appendJSONString(dst []byte, s string) []byte {
func AppendString(dst []byte, s string) []byte {
// Start with a double quote.
dst = append(dst, '"')
// Loop through each character in the string.
@@ -24,7 +39,7 @@ func appendJSONString(dst []byte, s string) []byte {
if s[i] < 0x20 || s[i] > 0x7e || s[i] == '\\' || s[i] == '"' {
// We encountered a character that needs to be encoded. Switch
// to complex version of the algorithm.
dst = appendJSONStringComplex(dst, s, i)
dst = appendStringComplex(dst, s, i)
return append(dst, '"')
}
}
@@ -35,10 +50,10 @@ func appendJSONString(dst []byte, s string) []byte {
return append(dst, '"')
}
// appendJSONStringComplex is used by appendJSONString to take over an in
// appendStringComplex is used by appendString to take over an in
// progress JSON string encoding that encountered a character that needs
// to be encoded.
func appendJSONStringComplex(dst []byte, s string, i int) []byte {
func appendStringComplex(dst []byte, s string, i int) []byte {
start := 0
for i < len(s) {
b := s[i]
@@ -95,12 +110,12 @@ func appendJSONStringComplex(dst []byte, s string, i int) []byte {
return dst
}
// appendJSONBytes is a mirror of appendJSONString with []byte arg
func appendJSONBytes(dst, s []byte) []byte {
// AppendBytes is a mirror of appendString with []byte arg
func AppendBytes(dst, s []byte) []byte {
dst = append(dst, '"')
for i := 0; i < len(s); i++ {
if s[i] < 0x20 || s[i] > 0x7e || s[i] == '\\' || s[i] == '"' {
dst = appendJSONBytesComplex(dst, s, i)
dst = appendBytesComplex(dst, s, i)
return append(dst, '"')
}
}
@@ -108,9 +123,9 @@ func appendJSONBytes(dst, s []byte) []byte {
return append(dst, '"')
}
// appendJSONBytesComplex is a mirror of the appendJSONStringComplex
// appendBytesComplex is a mirror of the appendStringComplex
// with []byte arg
func appendJSONBytesComplex(dst, s []byte, i int) []byte {
func appendBytesComplex(dst, s []byte, i int) []byte {
start := 0
for i < len(s) {
b := s[i]
+13 -13
View File
@@ -1,4 +1,4 @@
package zerolog
package json
import (
"testing"
@@ -53,20 +53,20 @@ var encodeStringTests = []struct {
{"emoji \u2764\ufe0f!", `"emoji ❤️!"`},
}
func TestAppendJSONString(t *testing.T) {
func TestappendString(t *testing.T) {
for _, tt := range encodeStringTests {
b := appendJSONString([]byte{}, tt.in)
b := AppendString([]byte{}, tt.in)
if got, want := string(b), tt.out; got != want {
t.Errorf("appendJSONString(%q) = %#q, want %#q", tt.in, got, want)
t.Errorf("appendString(%q) = %#q, want %#q", tt.in, got, want)
}
}
}
func TestAppendJSONBytes(t *testing.T) {
func TestappendBytes(t *testing.T) {
for _, tt := range encodeStringTests {
b := appendJSONBytes([]byte{}, []byte(tt.in))
b := AppendBytes([]byte{}, []byte(tt.in))
if got, want := string(b), tt.out; got != want {
t.Errorf("appendJSONBytes(%q) = %#q, want %#q", tt.in, got, want)
t.Errorf("appendBytes(%q) = %#q, want %#q", tt.in, got, want)
}
}
}
@@ -80,8 +80,8 @@ func TestStringBytes(t *testing.T) {
}
s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too
enc := string(appendJSONString([]byte{}, s))
encBytes := string(appendJSONBytes([]byte{}, []byte(s)))
enc := string(AppendString([]byte{}, s))
encBytes := string(AppendBytes([]byte{}, []byte(s)))
if enc != encBytes {
i := 0
@@ -108,7 +108,7 @@ func TestStringBytes(t *testing.T) {
}
}
func BenchmarkAppendJSONString(b *testing.B) {
func BenchmarkappendString(b *testing.B) {
tests := map[string]string{
"NoEncoding": `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
"EncodingFirst": `"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
@@ -122,13 +122,13 @@ func BenchmarkAppendJSONString(b *testing.B) {
b.Run(name, func(b *testing.B) {
buf := make([]byte, 0, 100)
for i := 0; i < b.N; i++ {
_ = appendJSONString(buf, str)
_ = AppendString(buf, str)
}
})
}
}
func BenchmarkAppendJSONBytes(b *testing.B) {
func BenchmarkappendBytes(b *testing.B) {
tests := map[string]string{
"NoEncoding": `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
"EncodingFirst": `"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
@@ -143,7 +143,7 @@ func BenchmarkAppendJSONBytes(b *testing.B) {
b.Run(name, func(b *testing.B) {
buf := make([]byte, 0, 100)
for i := 0; i < b.N; i++ {
_ = appendJSONBytes(buf, byt)
_ = AppendBytes(buf, byt)
}
})
}
+68
View File
@@ -0,0 +1,68 @@
package json
import (
"strconv"
"time"
)
func AppendTime(dst []byte, t time.Time, format string) []byte {
if format == "" {
return AppendInt64(dst, t.Unix())
}
return append(t.AppendFormat(append(dst, '"'), format), '"')
}
func AppendTimes(dst []byte, vals []time.Time, format string) []byte {
if format == "" {
return appendUnixTimes(dst, vals)
}
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = append(vals[0].AppendFormat(append(dst, '"'), format), '"')
if len(vals) > 1 {
for _, t := range vals[1:] {
dst = append(t.AppendFormat(append(dst, ',', '"'), format), '"')
}
}
dst = append(dst, ']')
return dst
}
func appendUnixTimes(dst []byte, vals []time.Time) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendInt(dst, vals[0].Unix(), 10)
if len(vals) > 1 {
for _, t := range vals[1:] {
dst = strconv.AppendInt(dst, t.Unix(), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool) []byte {
if useInt {
return strconv.AppendInt(dst, int64(d/unit), 10)
}
return AppendFloat64(dst, float64(d)/float64(unit))
}
func AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = AppendDuration(dst, vals[0], unit, useInt)
if len(vals) > 1 {
for _, d := range vals[1:] {
dst = AppendDuration(append(dst, ','), d, unit, useInt)
}
}
dst = append(dst, ']')
return dst
}
+278
View File
@@ -0,0 +1,278 @@
package json
import (
"encoding/json"
"fmt"
"math"
"strconv"
)
func AppendBool(dst []byte, val bool) []byte {
return strconv.AppendBool(dst, val)
}
func AppendBools(dst []byte, vals []bool) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendBool(dst, vals[0])
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendBool(append(dst, ','), val)
}
}
dst = append(dst, ']')
return dst
}
func AppendInt(dst []byte, val int) []byte {
return strconv.AppendInt(dst, int64(val), 10)
}
func AppendInts(dst []byte, vals []int) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendInt(dst, int64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendInt8(dst []byte, val int8) []byte {
return strconv.AppendInt(dst, int64(val), 10)
}
func AppendInts8(dst []byte, vals []int8) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendInt(dst, int64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendInt16(dst []byte, val int16) []byte {
return strconv.AppendInt(dst, int64(val), 10)
}
func AppendInts16(dst []byte, vals []int16) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendInt(dst, int64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendInt32(dst []byte, val int32) []byte {
return strconv.AppendInt(dst, int64(val), 10)
}
func AppendInts32(dst []byte, vals []int32) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendInt(dst, int64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendInt64(dst []byte, val int64) []byte {
return strconv.AppendInt(dst, val, 10)
}
func AppendInts64(dst []byte, vals []int64) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendInt(dst, vals[0], 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendInt(append(dst, ','), val, 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendUint(dst []byte, val uint) []byte {
return strconv.AppendUint(dst, uint64(val), 10)
}
func AppendUints(dst []byte, vals []uint) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendUint8(dst []byte, val uint8) []byte {
return strconv.AppendUint(dst, uint64(val), 10)
}
func AppendUints8(dst []byte, vals []uint8) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendUint16(dst []byte, val uint16) []byte {
return strconv.AppendUint(dst, uint64(val), 10)
}
func AppendUints16(dst []byte, vals []uint16) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendUint32(dst []byte, val uint32) []byte {
return strconv.AppendUint(dst, uint64(val), 10)
}
func AppendUints32(dst []byte, vals []uint32) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
}
}
dst = append(dst, ']')
return dst
}
func AppendUint64(dst []byte, val uint64) []byte {
return strconv.AppendUint(dst, uint64(val), 10)
}
func AppendUints64(dst []byte, vals []uint64) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = strconv.AppendUint(dst, vals[0], 10)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = strconv.AppendUint(append(dst, ','), val, 10)
}
}
dst = append(dst, ']')
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, val float32) []byte {
return AppendFloat(dst, float64(val), 32)
}
func AppendFloats32(dst []byte, vals []float32) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = AppendFloat(dst, float64(vals[0]), 32)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = AppendFloat(append(dst, ','), float64(val), 32)
}
}
dst = append(dst, ']')
return dst
}
func AppendFloat64(dst []byte, val float64) []byte {
return AppendFloat(dst, val, 64)
}
func AppendFloats64(dst []byte, vals []float64) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = AppendFloat(dst, vals[0], 32)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = AppendFloat(append(dst, ','), val, 64)
}
}
dst = append(dst, ']')
return dst
}
func AppendInterface(dst []byte, i interface{}) []byte {
marshaled, err := json.Marshal(i)
if err != nil {
return AppendString(dst, fmt.Sprintf("marshaling error: %v", err))
}
return append(dst, marshaled...)
}
+33
View File
@@ -0,0 +1,33 @@
package json
import (
"math"
"reflect"
"testing"
)
func Test_appendFloat64(t *testing.T) {
tests := []struct {
name string
input float64
want []byte
}{
{"-Inf", math.Inf(-1), []byte(`"-Inf"`)},
{"+Inf", math.Inf(1), []byte(`"+Inf"`)},
{"NaN", math.NaN(), []byte(`"NaN"`)},
{"0", 0, []byte(`0`)},
{"-1.1", -1.1, []byte(`-1.1`)},
{"1e20", 1e20, []byte(`100000000000000000000`)},
{"1e21", 1e21, []byte(`1000000000000000000000`)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := AppendFloat32([]byte{}, float32(tt.input)); !reflect.DeepEqual(got, tt.want) {
t.Errorf("appendFloat32() = %s, want %s", got, tt.want)
}
if got := AppendFloat64([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {
t.Errorf("appendFloat32() = %s, want %s", got, tt.want)
}
})
}
}
+12 -1
View File
@@ -73,6 +73,8 @@ import (
"os"
"strconv"
"sync/atomic"
"github.com/rs/zerolog/internal/json"
)
// Level defines log levels.
@@ -160,6 +162,15 @@ func Nop() Logger {
return New(nil).Level(Disabled)
}
// Output duplicates the current logger and sets w as its output.
func (l Logger) Output(w io.Writer) Logger {
l2 := New(w)
l2.level = l.level
l2.sample = l.sample
copy(l2.context, l.context)
return l2
}
// With creates a child logger with the field added to its context.
func (l Logger) With() Context {
context := l.context
@@ -306,7 +317,7 @@ func (l Logger) newEvent(level Level, addLevelField bool, done func(string)) *Ev
e.done = done
if l.context != nil && len(l.context) > 0 && l.context[0] > 0 {
// first byte of context is ts flag
e.buf = appendTimestamp(e.buf)
e.buf = json.AppendTime(json.AppendKey(e.buf, TimestampFieldName), TimestampFunc(), TimeFieldFormat)
}
if addLevelField {
e.Str(LevelFieldName, level.String())
+6
View File
@@ -3,6 +3,7 @@ package log
import (
"context"
"io"
"os"
"github.com/rs/zerolog"
@@ -11,6 +12,11 @@ import (
// Logger is the global logger.
var Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()
// Output duplicates the global logger and sets w as its output.
func Output(w io.Writer) zerolog.Logger {
return Logger.Output(w)
}
// With creates a child logger with the field added to its context.
func With() zerolog.Context {
return Logger.With()
+109
View File
@@ -138,6 +138,71 @@ func ExampleEvent_Dict() {
// Output: {"foo":"bar","dict":{"bar":"baz","n":1},"message":"hello world"}
}
type User struct {
Name string
Age int
Created time.Time
}
func (u User) MarshalZerologObject(e *zerolog.Event) {
e.Str("name", u.Name).
Int("age", u.Age).
Time("created", u.Created)
}
type Users []User
func (uu Users) MarshalZerologArray(a *zerolog.Array) {
for _, u := range uu {
a.Object(u)
}
}
func ExampleEvent_Array() {
log := zerolog.New(os.Stdout)
log.Log().
Str("foo", "bar").
Array("array", zerolog.Arr().
Str("baz").
Int(1),
).
Msg("hello world")
// Output: {"foo":"bar","array":["baz",1],"message":"hello world"}
}
func ExampleEvent_Array_object() {
log := zerolog.New(os.Stdout)
// Users implements zerolog.LogArrayMarshaler
u := Users{
User{"John", 35, time.Time{}},
User{"Bob", 55, time.Time{}},
}
log.Log().
Str("foo", "bar").
Array("users", u).
Msg("hello world")
// Output: {"foo":"bar","users":[{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},{"name":"Bob","age":55,"created":"0001-01-01T00:00:00Z"}],"message":"hello world"}
}
func ExampleEvent_Object() {
log := zerolog.New(os.Stdout)
// User implements zerolog.LogObjectMarshaler
u := User{"John", 35, time.Time{}}
log.Log().
Str("foo", "bar").
Object("user", u).
Msg("hello world")
// Output: {"foo":"bar","user":{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},"message":"hello world"}
}
func ExampleEvent_Interface() {
log := zerolog.New(os.Stdout)
@@ -197,6 +262,50 @@ func ExampleContext_Dict() {
// Output: {"foo":"bar","dict":{"bar":"baz","n":1},"message":"hello world"}
}
func ExampleContext_Array() {
log := zerolog.New(os.Stdout).With().
Str("foo", "bar").
Array("array", zerolog.Arr().
Str("baz").
Int(1),
).Logger()
log.Log().Msg("hello world")
// Output: {"foo":"bar","array":["baz",1],"message":"hello world"}
}
func ExampleContext_Array_object() {
// Users implements zerolog.LogArrayMarshaler
u := Users{
User{"John", 35, time.Time{}},
User{"Bob", 55, time.Time{}},
}
log := zerolog.New(os.Stdout).With().
Str("foo", "bar").
Array("users", u).
Logger()
log.Log().Msg("hello world")
// Output: {"foo":"bar","users":[{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},{"name":"Bob","age":55,"created":"0001-01-01T00:00:00Z"}],"message":"hello world"}
}
func ExampleContext_Object() {
// User implements zerolog.LogObjectMarshaler
u := User{"John", 35, time.Time{}}
log := zerolog.New(os.Stdout).With().
Str("foo", "bar").
Object("user", u).
Logger()
log.Log().Msg("hello world")
// Output: {"foo":"bar","user":{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},"message":"hello world"}
}
func ExampleContext_Interface() {
obj := struct {
Name string `json:"name"`