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

Compare commits

..

5 Commits

Author SHA1 Message Date
Olivier Poitrey 9f1f25d2fa pkgerrors: quick refactoring for better readibility 2019-01-02 17:07:20 -08:00
Olivier Poitrey 6d2153805b Revert change on go.mod 2019-01-02 17:02:58 -08:00
Olivier Poitrey 2988c1e444 pkgerrors: remove fmt dep 2019-01-02 13:19:14 -08:00
Olivier Poitrey adb19ff852 Merge branch 'master' into stacktrace 2019-01-02 12:11:57 -08:00
Olivier Poitrey 3786fbfa73 Add support for stack trace extration of error fields 2018-02-11 17:55:34 -08:00
44 changed files with 160 additions and 1075 deletions
-2
View File
@@ -4,8 +4,6 @@ go:
- "1.8"
- "1.9"
- "1.10"
- "1.11"
- "1.12"
- "master"
matrix:
allow_failures:
+24 -51
View File
@@ -31,7 +31,7 @@ Find out [who uses zerolog](https://github.com/rs/zerolog/wiki/Who-uses-zerolog)
## Installation
```bash
```go
go get -u github.com/rs/zerolog/log
```
@@ -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.TimeFormatUnix
zerolog.TimeFieldFormat = ""
log.Print("hello world")
}
@@ -76,20 +76,15 @@ import (
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
zerolog.TimeFieldFormat = ""
log.Debug().
Str("Scale", "833 cents").
Float64("Interval", 833.09).
Msg("Fibonacci is everywhere")
log.Debug().
Str("Name", "Tom").
Send()
}
// Output: {"level":"debug","Scale":"833 cents","Interval":833.09,"time":1562212768,"message":"Fibonacci is everywhere"}
// Output: {"level":"debug","Name":"Tom","time":1562212768}
// Output: {"time":1524104936,"level":"debug","Scale":"833 cents","Interval":833.09,"message":"Fibonacci is everywhere"}
```
> You'll note in the above example that when adding contextual fields, the fields are strongly typed. You can find the full list of supported fields [here](#standard-types)
@@ -107,7 +102,7 @@ import (
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
zerolog.TimeFieldFormat = ""
log.Info().Msg("hello world")
}
@@ -125,7 +120,6 @@ func main() {
* warn (`zerolog.WarnLevel`, 2)
* info (`zerolog.InfoLevel`, 1)
* debug (`zerolog.DebugLevel`, 0)
* trace (`zerolog.TraceLevel`, -1)
You can set the Global logging level to any of these options using the `SetGlobalLevel` function in the zerolog package, passing in one of the given constants above, e.g. `zerolog.InfoLevel` would be the "info" level. Whichever level is chosen, all logs with a level greater than or equal to that level will be written. To turn off logging entirely, pass the `zerolog.Disabled` constant.
@@ -144,7 +138,7 @@ import (
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
zerolog.TimeFieldFormat = ""
debug := flag.Bool("debug", false, "sets log level to debug")
flag.Parse()
@@ -195,7 +189,7 @@ import (
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
zerolog.TimeFieldFormat = ""
log.Log().
Str("foo", "bar").
@@ -221,7 +215,7 @@ func main() {
err := errors.New("A repo man spends his life getting into tense situations")
service := "myservice"
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
zerolog.TimeFieldFormat = ""
log.Fatal().
Err(err).
@@ -341,7 +335,7 @@ If your writer might be slow or not thread-safe and you need your log producers
wr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) {
fmt.Printf("Logger Dropped %d messages", missed)
})
log := zerolog.New(wr)
log := zerolog.New(w)
log.Print("test")
```
@@ -435,11 +429,11 @@ c := alice.New()
c = c.Append(hlog.NewHandler(log))
// Install some provided extra handler to set some request's context fields.
// Thanks to that handler, all our logs will come with some prepopulated fields.
// Thanks to those handler, all our logs will come with some pre-populated fields.
c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
hlog.FromRequest(r).Info().
Str("method", r.Method).
Stringer("url", r.URL).
Str("url", r.URL.String()).
Int("status", status).
Int("size", size).
Dur("duration", duration).
@@ -469,40 +463,23 @@ if err := http.ListenAndServe(":8080", nil); err != nil {
}
```
## Multiple Log Output
`zerolog.MultiLevelWriter` may be used to send the log message to multiple outputs.
In this example, we send the log message to both `os.Stdout` and the in-built ConsoleWriter.
```go
func main() {
consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
multi := zerolog.MultiLevelWriter(consoleWriter, os.Stdout)
logger := zerolog.New(multi).With().Timestamp().Logger()
logger.Info().Msg("Hello World!")
}
// Output (Line 1: Console; Line 2: Stdout)
// 12:36PM INF Hello World!
// {"level":"info","time":"2019-11-07T12:36:38+03:00","message":"Hello World!"}
```
## Global Settings
Some settings can be changed and will by applied to all loggers:
* `log.Logger`: You can set this value to customize the global logger (the one used by package level methods).
* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Call this with `zerolog.Disabled` to disable logging altogether (quiet mode).
* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Set this to `zerolog.Disabled` to disable logging altogether (quiet mode).
* `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events.
* `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name.
* `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 `zerolog.TimeFormatUnix`, `zerolog.TimeFormatUnixMs` or `zerolog.TimeFormatUnixMicro`, times are formated as UNIX timestamp.
* `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`).
* `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`).
* `zerolog.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.
* `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.
* `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.
## Field Types
@@ -516,17 +493,13 @@ Some settings can be changed and will by applied to all loggers:
### Advanced Fields
* `Err`: Takes an `error` and renders it as a string using the `zerolog.ErrorFieldName` field name.
* `Timestamp`: Inserts a timestamp field with `zerolog.TimestampFieldName` field name, formatted using `zerolog.TimeFieldFormat`.
* `Time`: Adds a field with time formatted with `zerolog.TimeFieldFormat`.
* `Dur`: Adds a field with `time.Duration`.
* `Err`: Takes an `error` and render it as a string using the `zerolog.ErrorFieldName` field name.
* `Timestamp`: Insert a timestamp field with `zerolog.TimestampFieldName` field name and formatted using `zerolog.TimeFieldFormat`.
* `Time`: Adds a field with the time formated with the `zerolog.TimeFieldFormat`.
* `Dur`: Adds a field with a `time.Duration`.
* `Dict`: Adds a sub-key/value as a field of the event.
* `RawJSON`: Adds a field with an already encoded JSON (`[]byte`)
* `Hex`: Adds a field with value formatted as a hexadecimal string (`[]byte`)
* `Interface`: Uses reflection to marshal the type.
Most fields are also available in the slice format (`Strs` for `[]string`, `Errs` for `[]error` etc.)
## Binary Encoding
In addition to the default JSON encoding, `zerolog` can produce binary logs using [CBOR](http://cbor.io) encoding. The choice of encoding can be decided at compile time using the build tag `binary_log` as follows:
@@ -605,7 +578,7 @@ Log a static string, without any context or `printf`-style templating:
## Caveats
Note that zerolog does no de-duplication of fields. Using the same key multiple times creates multiple keys in final JSON:
Note that zerolog does de-duplication fields. Using the same key multiple times creates multiple keys in final JSON:
```go
logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
@@ -615,4 +588,4 @@ logger.Info().
// Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
```
In this case, many consumers will take the last value, but this is not guaranteed; check yours if in doubt.
However, its not a big deal as JSON accepts dup keys; the last one prevails.
+3 -12
View File
@@ -85,15 +85,10 @@ func (a *Array) Hex(val []byte) *Array {
return a
}
// RawJSON adds already encoded JSON to the array.
func (a *Array) RawJSON(val []byte) *Array {
a.buf = appendJSON(enc.AppendArrayDelim(a.buf), val)
return a
}
// Err serializes and appends the err to the array.
func (a *Array) Err(err error) *Array {
switch m := ErrorMarshalFunc(err).(type) {
marshaled := ErrorMarshalFunc(err)
switch m := marshaled.(type) {
case LogObjectMarshaler:
e := newEvent(nil, 0)
e.buf = e.buf[:0]
@@ -101,11 +96,7 @@ func (a *Array) Err(err error) *Array {
a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
putEvent(e)
case error:
if m == nil || isNilValue(m) {
a.buf = enc.AppendNil(enc.AppendArrayDelim(a.buf))
} else {
a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m.Error())
}
a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m.Error())
case string:
a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m)
default:
+1 -2
View File
@@ -24,11 +24,10 @@ func TestArray(t *testing.T) {
Str("a").
Bytes([]byte("b")).
Hex([]byte{0x1f}).
RawJSON([]byte(`{"some":"json"}`)).
Time(time.Time{}).
IPAddr(net.IP{192, 168, 0, 10}).
Dur(0)
want := `[true,1,2,3,4,5,6,7,8,9,10,11.98122,12.987654321,"a","b","1f",{"some":"json"},"0001-01-01T00:00:00Z","192.168.0.10",0]`
want := `[true,1,2,3,4,5,6,7,8,9,10,11.98122,12.987654321,"a","b","1f","0001-01-01T00:00:00Z","192.168.0.10",0]`
if got := decodeObjectToStr(a.write([]byte{})); got != want {
t.Errorf("Array.write()\ngot: %s\nwant: %s", got, want)
}
+1 -1
View File
@@ -234,7 +234,7 @@ func BenchmarkLogFieldType(b *testing.B) {
func BenchmarkContextFieldType(b *testing.B) {
oldFormat := TimeFieldFormat
TimeFieldFormat = TimeFormatUnix
TimeFieldFormat = ""
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}
-13
View File
@@ -108,19 +108,6 @@ func ExampleLogger_Printf() {
// Output: {"level":"debug","message":"hello world"}
}
func ExampleLogger_Trace() {
dst := bytes.Buffer{}
log := New(&dst)
log.Trace().
Str("foo", "bar").
Int("n", 123).
Msg("hello world")
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
// Output: {"level":"trace","foo":"bar","n":123,"message":"hello world"}
}
func ExampleLogger_Debug() {
dst := bytes.Buffer{}
log := New(&dst)
+10 -32
View File
@@ -13,6 +13,11 @@ import (
"time"
)
const (
colorBold = iota + 1
colorFaint
)
const (
colorBlack = iota + 30
colorRed
@@ -22,9 +27,6 @@ const (
colorMagenta
colorCyan
colorWhite
colorBold = 1
colorDarkGray = 90
)
var (
@@ -295,24 +297,9 @@ func consoleDefaultFormatTimestamp(timeFormat string, noColor bool) Formatter {
t = ts.Format(timeFormat)
}
case json.Number:
i, err := tt.Int64()
if err != nil {
t = tt.String()
} else {
var sec, nsec int64 = i, 0
switch TimeFieldFormat {
case TimeFormatUnixMs:
nsec = int64(time.Duration(i) * time.Millisecond)
sec = 0
case TimeFormatUnixMicro:
nsec = int64(time.Duration(i) * time.Microsecond)
sec = 0
}
ts := time.Unix(sec, nsec).UTC()
t = ts.Format(timeFormat)
}
t = tt.String()
}
return colorize(t, colorDarkGray, noColor)
return colorize(t, colorFaint, noColor)
}
}
@@ -321,8 +308,6 @@ func consoleDefaultFormatLevel(noColor bool) Formatter {
var l string
if ll, ok := i.(string); ok {
switch ll {
case "trace":
l = colorize("TRC", colorMagenta, noColor)
case "debug":
l = colorize("DBG", colorYellow, noColor)
case "info":
@@ -339,11 +324,7 @@ func consoleDefaultFormatLevel(noColor bool) Formatter {
l = colorize("???", colorBold, noColor)
}
} else {
if i == nil {
l = colorize("???", colorBold, noColor)
} else {
l = strings.ToUpper(fmt.Sprintf("%s", i))[0:3]
}
l = strings.ToUpper(fmt.Sprintf("%s", i))[0:3]
}
return l
}
@@ -361,22 +342,19 @@ func consoleDefaultFormatCaller(noColor bool) Formatter {
c = strings.TrimPrefix(c, cwd)
c = strings.TrimPrefix(c, "/")
}
c = colorize(c, colorBold, noColor) + colorize(" >", colorCyan, noColor)
c = colorize(c, colorBold, noColor) + colorize(" >", colorFaint, 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), colorCyan, noColor)
return colorize(fmt.Sprintf("%s=", i), colorFaint, noColor)
}
}
+10 -124
View File
@@ -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[90m<nil>\x1b[0m \x1b[31mWRN\x1b[0m Foobar\n"
expectedOutput := "\x1b[2m<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,126 +121,12 @@ 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, TimeFormat: time.StampMilli, NoColor: true}
_, err := w.Write([]byte(`{"time": 1234, "level": "debug", "message": "Foobar", "foo": "bar"}`))
if err != nil {
t.Errorf("Unexpected error when writing output: %s", err)
}
expectedOutput := "Jan 1 00:20:34.000 DBG Foobar foo=bar\n"
actualOutput := buf.String()
if actualOutput != expectedOutput {
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
}
})
t.Run("Unix timestamp ms input format", func(t *testing.T) {
of := zerolog.TimeFieldFormat
defer func() {
zerolog.TimeFieldFormat = of
}()
zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs
buf := &bytes.Buffer{}
w := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.StampMilli, NoColor: true}
_, err := w.Write([]byte(`{"time": 1234567, "level": "debug", "message": "Foobar", "foo": "bar"}`))
if err != nil {
t.Errorf("Unexpected error when writing output: %s", err)
}
expectedOutput := "Jan 1 00:20:34.567 DBG Foobar foo=bar\n"
actualOutput := buf.String()
if actualOutput != expectedOutput {
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
}
})
t.Run("Unix timestamp us input format", func(t *testing.T) {
of := zerolog.TimeFieldFormat
defer func() {
zerolog.TimeFieldFormat = of
}()
zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMicro
buf := &bytes.Buffer{}
w := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.StampMicro, NoColor: true}
_, err := w.Write([]byte(`{"time": 1234567891, "level": "debug", "message": "Foobar", "foo": "bar"}`))
if err != nil {
t.Errorf("Unexpected error when writing output: %s", err)
}
expectedOutput := "Jan 1 00:20:34.567891 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))
@@ -265,7 +151,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))
@@ -284,7 +170,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))
@@ -306,7 +192,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 {
@@ -324,7 +210,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)
@@ -342,7 +228,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}
+10 -42
View File
@@ -2,7 +2,6 @@ package zerolog
import (
"io/ioutil"
"math"
"net"
"time"
)
@@ -104,17 +103,14 @@ func (c Context) RawJSON(key string, b []byte) Context {
// AnErr adds the field key with serialized err to the logger context.
func (c Context) AnErr(key string, err error) Context {
switch m := ErrorMarshalFunc(err).(type) {
marshaled := ErrorMarshalFunc(err)
switch m := marshaled.(type) {
case nil:
return c
case LogObjectMarshaler:
return c.Object(key, m)
case error:
if m == nil || isNilValue(m) {
return c
} else {
return c.Str(key, m.Error())
}
return c.Str(key, m.Error())
case string:
return c.Str(key, m)
default:
@@ -127,15 +123,12 @@ func (c Context) AnErr(key string, err error) Context {
func (c Context) Errs(key string, errs []error) Context {
arr := Arr()
for _, err := range errs {
switch m := ErrorMarshalFunc(err).(type) {
marshaled := ErrorMarshalFunc(err)
switch m := marshaled.(type) {
case LogObjectMarshaler:
arr = arr.Object(m)
case error:
if m == nil || isNilValue(m) {
arr = arr.Interface(nil)
} else {
arr = arr.Str(m.Error())
}
arr = arr.Str(m.Error())
case string:
arr = arr.Str(m)
default:
@@ -354,31 +347,14 @@ func (c Context) Interface(key string, i interface{}) Context {
return c
}
type callerHook struct {
callerSkipFrameCount int
}
func newCallerHook(skipFrameCount int) callerHook {
return callerHook{callerSkipFrameCount: skipFrameCount}
}
type callerHook struct{}
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)
}
// Three extra frames to skip (added by hook infra).
e.caller(CallerSkipFrameCount + 3)
}
// 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)
var ch = callerHook{}
// Caller adds the file:line of the caller with the zerolog.CallerFieldName key.
func (c Context) Caller() Context {
@@ -386,14 +362,6 @@ 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) {
-1
View File
@@ -7,7 +7,6 @@ import (
var disabledLogger *Logger
func init() {
SetGlobalLevel(TraceLevel)
l := Nop()
disabledLogger = &l
}
+1 -1
View File
@@ -9,9 +9,9 @@ import (
// ManyToOne diode is optimal for many writers (go-routines B-n) and a single
// reader (go-routine A). It is not thread safe for multiple readers.
type ManyToOne struct {
buffer []unsafe.Pointer
writeIndex uint64
readIndex uint64
buffer []unsafe.Pointer
alerter Alerter
}
+1 -1
View File
@@ -31,9 +31,9 @@ type bucket struct {
// OneToOne diode is meant to be used by a single reader and a single writer.
// It is not thread safe if used otherwise.
type OneToOne struct {
buffer []unsafe.Pointer
writeIndex uint64
readIndex uint64
buffer []unsafe.Pointer
alerter Alerter
}
+18 -49
View File
@@ -5,6 +5,7 @@ import (
"net"
"os"
"runtime"
"strconv"
"sync"
"time"
)
@@ -61,7 +62,6 @@ func newEvent(w LevelWriter, level Level) *Event {
e.buf = enc.AppendBeginMarker(e.buf)
e.w = w
e.level = level
e.stack = false
return e
}
@@ -106,20 +106,10 @@ func (e *Event) Msg(msg string) {
e.msg(msg)
}
// Send is equivalent to calling Msg("").
// Msgf sends the event with formated msg added as the message field if not empty.
//
// NOTICE: once this method is called, the *Event should be disposed.
func (e *Event) Send() {
if e == nil {
return
}
e.msg("")
}
// Msgf sends the event with formatted msg added as the message field if not empty.
//
// NOTICE: once this method is called, the *Event should be disposed.
// Calling Msgf twice can have unexpected result.
// NOTICE: once this methid is called, the *Event should be disposed.
// Calling Msg twice can have unexpected result.
func (e *Event) Msgf(format string, v ...interface{}) {
if e == nil {
return
@@ -128,8 +118,13 @@ func (e *Event) Msgf(format string, v ...interface{}) {
}
func (e *Event) msg(msg string) {
for _, hook := range e.ch {
hook.Run(e, e.level, msg)
if len(e.ch) > 0 {
e.ch[0].Run(e, e.level, msg)
if len(e.ch) > 1 {
for _, hook := range e.ch[1:] {
hook.Run(e, e.level, msg)
}
}
}
if msg != "" {
e.buf = enc.AppendString(enc.AppendKey(e.buf, MessageFieldName), msg)
@@ -209,7 +204,7 @@ func (e *Event) Object(key string, obj LogObjectMarshaler) *Event {
return e
}
// EmbedObject marshals an object that implement the LogObjectMarshaler interface.
// Object marshals an object that implement the LogObjectMarshaler interface.
func (e *Event) EmbedObject(obj LogObjectMarshaler) *Event {
if e == nil {
return e
@@ -236,21 +231,6 @@ func (e *Event) Strs(key string, vals []string) *Event {
return e
}
// Stringer adds the field key with val.String() (or null if val is nil) to the *Event context.
func (e *Event) Stringer(key string, val fmt.Stringer) *Event {
if e == nil {
return e
}
if val != nil {
e.buf = enc.AppendString(enc.AppendKey(e.buf, key), val.String())
return e
}
e.buf = enc.AppendInterface(enc.AppendKey(e.buf, key), nil)
return e
}
// 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
@@ -296,11 +276,7 @@ func (e *Event) AnErr(key string, err error) *Event {
case LogObjectMarshaler:
return e.Object(key, m)
case error:
if m == nil || isNilValue(m) {
return e
} else {
return e.Str(key, m.Error())
}
return e.Str(key, m.Error())
case string:
return e.Str(key, m)
default:
@@ -333,6 +309,7 @@ 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.
//
@@ -349,9 +326,7 @@ func (e *Event) Err(err error) *Event {
case LogObjectMarshaler:
e.Object(ErrorStackFieldName, m)
case error:
if m != nil && !isNilValue(m) {
e.Str(ErrorStackFieldName, m.Error())
}
e.Str(ErrorStackFieldName, m.Error())
case string:
e.Str(ErrorStackFieldName, m)
default:
@@ -686,14 +661,8 @@ func (e *Event) Interface(key string, i interface{}) *Event {
}
// Caller adds the file:line of the caller with the zerolog.CallerFieldName key.
// The argument skip is the number of stack frames to ascend
// Skip If not passed, use the global variable CallerSkipFrameCount
func (e *Event) Caller(skip ...int) *Event {
sk := CallerSkipFrameCount
if len(skip) > 0 {
sk = skip[0] + CallerSkipFrameCount
}
return e.caller(sk)
func (e *Event) Caller() *Event {
return e.caller(CallerSkipFrameCount)
}
func (e *Event) caller(skip int) *Event {
@@ -704,7 +673,7 @@ func (e *Event) caller(skip int) *Event {
if !ok {
return e
}
e.buf = enc.AppendString(enc.AppendKey(e.buf, CallerFieldName), CallerMarshalFunc(file, line))
e.buf = enc.AppendString(enc.AppendKey(e.buf, CallerFieldName), file+":"+strconv.Itoa(line))
return e
}
-39
View File
@@ -1,39 +0,0 @@
// +build !binary_log
package zerolog
import (
"bytes"
"errors"
"strings"
"testing"
)
type nilError struct{}
func (nilError) Error() string {
return ""
}
func TestEvent_AnErr(t *testing.T) {
tests := []struct {
name string
err error
want string
}{
{"nil", nil, `{}`},
{"error", errors.New("test"), `{"err":"test"}`},
{"nil interface", func() *nilError { return nil }(), `{}`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
e := newEvent(levelWriterAdapter{&buf}, DebugLevel)
e.AnErr("err", tt.err)
_ = e.write()
if got, want := strings.TrimSpace(buf.String()), tt.want; got != want {
t.Errorf("Event.AnErr() = %v, want %v", got, want)
}
})
}
}
+6 -17
View File
@@ -4,13 +4,8 @@ import (
"net"
"sort"
"time"
"unsafe"
)
func isNilValue(i interface{}) bool {
return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0
}
func appendFields(dst []byte, fields map[string]interface{}) []byte {
keys := make([]string, 0, len(fields))
for key := range fields {
@@ -34,7 +29,8 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte {
case []byte:
dst = enc.AppendBytes(dst, val)
case error:
switch m := ErrorMarshalFunc(val).(type) {
marshaled := ErrorMarshalFunc(val)
switch m := marshaled.(type) {
case LogObjectMarshaler:
e := newEvent(nil, 0)
e.buf = e.buf[:0]
@@ -42,11 +38,7 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte {
dst = append(dst, e.buf...)
putEvent(e)
case error:
if m == nil || isNilValue(m) {
dst = enc.AppendNil(dst)
} else {
dst = enc.AppendString(dst, m.Error())
}
dst = enc.AppendString(dst, m.Error())
case string:
dst = enc.AppendString(dst, m)
default:
@@ -55,7 +47,8 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte {
case []error:
dst = enc.AppendArrayStart(dst)
for i, err := range val {
switch m := ErrorMarshalFunc(err).(type) {
marshaled := ErrorMarshalFunc(err)
switch m := marshaled.(type) {
case LogObjectMarshaler:
e := newEvent(nil, 0)
e.buf = e.buf[:0]
@@ -63,11 +56,7 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte {
dst = append(dst, e.buf...)
putEvent(e)
case error:
if m == nil || isNilValue(m) {
dst = enc.AppendNil(dst)
} else {
dst = enc.AppendString(dst, m.Error())
}
dst = enc.AppendString(dst, m.Error())
case string:
dst = enc.AppendString(dst, m)
default:
+12 -39
View File
@@ -1,24 +1,7 @@
package zerolog
import (
"strconv"
"sync/atomic"
"time"
)
const (
// TimeFormatUnix defines a time format that makes time fields to be
// serialized as Unix timestamp integers.
TimeFormatUnix = ""
// TimeFormatUnixMs defines a time format that makes time fields to be
// serialized as Unix timestamp integers in milliseconds.
TimeFormatUnixMs = "UNIXMS"
// TimeFormatUnixMicro defines a time format that makes time fields to be
// serialized as Unix timestamp integers in microseconds.
TimeFormatUnixMicro = "UNIXMICRO"
)
import "time"
import "sync/atomic"
var (
// TimestampFieldName is the field name used for the timestamp field.
@@ -27,11 +10,6 @@ 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"
@@ -44,11 +22,6 @@ var (
// CallerSkipFrameCount is the number of stack frames to skip to find the caller.
CallerSkipFrameCount = 2
// 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"
@@ -60,9 +33,9 @@ var (
return err
}
// TimeFieldFormat defines the time format of the Time field type. If set to
// TimeFormatUnix, TimeFormatUnixMs or TimeFormatUnixMicro, the time is formatted as an UNIX
// timestamp as integer.
// 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.
TimeFieldFormat = time.RFC3339
// TimestampFunc defines the function called to generate a timestamp.
@@ -83,8 +56,8 @@ var (
)
var (
gLevel = new(int32)
disableSampling = new(int32)
gLevel = new(uint32)
disableSampling = new(uint32)
)
// SetGlobalLevel sets the global override for log level. If this
@@ -92,23 +65,23 @@ var (
//
// To globally disable logs, set GlobalLevel to Disabled.
func SetGlobalLevel(l Level) {
atomic.StoreInt32(gLevel, int32(l))
atomic.StoreUint32(gLevel, uint32(l))
}
// GlobalLevel returns the current global log level
func GlobalLevel() Level {
return Level(atomic.LoadInt32(gLevel))
return Level(atomic.LoadUint32(gLevel))
}
// DisableSampling will disable sampling in all Loggers if true.
func DisableSampling(v bool) {
var i int32
var i uint32
if v {
i = 1
}
atomic.StoreInt32(disableSampling, i)
atomic.StoreUint32(disableSampling, i)
}
func samplingDisabled() bool {
return atomic.LoadInt32(disableSampling) == 1
return atomic.LoadUint32(disableSampling) == 1
}
-7
View File
@@ -1,8 +1 @@
module github.com/rs/zerolog
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
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74
)
-14
View File
@@ -1,14 +0,0 @@
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 h1:4cFkmztxtMslUX2SctSl+blCyXfpzhGOy9LhKAqSMA4=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-7
View File
@@ -1,7 +0,0 @@
// +build go1.12
package zerolog
// Since go 1.12, some auto generated init functions are hidden from
// runtime.Caller.
const contextCallerSkipFrameCount = 2
+1 -17
View File
@@ -9,8 +9,8 @@ import (
"github.com/rs/xid"
"github.com/rs/zerolog"
"github.com/rs/zerolog/hlog/internal/mutil"
"github.com/rs/zerolog/log"
"github.com/zenazn/goji/web/mutil"
)
// FromRequest gets the logger in the request's context.
@@ -171,22 +171,6 @@ func RequestIDHandler(fieldKey, headerName string) func(next http.Handler) http.
}
}
// CustomHeaderHandler adds given header from request's header as a field to
// the context's logger using fieldKey as field key.
func CustomHeaderHandler(fieldKey, header string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if val := r.Header.Get(header); val != "" {
log := zerolog.Ctx(r.Context())
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
return c.Str(fieldKey, val)
})
}
next.ServeHTTP(w, r)
})
}
}
// AccessHandler returns a handler that call f after each request.
func AccessHandler(f func(r *http.Request, status, size int, duration time.Duration)) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
-18
View File
@@ -182,24 +182,6 @@ func TestRequestIDHandler(t *testing.T) {
h.ServeHTTP(httptest.NewRecorder(), r)
}
func TestCustomHeaderHandler(t *testing.T) {
out := &bytes.Buffer{}
r := &http.Request{
Header: http.Header{
"X-Request-Id": []string{"514bbe5bb5251c92bd07a9846f4a1ab6"},
},
}
h := CustomHeaderHandler("reqID", "X-Request-Id")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
l := FromRequest(r)
l.Log().Msg("")
}))
h = NewHandler(zerolog.New(out))(h)
h.ServeHTTP(nil, r)
if want, got := `{"reqID":"514bbe5bb5251c92bd07a9846f4a1ab6"}`+"\n", decodeIfBinary(out); want != got {
t.Errorf("Invalid log output, got: %s, want: %s", got, want)
}
}
func TestCombinedHandlers(t *testing.T) {
out := &bytes.Buffer{}
r := &http.Request{
-20
View File
@@ -1,20 +0,0 @@
Copyright (c) 2014, 2015, 2016 Carl Jackson (carl@avtok.com)
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-6
View File
@@ -1,6 +0,0 @@
// Package mutil contains various functions that are helpful when writing http
// middleware.
//
// It has been vendored from Goji v1.0, with the exception of the code for Go 1.8:
// https://github.com/zenazn/goji/
package mutil
-149
View File
@@ -1,149 +0,0 @@
package mutil
import (
"bufio"
"io"
"net"
"net/http"
)
// WriterProxy is a proxy around an http.ResponseWriter that allows you to hook
// into various parts of the response process.
type WriterProxy interface {
http.ResponseWriter
// Status returns the HTTP status of the request, or 0 if one has not
// yet been sent.
Status() int
// BytesWritten returns the total number of bytes sent to the client.
BytesWritten() int
// Tee causes the response body to be written to the given io.Writer in
// addition to proxying the writes through. Only one io.Writer can be
// tee'd to at once: setting a second one will overwrite the first.
// Writes will be sent to the proxy before being written to this
// io.Writer. It is illegal for the tee'd writer to be modified
// concurrently with writes.
Tee(io.Writer)
// Unwrap returns the original proxied target.
Unwrap() http.ResponseWriter
}
// WrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to
// hook into various parts of the response process.
func WrapWriter(w http.ResponseWriter) WriterProxy {
_, cn := w.(http.CloseNotifier)
_, fl := w.(http.Flusher)
_, hj := w.(http.Hijacker)
_, rf := w.(io.ReaderFrom)
bw := basicWriter{ResponseWriter: w}
if cn && fl && hj && rf {
return &fancyWriter{bw}
}
if fl {
return &flushWriter{bw}
}
return &bw
}
// basicWriter wraps a http.ResponseWriter that implements the minimal
// http.ResponseWriter interface.
type basicWriter struct {
http.ResponseWriter
wroteHeader bool
code int
bytes int
tee io.Writer
}
func (b *basicWriter) WriteHeader(code int) {
if !b.wroteHeader {
b.code = code
b.wroteHeader = true
b.ResponseWriter.WriteHeader(code)
}
}
func (b *basicWriter) Write(buf []byte) (int, error) {
b.WriteHeader(http.StatusOK)
n, err := b.ResponseWriter.Write(buf)
if b.tee != nil {
_, err2 := b.tee.Write(buf[:n])
// Prefer errors generated by the proxied writer.
if err == nil {
err = err2
}
}
b.bytes += n
return n, err
}
func (b *basicWriter) maybeWriteHeader() {
if !b.wroteHeader {
b.WriteHeader(http.StatusOK)
}
}
func (b *basicWriter) Status() int {
return b.code
}
func (b *basicWriter) BytesWritten() int {
return b.bytes
}
func (b *basicWriter) Tee(w io.Writer) {
b.tee = w
}
func (b *basicWriter) Unwrap() http.ResponseWriter {
return b.ResponseWriter
}
// fancyWriter is a writer that additionally satisfies http.CloseNotifier,
// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case
// of wrapping the http.ResponseWriter that package http gives you, in order to
// make the proxied object support the full method set of the proxied object.
type fancyWriter struct {
basicWriter
}
func (f *fancyWriter) CloseNotify() <-chan bool {
cn := f.basicWriter.ResponseWriter.(http.CloseNotifier)
return cn.CloseNotify()
}
func (f *fancyWriter) Flush() {
fl := f.basicWriter.ResponseWriter.(http.Flusher)
fl.Flush()
}
func (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hj := f.basicWriter.ResponseWriter.(http.Hijacker)
return hj.Hijack()
}
func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {
if f.basicWriter.tee != nil {
return io.Copy(&f.basicWriter, r)
}
rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
f.basicWriter.maybeWriteHeader()
return rf.ReadFrom(r)
}
type flushWriter struct {
basicWriter
}
func (f *flushWriter) Flush() {
fl := f.basicWriter.ResponseWriter.(http.Flusher)
fl.Flush()
}
var (
_ http.CloseNotifier = &fancyWriter{}
_ http.Flusher = &fancyWriter{}
_ http.Hijacker = &fancyWriter{}
_ io.ReaderFrom = &fancyWriter{}
_ http.Flusher = &flushWriter{}
)
+1 -5
View File
@@ -17,16 +17,12 @@ func (h HookFunc) Run(e *Event, level Level, message string) {
// LevelHook applies a different hook for each level.
type LevelHook struct {
NoLevelHook, TraceHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook
NoLevelHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook
}
// Run implements the Hook interface.
func (h LevelHook) Run(e *Event, level Level, message string) {
switch level {
case TraceLevel:
if h.TraceHook != nil {
h.TraceHook.Run(e, level, message)
}
case DebugLevel:
if h.DebugHook != nil {
h.DebugHook.Run(e, level, message)
+3 -3
View File
@@ -24,9 +24,9 @@ func (Encoder) AppendEndMarker(dst []byte) []byte {
// AppendObjectData takes an object in form of a byte array and appends to dst.
func (Encoder) AppendObjectData(dst []byte, o []byte) []byte {
// BeginMarker is present in the dst, which
// should not be copied when appending to existing data.
return append(dst, o[1:]...)
// BeginMarker is present in the dst, which
// should not be copied when appending to existing data.
return append(dst, o[1:]...)
}
// AppendArrayStart adds markers to indicate the start of an array.
+8 -3
View File
@@ -88,8 +88,11 @@ var integerTestCases = []struct {
{0xFFFF, "\x19\xff\xff"},
// Value in 4 bytes.
{0x10000, "\x1a\x00\x01\x00\x00"},
{0x7FFFFFFE, "\x1a\x7f\xff\xff\xfe"},
{0xFFFFFFFE, "\x1a\xff\xff\xff\xfe"},
{1000000, "\x1a\x00\x0f\x42\x40"},
// Value in 8 bytes.
{0xabcd100000000, "\x1b\x00\x0a\xbc\xd1\x00\x00\x00\x00"},
{1000000000000, "\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00"},
// Negative number test cases.
// Value included in the type.
{-1, "\x20"},
@@ -113,9 +116,11 @@ var integerTestCases = []struct {
{-1000, "\x39\x03\xe7"},
// Value in 4 bytes.
{-0x10001, "\x3a\x00\x01\x00\x00"},
{-0x7FFFFFFE, "\x3a\x7f\xff\xff\xfd"},
{-0xFFFFFFFE, "\x3a\xff\xff\xff\xfd"},
{-1000000, "\x3a\x00\x0f\x42\x3f"},
// Value in 8 bytes.
{-0xabcd100000001, "\x3b\x00\x0a\xbc\xd1\x00\x00\x00\x00"},
{-1000000000001, "\x3b\x00\x00\x00\xe8\xd4\xa5\x10\x00"},
}
func TestAppendInt(t *testing.T) {
-35
View File
@@ -1,35 +0,0 @@
// +build !386
package cbor
import (
"encoding/hex"
"testing"
)
var enc2 = Encoder{}
var integerTestCases_64bit = []struct {
val int
binary string
}{
// Value in 8 bytes.
{0xabcd100000000, "\x1b\x00\x0a\xbc\xd1\x00\x00\x00\x00"},
{1000000000000, "\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00"},
// Value in 8 bytes.
{-0xabcd100000001, "\x3b\x00\x0a\xbc\xd1\x00\x00\x00\x00"},
{-1000000000001, "\x3b\x00\x00\x00\xe8\xd4\xa5\x10\x00"},
}
func TestAppendInt_64bit(t *testing.T) {
for _, tc := range integerTestCases_64bit {
s := enc2.AppendInt([]byte{}, tc.val)
got := string(s)
if got != tc.binary {
t.Errorf("AppendInt(0x%x)=0x%s, want: 0x%s",
tc.val, hex.EncodeToString(s),
hex.EncodeToString([]byte(tc.binary)))
}
}
}
+4 -3
View File
@@ -4,8 +4,9 @@ type Encoder struct{}
// AppendKey appends a new key to the output JSON.
func (e Encoder) AppendKey(dst []byte, key string) []byte {
if dst[len(dst)-1] != '{' {
if len(dst) > 1 && dst[len(dst)-1] != '{' {
dst = append(dst, ',')
}
return append(e.AppendString(dst, key), ':')
}
dst = e.AppendString(dst, key)
return append(dst, ':')
}
+2 -32
View File
@@ -5,23 +5,11 @@ import (
"time"
)
const (
// Import from zerolog/global.go
timeFormatUnix = ""
timeFormatUnixMs = "UNIXMS"
timeFormatUnixMicro = "UNIXMICRO"
)
// 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 {
switch format {
case timeFormatUnix:
if format == "" {
return e.AppendInt64(dst, t.Unix())
case timeFormatUnixMs:
return e.AppendInt64(dst, t.UnixNano()/1000000)
case timeFormatUnixMicro:
return e.AppendInt64(dst, t.UnixNano()/1000)
}
return append(t.AppendFormat(append(dst, '"'), format), '"')
}
@@ -29,11 +17,8 @@ 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 {
switch format {
case timeFormatUnix:
if format == "" {
return appendUnixTimes(dst, vals)
case timeFormatUnixMs:
return appendUnixMsTimes(dst, vals)
}
if len(vals) == 0 {
return append(dst, '[', ']')
@@ -64,21 +49,6 @@ 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 {
+5 -9
View File
@@ -373,16 +373,12 @@ func (e Encoder) AppendInterface(dst []byte, i interface{}) []byte {
// AppendObjectData takes in an object that is already in a byte array
// and adds it to the dst.
func (Encoder) AppendObjectData(dst []byte, o []byte) []byte {
// Three conditions apply here:
// 1. new content starts with '{' - which should be dropped OR
// 2. new content starts with '{' - which should be replaced with ','
// to separate with existing content OR
// 3. existing content has already other fields
// Two conditions we want to put a ',' between existing content and
// new content:
// 1. new content starts with '{' - which shd be dropped OR
// 2. existing content has already other fields
if o[0] == '{' {
if len(dst) > 1 {
dst = append(dst, ',')
}
o = o[1:]
o[0] = ','
} else if len(dst) > 1 {
dst = append(dst, ',')
}
-20
View File
@@ -165,23 +165,3 @@ func Test_appendMac(t *testing.T) {
})
}
}
func Test_appendObjectData(t *testing.T) {
tests := []struct {
dst []byte
obj []byte
want []byte
}{
{[]byte{}, []byte(`{"foo":"bar"}`), []byte(`"foo":"bar"}`)},
{[]byte(`{"qux":"quz"`), []byte(`{"foo":"bar"}`), []byte(`{"qux":"quz","foo":"bar"}`)},
{[]byte{}, []byte(`"foo":"bar"`), []byte(`"foo":"bar"`)},
{[]byte(`{"qux":"quz"`), []byte(`"foo":"bar"`), []byte(`{"qux":"quz","foo":"bar"`)},
}
for _, tt := range tests {
t.Run("ObjectData", func(t *testing.T) {
if got := enc.AppendObjectData(tt.dst, tt.obj); !reflect.DeepEqual(got, tt.want) {
t.Errorf("appendObjectData() = %s, want %s", got, tt.want)
}
})
}
}
+3 -13
View File
@@ -20,12 +20,11 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"strings"
"github.com/coreos/go-systemd/journal"
"github.com/rs/zerolog"
"github.com/rs/zerolog/internal/cbor"
"io"
"strings"
)
const defaultJournalDPrio = journal.PriNotice
@@ -48,8 +47,6 @@ func levelToJPrio(zLevel string) journal.Priority {
lvl, _ := zerolog.ParseLevel(zLevel)
switch lvl {
case zerolog.TraceLevel:
return journal.PriDebug
case zerolog.DebugLevel:
return journal.PriDebug
case zerolog.InfoLevel:
@@ -70,12 +67,10 @@ func levelToJPrio(zLevel string) journal.Priority {
func (w journalWriter) Write(p []byte) (n int, err error) {
if !journal.Enabled() {
err = fmt.Errorf("cannot connect to journalD")
err = fmt.Errorf("Cannot connect to journalD!!")
return
}
var event map[string]interface{}
origPLen := len(p)
p = cbor.DecodeIfBinaryToBytes(p)
d := json.NewDecoder(bytes.NewReader(p))
d.UseNumber()
@@ -116,10 +111,5 @@ func (w journalWriter) Write(p []byte) (n int, err error) {
}
args["JSON"] = string(p)
err = journal.Send(msg, jPrio, args)
if err == nil {
n = origPLen
}
return
}
+3 -45
View File
@@ -1,15 +1,9 @@
// +build linux
// +build !windows
package journald_test
import (
"bytes"
"io"
"testing"
"github.com/rs/zerolog"
"github.com/rs/zerolog/journald"
)
import "github.com/rs/zerolog"
import "github.com/rs/zerolog/journald"
func ExampleNewJournalDWriter() {
log := zerolog.New(journald.NewJournalDWriter())
@@ -48,39 +42,3 @@ Thu 2018-04-26 22:30:20.768136 PDT [s=3284d695bde946e4b5017c77a399237f;i=329f0;b
_PID=27103
_SOURCE_REALTIME_TIMESTAMP=1524807020768136
*/
func TestWriteReturnsNoOfWrittenBytes(t *testing.T) {
input := []byte(`{"level":"info","time":1570912626,"message":"Starting..."}`)
wr := journald.NewJournalDWriter()
want := len(input)
got, err := wr.Write(input)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
if want != got {
t.Errorf("Expected %d bytes to be written got %d", want, got)
}
}
func TestMultiWrite(t *testing.T) {
var (
w1 = new(bytes.Buffer)
w2 = new(bytes.Buffer)
w3 = journald.NewJournalDWriter()
)
zerolog.ErrorHandler = func(err error) {
if err == io.ErrShortWrite {
t.Errorf("Unexpected ShortWriteError")
t.FailNow()
}
}
log := zerolog.New(io.MultiWriter(w1, w2, w3)).With().Logger()
for i := 0; i < 10; i++ {
log.Info().Msg("Tick!")
}
}
+15 -52
View File
@@ -94,8 +94,8 @@
// Msg("dup")
// // Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
//
// In this case, many consumers will take the last value,
// but this is not guaranteed; check yours if in doubt.
// However, its not a big deal though as JSON accepts dup keys,
// the last one prevails.
package zerolog
import (
@@ -107,7 +107,7 @@ import (
)
// Level defines log levels.
type Level int8
type Level uint8
const (
// DebugLevel defines debug log level.
@@ -126,15 +126,10 @@ const (
NoLevel
// Disabled disables the logger.
Disabled
// TraceLevel defines trace log level.
TraceLevel Level = -1
)
func (l Level) String() string {
switch l {
case TraceLevel:
return "trace"
case DebugLevel:
return "debug"
case InfoLevel:
@@ -157,21 +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 LevelFieldMarshalFunc(TraceLevel):
return TraceLevel, nil
case LevelFieldMarshalFunc(DebugLevel):
case DebugLevel.String():
return DebugLevel, nil
case LevelFieldMarshalFunc(InfoLevel):
case InfoLevel.String():
return InfoLevel, nil
case LevelFieldMarshalFunc(WarnLevel):
case WarnLevel.String():
return WarnLevel, nil
case LevelFieldMarshalFunc(ErrorLevel):
case ErrorLevel.String():
return ErrorLevel, nil
case LevelFieldMarshalFunc(FatalLevel):
case FatalLevel.String():
return FatalLevel, nil
case LevelFieldMarshalFunc(PanicLevel):
case PanicLevel.String():
return PanicLevel, nil
case LevelFieldMarshalFunc(NoLevel):
case NoLevel.String():
return NoLevel, nil
}
return NoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
@@ -179,7 +172,7 @@ func ParseLevel(levelStr string) (Level, error) {
// A Logger represents an active logging object that generates lines
// of JSON output to an io.Writer. Each logging operation makes a single
// call to the Writer's Write method. There is no guarantee on access
// call to the Writer's Write method. There is no guaranty on access
// serialization to the Writer. If your Writer is not thread safe,
// you may consider a sync wrapper.
type Logger struct {
@@ -195,7 +188,7 @@ type Logger struct {
// one.
//
// Each logging operation makes a single call to the Writer's Write method. There is no
// guarantee on access serialization to the Writer. If your Writer is not thread safe,
// guaranty on access serialization to the Writer. If your Writer is not thread safe,
// you may consider using sync wrapper.
func New(w io.Writer) Logger {
if w == nil {
@@ -205,7 +198,7 @@ func New(w io.Writer) Logger {
if !ok {
lw = levelWriterAdapter{w}
}
return Logger{w: lw, level: TraceLevel}
return Logger{w: lw}
}
// Nop returns a disabled logger for which all operation are no-op.
@@ -234,10 +227,6 @@ func (l Logger) With() Context {
l.context = make([]byte, 0, 500)
if context != nil {
l.context = append(l.context, context...)
} else {
// This is needed for AppendKey to not check len of input
// thus making it inlinable
l.context = enc.AppendBeginMarker(l.context)
}
return Context{l}
}
@@ -262,11 +251,6 @@ func (l Logger) Level(lvl Level) Logger {
return l
}
// GetLevel returns the current Level of l.
func (l Logger) GetLevel() Level {
return l.level
}
// Sample returns a logger with the s sampler.
func (l Logger) Sample(s Sampler) Logger {
l.sampler = s
@@ -279,13 +263,6 @@ func (l Logger) Hook(h Hook) Logger {
return l
}
// Trace starts a new message with trace level.
//
// You must call Msg on the returned event in order to send the event.
func (l *Logger) Trace() *Event {
return l.newEvent(TraceLevel, nil)
}
// Debug starts a new message with debug level.
//
// You must call Msg on the returned event in order to send the event.
@@ -314,18 +291,6 @@ 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)
}
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.
//
@@ -349,8 +314,6 @@ func (l *Logger) Panic() *Event {
// You must call Msg on the returned event in order to send the event.
func (l *Logger) WithLevel(level Level) *Event {
switch level {
case TraceLevel:
return l.Trace()
case DebugLevel:
return l.Debug()
case InfoLevel:
@@ -417,9 +380,9 @@ func (l *Logger) newEvent(level Level, done func(string)) *Event {
e.done = done
e.ch = l.hooks
if level != NoLevel {
e.Str(LevelFieldName, LevelFieldMarshalFunc(level))
e.Str(LevelFieldName, level.String())
}
if l.context != nil && len(l.context) > 1 {
if l.context != nil && len(l.context) > 0 {
e.buf = enc.AppendObjectData(e.buf, l.context)
}
return e
-15
View File
@@ -37,21 +37,6 @@ func Hook(h zerolog.Hook) zerolog.Logger {
return Logger.Hook(h)
}
// 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 Err(err error) *zerolog.Event {
return Logger.Err(err)
}
// Trace starts a new message with trace level.
//
// You must call Msg on the returned event in order to send the event.
func Trace() *zerolog.Event {
return Logger.Trace()
}
// Debug starts a new message with debug level.
//
// You must call Msg on the returned event in order to send the event.
-19
View File
@@ -54,25 +54,6 @@ func ExampleLog() {
// Output: {"time":1199811905,"message":"hello world"}
}
// Example of a conditional level based on the presence of an error.
func ExampleErr() {
setup()
err := errors.New("some error")
log.Err(err).Msg("hello world")
log.Err(nil).Msg("hello world")
// Output: {"level":"error","error":"some error","time":1199811905,"message":"hello world"}
// {"level":"info","time":1199811905,"message":"hello world"}
}
// Example of a log at a particular "level" (in this case, "trace")
func ExampleTrace() {
setup()
log.Trace().Msg("hello world")
// Output: {"level":"trace","time":1199811905,"message":"hello world"}
}
// Example of a log at a particular "level" (in this case, "debug")
func ExampleDebug() {
setup()
-11
View File
@@ -95,17 +95,6 @@ func ExampleLogger_Printf() {
// Output: {"level":"debug","message":"hello world"}
}
func ExampleLogger_Trace() {
log := zerolog.New(os.Stdout)
log.Trace().
Str("foo", "bar").
Int("n", 123).
Msg("hello world")
// Output: {"level":"trace","foo":"bar","n":123,"message":"hello world"}
}
func ExampleLogger_Debug() {
log := zerolog.New(os.Stdout)
+9 -118
View File
@@ -7,8 +7,6 @@ import (
"net"
"reflect"
"runtime"
"strconv"
"strings"
"testing"
"time"
)
@@ -107,20 +105,6 @@ 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) {
@@ -235,8 +219,6 @@ func TestFields(t *testing.T) {
log.Log().
Caller().
Str("string", "foo").
Stringer("stringer", net.IP{127, 0, 0, 1}).
Stringer("stringer_nil", nil).
Bytes("bytes", []byte("bar")).
Hex("hex", []byte{0x12, 0xef}).
RawJSON("json", []byte(`{"some":"json"}`)).
@@ -263,7 +245,7 @@ func TestFields(t *testing.T) {
Time("time", time.Time{}).
TimeDiff("diff", now, now.Add(-10*time.Second)).
Msg("")
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","string":"foo","stringer":"127.0.0.1","stringer_nil":null,"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,"IPv4":"192.168.0.100","IPv6":"2001:db8:85a3::8a2e:370:7334","Mac":"00:14:22:01:23:45","Prefix":"192.168.0.100/24","float32":11.1234,"float64":12.321321321,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want {
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","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,"IPv4":"192.168.0.100","IPv6":"2001:db8:85a3::8a2e:370:7334","Mac":"00:14:22:01:23:45","Prefix":"192.168.0.100/24","float32":11.1234,"float64":12.321321321,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want {
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
}
}
@@ -455,24 +437,6 @@ func TestLevel(t *testing.T) {
})
}
func TestGetLevel(t *testing.T) {
levels := []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
NoLevel,
Disabled,
}
for _, level := range levels {
if got, want := New(nil).Level(level).GetLevel(), level; got != want {
t.Errorf("GetLevel() = %v, want: %v", got, want)
}
}
}
func TestSampling(t *testing.T) {
out := &bytes.Buffer{}
log := New(out).Sample(&BasicSampler{N: 2})
@@ -528,34 +492,30 @@ func TestLevelWriter(t *testing.T) {
}{},
}
log := New(lw)
log.Trace().Msg("0")
log.Debug().Msg("1")
log.Info().Msg("2")
log.Warn().Msg("3")
log.Error().Msg("4")
log.Log().Msg("nolevel-1")
log.WithLevel(TraceLevel).Msg("5")
log.WithLevel(DebugLevel).Msg("6")
log.WithLevel(InfoLevel).Msg("7")
log.WithLevel(WarnLevel).Msg("8")
log.WithLevel(ErrorLevel).Msg("9")
log.WithLevel(DebugLevel).Msg("5")
log.WithLevel(InfoLevel).Msg("6")
log.WithLevel(WarnLevel).Msg("7")
log.WithLevel(ErrorLevel).Msg("8")
log.WithLevel(NoLevel).Msg("nolevel-2")
want := []struct {
l Level
p string
}{
{TraceLevel, `{"level":"trace","message":"0"}` + "\n"},
{DebugLevel, `{"level":"debug","message":"1"}` + "\n"},
{InfoLevel, `{"level":"info","message":"2"}` + "\n"},
{WarnLevel, `{"level":"warn","message":"3"}` + "\n"},
{ErrorLevel, `{"level":"error","message":"4"}` + "\n"},
{NoLevel, `{"message":"nolevel-1"}` + "\n"},
{TraceLevel, `{"level":"trace","message":"5"}` + "\n"},
{DebugLevel, `{"level":"debug","message":"6"}` + "\n"},
{InfoLevel, `{"level":"info","message":"7"}` + "\n"},
{WarnLevel, `{"level":"warn","message":"8"}` + "\n"},
{ErrorLevel, `{"level":"error","message":"9"}` + "\n"},
{DebugLevel, `{"level":"debug","message":"5"}` + "\n"},
{InfoLevel, `{"level":"info","message":"6"}` + "\n"},
{WarnLevel, `{"level":"warn","message":"7"}` + "\n"},
{ErrorLevel, `{"level":"error","message":"8"}` + "\n"},
{NoLevel, `{"message":"nolevel-2"}` + "\n"},
}
if got := lw.ops; !reflect.DeepEqual(got, want) {
@@ -681,75 +641,6 @@ 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)
}
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
}
-5
View File
@@ -1,5 +0,0 @@
// +build !go1.12
package zerolog
const contextCallerSkipFrameCount = 3
+2 -10
View File
@@ -46,12 +46,8 @@ type BasicSampler struct {
// Sample implements the Sampler interface.
func (s *BasicSampler) Sample(lvl Level) bool {
n := s.N
if n == 1 {
return true
}
c := atomic.AddUint32(&s.counter, 1)
return c%n == 1
return c%s.N == s.N-1
}
// BurstSampler lets Burst events pass per Period then pass the decision to
@@ -104,15 +100,11 @@ func (s *BurstSampler) inc() uint32 {
// LevelSampler applies a different sampler for each level.
type LevelSampler struct {
TraceSampler, DebugSampler, InfoSampler, WarnSampler, ErrorSampler Sampler
DebugSampler, InfoSampler, WarnSampler, ErrorSampler Sampler
}
func (s LevelSampler) Sample(lvl Level) bool {
switch lvl {
case TraceLevel:
if s.TraceSampler != nil {
return s.TraceSampler.Sample(lvl)
}
case DebugLevel:
if s.DebugSampler != nil {
return s.DebugSampler.Sample(lvl)
-1
View File
@@ -35,7 +35,6 @@ func (sw syslogWriter) Write(p []byte) (n int, err error) {
// WriteLevel implements LevelWriter interface.
func (sw syslogWriter) WriteLevel(level Level, p []byte) (n int, err error) {
switch level {
case TraceLevel:
case DebugLevel:
err = sw.w.Debug(string(p))
case InfoLevel:
+2 -9
View File
@@ -3,10 +3,8 @@
package zerolog
import (
"reflect"
"testing"
)
import "testing"
import "reflect"
type syslogEvent struct {
level string
@@ -19,10 +17,6 @@ type syslogTestWriter struct {
func (w *syslogTestWriter) Write(p []byte) (int, error) {
return 0, nil
}
func (w *syslogTestWriter) Trace(m string) error {
w.events = append(w.events, syslogEvent{"Trace", m})
return nil
}
func (w *syslogTestWriter) Debug(m string) error {
w.events = append(w.events, syslogEvent{"Debug", m})
return nil
@@ -51,7 +45,6 @@ func (w *syslogTestWriter) Crit(m string) error {
func TestSyslogWriter(t *testing.T) {
sw := &syslogTestWriter{}
log := New(SyslogLevelWriter(sw))
log.Trace().Msg("trace")
log.Debug().Msg("debug")
log.Info().Msg("info")
log.Warn().Msg("warn")
+5 -3
View File
@@ -26,9 +26,11 @@ type syncWriter struct {
}
// SyncWriter wraps w so that each call to Write is synchronized with a mutex.
// This syncer can be used to wrap the call to writer's Write method if it is
// not thread safe. Note that you do not need this wrapper for os.File Write
// operations on POSIX and Windows systems as they are already thread-safe.
// This syncer can be the call to writer's Write method is not thread safe.
// Note that os.File Write operation is using write() syscall which is supposed
// to be thread-safe on POSIX systems. So there is no need to use this with
// os.File on such systems as zerolog guaranties to issue a single Write call
// per log event.
func SyncWriter(w io.Writer) io.Writer {
if lw, ok := w.(LevelWriter); ok {
return &syncWriter{lw: lw}