mirror of
https://github.com/rs/zerolog
synced 2026-06-08 17:13:30 +00:00
Compare commits
10 Commits
stacktrace
...
v1.14.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 33f552ec3d | |||
| 2a07580c27 | |||
| 3e85c4b21c | |||
| 509d727fba | |||
| 651d361cfe | |||
| 8e5449ab35 | |||
| 6d6350a511 | |||
| 299ff038c1 | |||
| 4daee2b758 | |||
| aa55558e4c |
@@ -4,6 +4,8 @@ go:
|
||||
- "1.8"
|
||||
- "1.9"
|
||||
- "1.10"
|
||||
- "1.11"
|
||||
- "1.12"
|
||||
- "master"
|
||||
matrix:
|
||||
allow_failures:
|
||||
|
||||
@@ -53,7 +53,7 @@ func main() {
|
||||
// UNIX Time is faster and smaller than most timestamps
|
||||
// If you set zerolog.TimeFieldFormat to an empty string,
|
||||
// logs will write with UNIX time
|
||||
zerolog.TimeFieldFormat = ""
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
|
||||
log.Print("hello world")
|
||||
}
|
||||
@@ -76,7 +76,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = ""
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
|
||||
log.Debug().
|
||||
Str("Scale", "833 cents").
|
||||
@@ -102,7 +102,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = ""
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
|
||||
log.Info().Msg("hello world")
|
||||
}
|
||||
@@ -138,7 +138,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = ""
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
debug := flag.Bool("debug", false, "sets log level to debug")
|
||||
|
||||
flag.Parse()
|
||||
@@ -189,7 +189,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = ""
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
|
||||
log.Log().
|
||||
Str("foo", "bar").
|
||||
@@ -215,7 +215,7 @@ func main() {
|
||||
err := errors.New("A repo man spends his life getting into tense situations")
|
||||
service := "myservice"
|
||||
|
||||
zerolog.TimeFieldFormat = ""
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
@@ -474,9 +474,8 @@ Some settings can be changed and will by applied to all loggers:
|
||||
* `zerolog.LevelFieldName`: Can be set to customize level field name.
|
||||
* `zerolog.MessageFieldName`: Can be set to customize message field name.
|
||||
* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.
|
||||
* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with an empty string, times are formated as UNIX timestamp.
|
||||
// DurationFieldUnit defines the unit for time.Duration type fields added
|
||||
// using the Dur method.
|
||||
* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix` or `zerolog.TimeFormatUnixMs`, times are formated as UNIX timestamp.
|
||||
* DurationFieldUnit defines the unit for time.Duration type fields added using the Dur method.
|
||||
* `DurationFieldUnit`: Sets the unit of the fields added by `Dur` (default: `time.Millisecond`).
|
||||
* `DurationFieldInteger`: If set to true, `Dur` fields are formatted as integers instead of floats.
|
||||
* `ErrorHandler`: Called whenever zerolog fails to write an event on its output. If not set, an error is printed on the stderr. This handler must be thread safe and non-blocking.
|
||||
|
||||
+1
-1
@@ -234,7 +234,7 @@ func BenchmarkLogFieldType(b *testing.B) {
|
||||
|
||||
func BenchmarkContextFieldType(b *testing.B) {
|
||||
oldFormat := TimeFieldFormat
|
||||
TimeFieldFormat = ""
|
||||
TimeFieldFormat = TimeFormatUnix
|
||||
defer func() { TimeFieldFormat = oldFormat }()
|
||||
bools := []bool{true, false, true, false, true, false, true, false, true, false}
|
||||
ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
+21
-10
@@ -13,11 +13,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
colorBold = iota + 1
|
||||
colorFaint
|
||||
)
|
||||
|
||||
const (
|
||||
colorBlack = iota + 30
|
||||
colorRed
|
||||
@@ -27,6 +22,9 @@ const (
|
||||
colorMagenta
|
||||
colorCyan
|
||||
colorWhite
|
||||
|
||||
colorBold = 1
|
||||
colorDarkGray = 90
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -297,9 +295,15 @@ func consoleDefaultFormatTimestamp(timeFormat string, noColor bool) Formatter {
|
||||
t = ts.Format(timeFormat)
|
||||
}
|
||||
case json.Number:
|
||||
t = tt.String()
|
||||
i, err := tt.Int64()
|
||||
if err != nil {
|
||||
t = tt.String()
|
||||
} else {
|
||||
ts := time.Unix(i, 0)
|
||||
t = ts.Format(timeFormat)
|
||||
}
|
||||
}
|
||||
return colorize(t, colorFaint, noColor)
|
||||
return colorize(t, colorDarkGray, noColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +328,11 @@ func consoleDefaultFormatLevel(noColor bool) Formatter {
|
||||
l = colorize("???", colorBold, noColor)
|
||||
}
|
||||
} else {
|
||||
l = strings.ToUpper(fmt.Sprintf("%s", i))[0:3]
|
||||
if i == nil {
|
||||
l = colorize("???", colorBold, noColor)
|
||||
} else {
|
||||
l = strings.ToUpper(fmt.Sprintf("%s", i))[0:3]
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
@@ -342,19 +350,22 @@ func consoleDefaultFormatCaller(noColor bool) Formatter {
|
||||
c = strings.TrimPrefix(c, cwd)
|
||||
c = strings.TrimPrefix(c, "/")
|
||||
}
|
||||
c = colorize(c, colorBold, noColor) + colorize(" >", colorFaint, noColor)
|
||||
c = colorize(c, colorBold, noColor) + colorize(" >", colorCyan, noColor)
|
||||
}
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
func consoleDefaultFormatMessage(i interface{}) string {
|
||||
if i == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s", i)
|
||||
}
|
||||
|
||||
func consoleDefaultFormatFieldName(noColor bool) Formatter {
|
||||
return func(i interface{}) string {
|
||||
return colorize(fmt.Sprintf("%s=", i), colorFaint, noColor)
|
||||
return colorize(fmt.Sprintf("%s=", i), colorCyan, noColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+80
-10
@@ -76,7 +76,7 @@ func TestConsoleWriter(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true, PartsOrder: []string{"foo"}}
|
||||
|
||||
_, err := w.Write([]byte(`{"foo" : "DEFAULT"}`))
|
||||
_, err := w.Write([]byte(`{"foo": "DEFAULT"}`))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
}
|
||||
@@ -92,12 +92,12 @@ func TestConsoleWriter(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: false}
|
||||
|
||||
_, err := w.Write([]byte(`{"level" : "warn", "message" : "Foobar"}`))
|
||||
_, err := w.Write([]byte(`{"level": "warn", "message": "Foobar"}`))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
}
|
||||
|
||||
expectedOutput := "\x1b[2m<nil>\x1b[0m \x1b[31mWRN\x1b[0m Foobar\n"
|
||||
expectedOutput := "\x1b[90m<nil>\x1b[0m \x1b[31mWRN\x1b[0m Foobar\n"
|
||||
actualOutput := buf.String()
|
||||
if actualOutput != expectedOutput {
|
||||
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
|
||||
@@ -109,7 +109,7 @@ func TestConsoleWriter(t *testing.T) {
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true}
|
||||
|
||||
d := time.Unix(0, 0).UTC().Format(time.RFC3339)
|
||||
_, err := w.Write([]byte(`{"time" : "` + d + `", "level" : "debug", "message" : "Foobar", "foo" : "bar"}`))
|
||||
_, err := w.Write([]byte(`{"time": "` + d + `", "level": "debug", "message": "Foobar", "foo": "bar"}`))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
}
|
||||
@@ -121,12 +121,82 @@ func TestConsoleWriter(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unix timestamp input format", func(t *testing.T) {
|
||||
of := zerolog.TimeFieldFormat
|
||||
defer func() {
|
||||
zerolog.TimeFieldFormat = of
|
||||
}()
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true}
|
||||
|
||||
_, err := w.Write([]byte(`{"time": 0, "level": "debug", "message": "Foobar", "foo": "bar"}`))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
}
|
||||
|
||||
expectedOutput := "4:00PM DBG Foobar foo=bar\n"
|
||||
actualOutput := buf.String()
|
||||
if actualOutput != expectedOutput {
|
||||
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("No message field", func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true}
|
||||
|
||||
_, err := w.Write([]byte(`{"level": "debug", "foo": "bar"}`))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
}
|
||||
|
||||
expectedOutput := "<nil> DBG foo=bar\n"
|
||||
actualOutput := buf.String()
|
||||
if actualOutput != expectedOutput {
|
||||
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("No level field", func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true}
|
||||
|
||||
_, err := w.Write([]byte(`{"message": "Foobar", "foo": "bar"}`))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
}
|
||||
|
||||
expectedOutput := "<nil> ??? Foobar foo=bar\n"
|
||||
actualOutput := buf.String()
|
||||
if actualOutput != expectedOutput {
|
||||
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Write colorized fields", func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: false}
|
||||
|
||||
_, err := w.Write([]byte(`{"level": "warn", "message": "Foobar", "foo": "bar"}`))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
}
|
||||
|
||||
expectedOutput := "\x1b[90m<nil>\x1b[0m \x1b[31mWRN\x1b[0m Foobar \x1b[36mfoo=\x1b[0mbar\n"
|
||||
actualOutput := buf.String()
|
||||
if actualOutput != expectedOutput {
|
||||
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Write error field", func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true}
|
||||
|
||||
d := time.Unix(0, 0).UTC().Format(time.RFC3339)
|
||||
evt := `{"time" : "` + d + `", "level" : "error", "message" : "Foobar", "aaa" : "bbb", "error" : "Error"}`
|
||||
evt := `{"time": "` + d + `", "level": "error", "message": "Foobar", "aaa": "bbb", "error": "Error"}`
|
||||
// t.Log(evt)
|
||||
|
||||
_, err := w.Write([]byte(evt))
|
||||
@@ -151,7 +221,7 @@ func TestConsoleWriter(t *testing.T) {
|
||||
}
|
||||
|
||||
d := time.Unix(0, 0).UTC().Format(time.RFC3339)
|
||||
evt := `{"time" : "` + d + `", "level" : "debug", "message" : "Foobar", "foo" : "bar", "caller" : "` + cwd + `/foo/bar.go"}`
|
||||
evt := `{"time": "` + d + `", "level": "debug", "message": "Foobar", "foo": "bar", "caller": "` + cwd + `/foo/bar.go"}`
|
||||
// t.Log(evt)
|
||||
|
||||
_, err = w.Write([]byte(evt))
|
||||
@@ -170,7 +240,7 @@ func TestConsoleWriter(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true}
|
||||
|
||||
evt := `{"level" : "debug", "message" : "Foobar", "foo" : [1, 2, 3], "bar" : true}`
|
||||
evt := `{"level": "debug", "message": "Foobar", "foo": [1, 2, 3], "bar": true}`
|
||||
// t.Log(evt)
|
||||
|
||||
_, err := w.Write([]byte(evt))
|
||||
@@ -192,7 +262,7 @@ func TestConsoleWriterConfiguration(t *testing.T) {
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true, TimeFormat: time.RFC3339}
|
||||
|
||||
d := time.Unix(0, 0).UTC().Format(time.RFC3339)
|
||||
evt := `{"time" : "` + d + `", "level" : "info", "message" : "Foobar"}`
|
||||
evt := `{"time": "` + d + `", "level": "info", "message": "Foobar"}`
|
||||
|
||||
_, err := w.Write([]byte(evt))
|
||||
if err != nil {
|
||||
@@ -210,7 +280,7 @@ func TestConsoleWriterConfiguration(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true, PartsOrder: []string{"message", "level"}}
|
||||
|
||||
evt := `{"level" : "info", "message" : "Foobar"}`
|
||||
evt := `{"level": "info", "message": "Foobar"}`
|
||||
_, err := w.Write([]byte(evt))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
@@ -228,7 +298,7 @@ func BenchmarkConsoleWriter(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
var msg = []byte(`{"level" : "info", "foo" : "bar", "message" : "HELLO", "time" : "1990-01-01"}`)
|
||||
var msg = []byte(`{"level": "info", "foo": "bar", "message": "HELLO", "time": "1990-01-01"}`)
|
||||
|
||||
w := zerolog.ConsoleWriter{Out: ioutil.Discard, NoColor: false}
|
||||
|
||||
|
||||
+46
-6
@@ -2,6 +2,7 @@ package zerolog
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
@@ -347,14 +348,31 @@ func (c Context) Interface(key string, i interface{}) Context {
|
||||
return c
|
||||
}
|
||||
|
||||
type callerHook struct{}
|
||||
|
||||
func (ch callerHook) Run(e *Event, level Level, msg string) {
|
||||
// Three extra frames to skip (added by hook infra).
|
||||
e.caller(CallerSkipFrameCount + 3)
|
||||
type callerHook struct {
|
||||
callerSkipFrameCount int
|
||||
}
|
||||
|
||||
var ch = callerHook{}
|
||||
func newCallerHook(skipFrameCount int) callerHook {
|
||||
return callerHook{callerSkipFrameCount: skipFrameCount}
|
||||
}
|
||||
|
||||
func (ch callerHook) Run(e *Event, level Level, msg string) {
|
||||
switch ch.callerSkipFrameCount {
|
||||
case useGlobalSkipFrameCount:
|
||||
// Extra frames to skip (added by hook infra).
|
||||
e.caller(CallerSkipFrameCount + contextCallerSkipFrameCount)
|
||||
default:
|
||||
// Extra frames to skip (added by hook infra).
|
||||
e.caller(ch.callerSkipFrameCount + contextCallerSkipFrameCount)
|
||||
}
|
||||
}
|
||||
|
||||
// useGlobalSkipFrameCount acts as a flag to informat callerHook.Run
|
||||
// to use the global CallerSkipFrameCount.
|
||||
const useGlobalSkipFrameCount = math.MinInt32
|
||||
|
||||
// ch is the default caller hook using the global CallerSkipFrameCount.
|
||||
var ch = newCallerHook(useGlobalSkipFrameCount)
|
||||
|
||||
// Caller adds the file:line of the caller with the zerolog.CallerFieldName key.
|
||||
func (c Context) Caller() Context {
|
||||
@@ -362,6 +380,28 @@ func (c Context) Caller() Context {
|
||||
return c
|
||||
}
|
||||
|
||||
// CallerWithSkipFrameCount adds the file:line of the caller with the zerolog.CallerFieldName key.
|
||||
// The specified skipFrameCount int will override the global CallerSkipFrameCount for this context's respective logger.
|
||||
// If set to -1 the global CallerSkipFrameCount will be used.
|
||||
func (c Context) CallerWithSkipFrameCount(skipFrameCount int) Context {
|
||||
c.l = c.l.Hook(newCallerHook(skipFrameCount))
|
||||
return c
|
||||
}
|
||||
|
||||
type stackTraceHook struct{}
|
||||
|
||||
func (sh stackTraceHook) Run(e *Event, level Level, msg string) {
|
||||
e.Stack()
|
||||
}
|
||||
|
||||
var sh = stackTraceHook{}
|
||||
|
||||
// Stack enables stack trace printing for the error passed to Err().
|
||||
func (c Context) Stack() Context {
|
||||
c.l = c.l.Hook(sh)
|
||||
return c
|
||||
}
|
||||
|
||||
// IPAddr adds IPv4 or IPv6 Address to the context
|
||||
func (c Context) IPAddr(key string, ip net.IP) Context {
|
||||
c.l.context = enc.AppendIPAddr(enc.AppendKey(c.l.context, key), ip)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -18,11 +17,6 @@ var eventPool = &sync.Pool{
|
||||
},
|
||||
}
|
||||
|
||||
// ErrorMarshalFunc allows customization of global error marshaling
|
||||
var ErrorMarshalFunc = func(err error) interface{} {
|
||||
return err
|
||||
}
|
||||
|
||||
// Event represents a log event. It is instanced by one of the level method of
|
||||
// Logger and finalized by the Msg or Msgf method.
|
||||
type Event struct {
|
||||
@@ -30,6 +24,7 @@ type Event struct {
|
||||
w LevelWriter
|
||||
level Level
|
||||
done func(msg string)
|
||||
stack bool // enable error stack trace
|
||||
ch []Hook // hooks from context
|
||||
}
|
||||
|
||||
@@ -271,8 +266,10 @@ func (e *Event) RawJSON(key string, b []byte) *Event {
|
||||
// AnErr adds the field key with serialized err to the *Event context.
|
||||
// If err is nil, no field is added.
|
||||
func (e *Event) AnErr(key string, err error) *Event {
|
||||
marshaled := ErrorMarshalFunc(err)
|
||||
switch m := marshaled.(type) {
|
||||
if e == nil {
|
||||
return e
|
||||
}
|
||||
switch m := ErrorMarshalFunc(err).(type) {
|
||||
case nil:
|
||||
return e
|
||||
case LogObjectMarshaler:
|
||||
@@ -292,11 +289,9 @@ func (e *Event) Errs(key string, errs []error) *Event {
|
||||
if e == nil {
|
||||
return e
|
||||
}
|
||||
|
||||
arr := Arr()
|
||||
for _, err := range errs {
|
||||
marshaled := ErrorMarshalFunc(err)
|
||||
switch m := marshaled.(type) {
|
||||
switch m := ErrorMarshalFunc(err).(type) {
|
||||
case LogObjectMarshaler:
|
||||
arr = arr.Object(m)
|
||||
case error:
|
||||
@@ -314,10 +309,42 @@ func (e *Event) Errs(key string, errs []error) *Event {
|
||||
// Err adds the field "error" with serialized err to the *Event context.
|
||||
// If err is nil, no field is added.
|
||||
// To customize the key name, change zerolog.ErrorFieldName.
|
||||
//
|
||||
// To customize the key name, change zerolog.ErrorFieldName.
|
||||
//
|
||||
// If Stack() has been called before and zerolog.ErrorStackMarshaler is defined,
|
||||
// the err is passed to ErrorStackMarshaler and the result is appended to the
|
||||
// zerolog.ErrorStackFieldName.
|
||||
func (e *Event) Err(err error) *Event {
|
||||
if e == nil {
|
||||
return e
|
||||
}
|
||||
if e.stack && ErrorStackMarshaler != nil {
|
||||
switch m := ErrorStackMarshaler(err).(type) {
|
||||
case nil:
|
||||
case LogObjectMarshaler:
|
||||
e.Object(ErrorStackFieldName, m)
|
||||
case error:
|
||||
e.Str(ErrorStackFieldName, m.Error())
|
||||
case string:
|
||||
e.Str(ErrorStackFieldName, m)
|
||||
default:
|
||||
e.Interface(ErrorStackFieldName, m)
|
||||
}
|
||||
}
|
||||
return e.AnErr(ErrorFieldName, err)
|
||||
}
|
||||
|
||||
// Stack enables stack trace printing for the error passed to Err().
|
||||
//
|
||||
// ErrorStackMarshaler must be set for this method to do something.
|
||||
func (e *Event) Stack() *Event {
|
||||
if e != nil {
|
||||
e.stack = true
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Bool adds the field key with val as a bool to the *Event context.
|
||||
func (e *Event) Bool(key string, b bool) *Event {
|
||||
if e == nil {
|
||||
@@ -645,7 +672,7 @@ func (e *Event) caller(skip int) *Event {
|
||||
if !ok {
|
||||
return e
|
||||
}
|
||||
e.buf = enc.AppendString(enc.AppendKey(e.buf, CallerFieldName), file+":"+strconv.Itoa(line))
|
||||
e.buf = enc.AppendString(enc.AppendKey(e.buf, CallerFieldName), CallerMarshalFunc(file, line))
|
||||
return e
|
||||
}
|
||||
|
||||
|
||||
+38
-4
@@ -1,8 +1,21 @@
|
||||
package zerolog
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
import "sync/atomic"
|
||||
|
||||
const (
|
||||
// TimeFormatUnix defines a time format that makes time fields to be
|
||||
// serialized as Unix timestamp integers.
|
||||
TimeFormatUnix = ""
|
||||
|
||||
// TimeFormatUnix defines a time format that makes time fields to be
|
||||
// serialized as Unix timestamp integers in milliseconds.
|
||||
TimeFormatUnixMs = "UNIXMS"
|
||||
)
|
||||
|
||||
var (
|
||||
// TimestampFieldName is the field name used for the timestamp field.
|
||||
TimestampFieldName = "time"
|
||||
@@ -10,6 +23,11 @@ var (
|
||||
// LevelFieldName is the field name used for the level field.
|
||||
LevelFieldName = "level"
|
||||
|
||||
// LevelFieldMarshalFunc allows customization of global level field marshaling
|
||||
LevelFieldMarshalFunc = func(l Level) string {
|
||||
return l.String()
|
||||
}
|
||||
|
||||
// MessageFieldName is the field name used for the message field.
|
||||
MessageFieldName = "message"
|
||||
|
||||
@@ -22,9 +40,25 @@ var (
|
||||
// CallerSkipFrameCount is the number of stack frames to skip to find the caller.
|
||||
CallerSkipFrameCount = 2
|
||||
|
||||
// TimeFieldFormat defines the time format of the Time field type.
|
||||
// If set to an empty string, the time is formatted as an UNIX timestamp
|
||||
// as integer.
|
||||
// CallerMarshalFunc allows customization of global caller marshaling
|
||||
CallerMarshalFunc = func(file string, line int) string {
|
||||
return file + ":" + strconv.Itoa(line)
|
||||
}
|
||||
|
||||
// ErrorStackFieldName is the field name used for error stacks.
|
||||
ErrorStackFieldName = "stack"
|
||||
|
||||
// ErrorStackMarshaler extract the stack from err if any.
|
||||
ErrorStackMarshaler func(err error) interface{}
|
||||
|
||||
// ErrorMarshalFunc allows customization of global error marshaling
|
||||
ErrorMarshalFunc = func(err error) interface{} {
|
||||
return err
|
||||
}
|
||||
|
||||
// TimeFieldFormat defines the time format of the Time field type. If set to
|
||||
// TimeFormatUnix or TimeFormatUnixMs, the time is formatted as an UNIX
|
||||
// timestamp as integer.
|
||||
TimeFieldFormat = time.RFC3339
|
||||
|
||||
// TimestampFunc defines the function called to generate a timestamp.
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
module github.com/rs/zerolog
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/rs/xid v1.2.1
|
||||
github.com/zenazn/goji v0.9.0
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc
|
||||
)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// +build go1.12
|
||||
|
||||
package zerolog
|
||||
|
||||
// Since go 1.12, some auto generated init functions are hidden from
|
||||
// runtime.Caller.
|
||||
const contextCallerSkipFrameCount = 2
|
||||
+29
-2
@@ -5,11 +5,20 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Import from zerolog/global.go
|
||||
timeFormatUnix = ""
|
||||
timeFormatUnixMs = "UNIXMS"
|
||||
)
|
||||
|
||||
// AppendTime formats the input time with the given format
|
||||
// and appends the encoded string to the input byte slice.
|
||||
func (e Encoder) AppendTime(dst []byte, t time.Time, format string) []byte {
|
||||
if format == "" {
|
||||
switch format {
|
||||
case timeFormatUnix:
|
||||
return e.AppendInt64(dst, t.Unix())
|
||||
case timeFormatUnixMs:
|
||||
return e.AppendInt64(dst, t.UnixNano()/1000000)
|
||||
}
|
||||
return append(t.AppendFormat(append(dst, '"'), format), '"')
|
||||
}
|
||||
@@ -17,8 +26,11 @@ func (e Encoder) AppendTime(dst []byte, t time.Time, format string) []byte {
|
||||
// AppendTimes converts the input times with the given format
|
||||
// and appends the encoded string list to the input byte slice.
|
||||
func (Encoder) AppendTimes(dst []byte, vals []time.Time, format string) []byte {
|
||||
if format == "" {
|
||||
switch format {
|
||||
case timeFormatUnix:
|
||||
return appendUnixTimes(dst, vals)
|
||||
case timeFormatUnixMs:
|
||||
return appendUnixMsTimes(dst, vals)
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
return append(dst, '[', ']')
|
||||
@@ -49,6 +61,21 @@ func appendUnixTimes(dst []byte, vals []time.Time) []byte {
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendUnixMsTimes(dst []byte, vals []time.Time) []byte {
|
||||
if len(vals) == 0 {
|
||||
return append(dst, '[', ']')
|
||||
}
|
||||
dst = append(dst, '[')
|
||||
dst = strconv.AppendInt(dst, vals[0].UnixNano()/1000000, 10)
|
||||
if len(vals) > 1 {
|
||||
for _, t := range vals[1:] {
|
||||
dst = strconv.AppendInt(append(dst, ','), t.UnixNano()/1000000, 10)
|
||||
}
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
return dst
|
||||
}
|
||||
|
||||
// AppendDuration formats the input duration with the given unit & format
|
||||
// and appends the encoded string to the input byte slice.
|
||||
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool) []byte {
|
||||
|
||||
@@ -152,19 +152,19 @@ func (l Level) String() string {
|
||||
// returns an error if the input string does not match known values.
|
||||
func ParseLevel(levelStr string) (Level, error) {
|
||||
switch levelStr {
|
||||
case DebugLevel.String():
|
||||
case LevelFieldMarshalFunc(DebugLevel):
|
||||
return DebugLevel, nil
|
||||
case InfoLevel.String():
|
||||
case LevelFieldMarshalFunc(InfoLevel):
|
||||
return InfoLevel, nil
|
||||
case WarnLevel.String():
|
||||
case LevelFieldMarshalFunc(WarnLevel):
|
||||
return WarnLevel, nil
|
||||
case ErrorLevel.String():
|
||||
case LevelFieldMarshalFunc(ErrorLevel):
|
||||
return ErrorLevel, nil
|
||||
case FatalLevel.String():
|
||||
case LevelFieldMarshalFunc(FatalLevel):
|
||||
return FatalLevel, nil
|
||||
case PanicLevel.String():
|
||||
case LevelFieldMarshalFunc(PanicLevel):
|
||||
return PanicLevel, nil
|
||||
case NoLevel.String():
|
||||
case LevelFieldMarshalFunc(NoLevel):
|
||||
return NoLevel, nil
|
||||
}
|
||||
return NoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
|
||||
@@ -291,6 +291,18 @@ func (l *Logger) Error() *Event {
|
||||
return l.newEvent(ErrorLevel, nil)
|
||||
}
|
||||
|
||||
// Err starts a new message with error level with err as a field if not nil or
|
||||
// with info level if err is nil.
|
||||
//
|
||||
// You must call Msg on the returned event in order to send the event.
|
||||
func (l *Logger) Err(err error) *Event {
|
||||
if err != nil {
|
||||
return l.Error().Err(err)
|
||||
} else {
|
||||
return l.Info()
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal starts a new message with fatal level. The os.Exit(1) function
|
||||
// is called by the Msg method, which terminates the program immediately.
|
||||
//
|
||||
@@ -380,7 +392,7 @@ func (l *Logger) newEvent(level Level, done func(string)) *Event {
|
||||
e.done = done
|
||||
e.ch = l.hooks
|
||||
if level != NoLevel {
|
||||
e.Str(LevelFieldName, level.String())
|
||||
e.Str(LevelFieldName, LevelFieldMarshalFunc(level))
|
||||
}
|
||||
if l.context != nil && len(l.context) > 0 {
|
||||
e.buf = enc.AppendObjectData(e.buf, l.context)
|
||||
|
||||
+85
@@ -7,6 +7,8 @@ import (
|
||||
"net"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -105,6 +107,20 @@ func TestWith(t *testing.T) {
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
|
||||
// Validate CallerWithSkipFrameCount.
|
||||
out.Reset()
|
||||
_, file, line, _ = runtime.Caller(0)
|
||||
caller = fmt.Sprintf("%s:%d", file, line+5)
|
||||
log = ctx.CallerWithSkipFrameCount(3).Logger()
|
||||
func() {
|
||||
log.Log().Msg("")
|
||||
}()
|
||||
// The above line is a little contrived, but the line above should be the line due
|
||||
// to the extra frame skip.
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsMap(t *testing.T) {
|
||||
@@ -641,6 +657,75 @@ func TestErrorMarshalFunc(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallerMarshalFunc(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
|
||||
// test default behaviour this is really brittle due to the line numbers
|
||||
// actually mattering for validation
|
||||
_, file, line, _ := runtime.Caller(0)
|
||||
caller := fmt.Sprintf("%s:%d", file, line+2)
|
||||
log.Log().Caller().Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","message":"msg"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
out.Reset()
|
||||
|
||||
// test custom behavior. In this case we'll take just the last directory
|
||||
origCallerMarshalFunc := CallerMarshalFunc
|
||||
defer func() { CallerMarshalFunc = origCallerMarshalFunc }()
|
||||
CallerMarshalFunc = func(file string, line int) string {
|
||||
parts := strings.Split(file, "/")
|
||||
if len(parts) > 1 {
|
||||
return strings.Join(parts[len(parts)-2:], "/") + ":" + strconv.Itoa(line)
|
||||
} else {
|
||||
return file + ":" + strconv.Itoa(line)
|
||||
}
|
||||
}
|
||||
_, file, line, _ = runtime.Caller(0)
|
||||
caller = CallerMarshalFunc(file, line+2)
|
||||
log.Log().Caller().Msg("msg")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","message":"msg"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLevelFieldMarshalFunc(t *testing.T) {
|
||||
origLevelFieldMarshalFunc := LevelFieldMarshalFunc
|
||||
LevelFieldMarshalFunc = func(l Level) string {
|
||||
return strings.ToUpper(l.String())
|
||||
}
|
||||
defer func() {
|
||||
LevelFieldMarshalFunc = origLevelFieldMarshalFunc
|
||||
}()
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
|
||||
log.Debug().Msg("test")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"DEBUG","message":"test"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
out.Reset()
|
||||
|
||||
log.Info().Msg("test")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"INFO","message":"test"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
out.Reset()
|
||||
|
||||
log.Warn().Msg("test")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"WARN","message":"test"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
out.Reset()
|
||||
|
||||
log.Error().Msg("test")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"ERROR","message":"test"}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
out.Reset()
|
||||
}
|
||||
|
||||
type errWriter struct {
|
||||
error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// +build !go1.12
|
||||
|
||||
package zerolog
|
||||
|
||||
const contextCallerSkipFrameCount = 3
|
||||
@@ -0,0 +1,65 @@
|
||||
package pkgerrors
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
StackSourceFileName = "source"
|
||||
StackSourceLineName = "line"
|
||||
StackSourceFunctionName = "func"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
b []byte
|
||||
}
|
||||
|
||||
// Write implement fmt.Formatter interface.
|
||||
func (s *state) Write(b []byte) (n int, err error) {
|
||||
s.b = b
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Width implement fmt.Formatter interface.
|
||||
func (s *state) Width() (wid int, ok bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Precision implement fmt.Formatter interface.
|
||||
func (s *state) Precision() (prec int, ok bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Flag implement fmt.Formatter interface.
|
||||
func (s *state) Flag(c int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func frameField(f errors.Frame, s *state, c rune) string {
|
||||
f.Format(s, c)
|
||||
return string(s.b)
|
||||
}
|
||||
|
||||
// MarshalStack implements pkg/errors stack trace marshaling.
|
||||
//
|
||||
// zerolog.ErrorStackMarshaler = MarshalStack
|
||||
func MarshalStack(err error) interface{} {
|
||||
type stackTracer interface {
|
||||
StackTrace() errors.StackTrace
|
||||
}
|
||||
sterr, ok := err.(stackTracer)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
st := sterr.StackTrace()
|
||||
s := &state{}
|
||||
out := make([]map[string]string, 0, len(st))
|
||||
for _, frame := range st {
|
||||
out = append(out, map[string]string{
|
||||
StackSourceFileName: frameField(frame, s, 's'),
|
||||
StackSourceLineName: frameField(frame, s, 'd'),
|
||||
StackSourceFunctionName: frameField(frame, s, 'n'),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// +build !binary_log
|
||||
|
||||
package pkgerrors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func TestLogStack(t *testing.T) {
|
||||
zerolog.ErrorStackMarshaler = MarshalStack
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
log := zerolog.New(out)
|
||||
|
||||
err := errors.Wrap(errors.New("error message"), "from error")
|
||||
log.Log().Stack().Err(err).Msg("")
|
||||
|
||||
got := out.String()
|
||||
want := `\{"stack":\[\{"func":"TestLogStack","line":"20","source":"stacktrace_test.go"\},.*\],"error":"from error: error message"\}\n`
|
||||
if ok, _ := regexp.MatchString(want, got); !ok {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLogStack(b *testing.B) {
|
||||
zerolog.ErrorStackMarshaler = MarshalStack
|
||||
out := &bytes.Buffer{}
|
||||
log := zerolog.New(out)
|
||||
err := errors.Wrap(errors.New("error message"), "from error")
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Log().Stack().Err(err).Msg("")
|
||||
out.Reset()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user