mirror of
https://github.com/rs/zerolog
synced 2026-06-08 17:13:30 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6973188a74 | |||
| 78448ee023 | |||
| 197adb44cc | |||
| 07d347e658 | |||
| 6f8a5f9ccb | |||
| 9b9fc5c6b7 | |||
| 65adfd88ec | |||
| cdd74175d0 | |||
| fe931004ff | |||
| be6f6fd8f1 | |||
| d92a906fca | |||
| fad20d83d3 | |||
| 164ec91b0c | |||
| c1533bd5f8 | |||
| 0872592ea2 | |||
| da1cb97713 |
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
@@ -14,6 +14,12 @@ jobs:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Test
|
||||
run: go test -race -bench . -benchmem ./...
|
||||
- name: Test CBOR
|
||||
|
||||
@@ -617,6 +617,7 @@ with zerolog library is [CSD](https://github.com/toravir/csd/).
|
||||
|
||||
* [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog`
|
||||
* [overlog](https://github.com/Trendyol/overlog): Implementation of `Mapped Diagnostic Context` interface using `zerolog`
|
||||
* [zerologr](https://github.com/go-logr/zerologr): Implementation of `logr.LogSink` interface using `zerolog`
|
||||
|
||||
## Benchmarks
|
||||
|
||||
|
||||
@@ -231,3 +231,10 @@ func (a *Array) MACAddr(ha net.HardwareAddr) *Array {
|
||||
a.buf = enc.AppendMACAddr(enc.AppendArrayDelim(a.buf), ha)
|
||||
return a
|
||||
}
|
||||
|
||||
// Dict adds the dict Event to the array
|
||||
func (a *Array) Dict(dict *Event) *Array {
|
||||
dict.buf = enc.AppendEndMarker(dict.buf)
|
||||
a.buf = append(enc.AppendArrayDelim(a.buf), dict.buf...)
|
||||
return a
|
||||
}
|
||||
|
||||
+6
-2
@@ -27,8 +27,12 @@ func TestArray(t *testing.T) {
|
||||
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]`
|
||||
Dur(0).
|
||||
Dict(Dict().
|
||||
Str("bar", "baz").
|
||||
Int("n", 1),
|
||||
)
|
||||
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,{"bar":"baz","n":1}]`
|
||||
if got := decodeObjectToStr(a.write([]byte{})); got != want {
|
||||
t.Errorf("Array.write()\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
+20
-20
@@ -148,16 +148,16 @@ func BenchmarkLogFieldType(b *testing.B) {
|
||||
{"a", "a", 0},
|
||||
}
|
||||
objects := []obj{
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
}
|
||||
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
|
||||
types := map[string]func(e *Event) *Event{
|
||||
@@ -272,16 +272,16 @@ func BenchmarkContextFieldType(b *testing.B) {
|
||||
{"a", "a", 0},
|
||||
}
|
||||
objects := []obj{
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
obj{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
{"a", "a", 0},
|
||||
}
|
||||
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
|
||||
types := map[string]func(c Context) Context{
|
||||
|
||||
@@ -364,6 +364,42 @@ func ExampleEvent_Durs() {
|
||||
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleEvent_Fields_map() {
|
||||
fields := map[string]interface{}{
|
||||
"bar": "baz",
|
||||
"n": 1,
|
||||
}
|
||||
|
||||
dst := bytes.Buffer{}
|
||||
log := New(&dst)
|
||||
|
||||
log.Log().
|
||||
Str("foo", "bar").
|
||||
Fields(fields).
|
||||
Msg("hello world")
|
||||
|
||||
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleEvent_Fields_slice() {
|
||||
fields := []interface{}{
|
||||
"bar", "baz",
|
||||
"n", 1,
|
||||
}
|
||||
|
||||
dst := bytes.Buffer{}
|
||||
log := New(&dst)
|
||||
|
||||
log.Log().
|
||||
Str("foo", "bar").
|
||||
Fields(fields).
|
||||
Msg("hello world")
|
||||
|
||||
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_Dict() {
|
||||
dst := bytes.Buffer{}
|
||||
log := New(&dst).With().
|
||||
@@ -510,3 +546,39 @@ func ExampleContext_Durs() {
|
||||
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_Fields_map() {
|
||||
fields := map[string]interface{}{
|
||||
"bar": "baz",
|
||||
"n": 1,
|
||||
}
|
||||
|
||||
dst := bytes.Buffer{}
|
||||
log := New(&dst).With().
|
||||
Str("foo", "bar").
|
||||
Fields(fields).
|
||||
Logger()
|
||||
|
||||
log.Log().Msg("hello world")
|
||||
|
||||
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_Fields_slice() {
|
||||
fields := []interface{}{
|
||||
"bar", "baz",
|
||||
"n", 1,
|
||||
}
|
||||
|
||||
dst := bytes.Buffer{}
|
||||
log := New(&dst).With().
|
||||
Str("foo", "bar").
|
||||
Fields(fields).
|
||||
Logger()
|
||||
|
||||
log.Log().Msg("hello world")
|
||||
|
||||
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
+7
-7
@@ -333,19 +333,19 @@ func consoleDefaultFormatLevel(noColor bool) Formatter {
|
||||
var l string
|
||||
if ll, ok := i.(string); ok {
|
||||
switch ll {
|
||||
case "trace":
|
||||
case LevelTraceValue:
|
||||
l = colorize("TRC", colorMagenta, noColor)
|
||||
case "debug":
|
||||
case LevelDebugValue:
|
||||
l = colorize("DBG", colorYellow, noColor)
|
||||
case "info":
|
||||
case LevelInfoValue:
|
||||
l = colorize("INF", colorGreen, noColor)
|
||||
case "warn":
|
||||
case LevelWarnValue:
|
||||
l = colorize("WRN", colorRed, noColor)
|
||||
case "error":
|
||||
case LevelErrorValue:
|
||||
l = colorize(colorize("ERR", colorRed, noColor), colorBold, noColor)
|
||||
case "fatal":
|
||||
case LevelFatalValue:
|
||||
l = colorize(colorize("FTL", colorRed, noColor), colorBold, noColor)
|
||||
case "panic":
|
||||
case LevelPanicValue:
|
||||
l = colorize(colorize("PNC", colorRed, noColor), colorBold, noColor)
|
||||
default:
|
||||
l = colorize("???", colorBold, noColor)
|
||||
|
||||
+4
-2
@@ -18,8 +18,10 @@ func (c Context) Logger() Logger {
|
||||
return c.l
|
||||
}
|
||||
|
||||
// Fields is a helper function to use a map to set fields using type assertion.
|
||||
func (c Context) Fields(fields map[string]interface{}) Context {
|
||||
// Fields is a helper function to use a map or slice to set fields using type assertion.
|
||||
// Only map[string]interface{} and []interface{} are accepted. []interface{} must
|
||||
// alternate string keys and arbitrary values, and extraneous ones are ignored.
|
||||
func (c Context) Fields(fields interface{}) Context {
|
||||
c.l.context = appendFields(c.l.context, fields)
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -39,10 +39,13 @@ func (l *Logger) WithContext(ctx context.Context) context.Context {
|
||||
}
|
||||
|
||||
// Ctx returns the Logger associated with the ctx. If no logger
|
||||
// is associated, a disabled logger is returned.
|
||||
// is associated, DefaultContextLogger is returned, unless DefaultContextLogger
|
||||
// is nil, in which case a disabled logger is returned.
|
||||
func Ctx(ctx context.Context) *Logger {
|
||||
if l, ok := ctx.Value(ctxKey{}).(*Logger); ok {
|
||||
return l
|
||||
} else if l = DefaultContextLogger; l != nil {
|
||||
return l
|
||||
}
|
||||
return disabledLogger
|
||||
}
|
||||
|
||||
@@ -27,6 +27,13 @@ func TestCtx(t *testing.T) {
|
||||
if log2 != disabledLogger {
|
||||
t.Error("Ctx did not return the expected logger")
|
||||
}
|
||||
|
||||
DefaultContextLogger = &log
|
||||
t.Cleanup(func() { DefaultContextLogger = nil })
|
||||
log2 = Ctx(context.Background())
|
||||
if log2 != &log {
|
||||
t.Error("Ctx did not return the expected logger")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCtxDisabled(t *testing.T) {
|
||||
|
||||
@@ -148,8 +148,10 @@ func (e *Event) msg(msg string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fields is a helper function to use a map to set fields using type assertion.
|
||||
func (e *Event) Fields(fields map[string]interface{}) *Event {
|
||||
// Fields is a helper function to use a map or slice to set fields using type assertion.
|
||||
// Only map[string]interface{} and []interface{} are accepted. []interface{} must
|
||||
// alternate string keys and arbitrary values, and extraneous ones are ignored.
|
||||
func (e *Event) Fields(fields interface{}) *Event {
|
||||
if e == nil {
|
||||
return e
|
||||
}
|
||||
@@ -207,6 +209,12 @@ func (e *Event) Object(key string, obj LogObjectMarshaler) *Event {
|
||||
return e
|
||||
}
|
||||
e.buf = enc.AppendKey(e.buf, key)
|
||||
if obj == nil {
|
||||
e.buf = enc.AppendNil(e.buf)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
e.appendObject(obj)
|
||||
return e
|
||||
}
|
||||
@@ -224,6 +232,9 @@ func (e *Event) EmbedObject(obj LogObjectMarshaler) *Event {
|
||||
if e == nil {
|
||||
return e
|
||||
}
|
||||
if obj == nil {
|
||||
return e
|
||||
}
|
||||
obj.MarshalZerologObject(e)
|
||||
return e
|
||||
}
|
||||
@@ -246,18 +257,24 @@ 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.
|
||||
// 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
|
||||
}
|
||||
e.buf = enc.AppendStringer(enc.AppendKey(e.buf, key), val)
|
||||
return e
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
e.buf = enc.AppendString(enc.AppendKey(e.buf, key), val.String())
|
||||
// Stringers adds the field key with vals where each individual val
|
||||
// is used as val.String() (or null if val is empty) to the *Event
|
||||
// context.
|
||||
func (e *Event) Stringers(key string, vals []fmt.Stringer) *Event {
|
||||
if e == nil {
|
||||
return e
|
||||
}
|
||||
|
||||
e.buf = enc.AppendInterface(enc.AppendKey(e.buf, key), nil)
|
||||
e.buf = enc.AppendStringers(enc.AppendKey(e.buf, key), vals)
|
||||
return e
|
||||
}
|
||||
|
||||
|
||||
@@ -37,3 +37,29 @@ func TestEvent_AnErr(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_ObjectWithNil(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
e := newEvent(levelWriterAdapter{&buf}, DebugLevel)
|
||||
_ = e.Object("obj", nil)
|
||||
_ = e.write()
|
||||
|
||||
want := `{"obj":null}`
|
||||
got := strings.TrimSpace(buf.String())
|
||||
if got != want {
|
||||
t.Errorf("Event.Object() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_EmbedObjectWithNil(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
e := newEvent(levelWriterAdapter{&buf}, DebugLevel)
|
||||
_ = e.EmbedObject(nil)
|
||||
_ = e.write()
|
||||
|
||||
want := "{}"
|
||||
got := strings.TrimSpace(buf.String())
|
||||
if got != want {
|
||||
t.Errorf("Event.EmbedObject() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"sort"
|
||||
"time"
|
||||
@@ -11,15 +12,36 @@ 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 {
|
||||
keys = append(keys, key)
|
||||
func appendFields(dst []byte, fields interface{}) []byte {
|
||||
switch fields := fields.(type) {
|
||||
case []interface{}:
|
||||
if n := len(fields); n&0x1 == 1 { // odd number
|
||||
fields = fields[:n-1]
|
||||
}
|
||||
dst = appendFieldList(dst, fields)
|
||||
case map[string]interface{}:
|
||||
keys := make([]string, 0, len(fields))
|
||||
for key := range fields {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
kv := make([]interface{}, 2)
|
||||
for _, key := range keys {
|
||||
kv[0], kv[1] = key, fields[key]
|
||||
dst = appendFieldList(dst, kv)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
dst = enc.AppendKey(dst, key)
|
||||
val := fields[key]
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendFieldList(dst []byte, kvList []interface{}) []byte {
|
||||
for i, n := 0, len(kvList); i < n; i += 2 {
|
||||
key, val := kvList[i], kvList[i+1]
|
||||
if key, ok := key.(string); ok {
|
||||
dst = enc.AppendKey(dst, key)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
if val, ok := val.(LogObjectMarshaler); ok {
|
||||
e := newEvent(nil, 0)
|
||||
e.buf = e.buf[:0]
|
||||
@@ -245,6 +267,8 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte {
|
||||
dst = enc.AppendIPPrefix(dst, val)
|
||||
case net.HardwareAddr:
|
||||
dst = enc.AppendMACAddr(dst, val)
|
||||
case json.RawMessage:
|
||||
dst = appendJSON(dst, val)
|
||||
default:
|
||||
dst = enc.AppendInterface(dst, val)
|
||||
}
|
||||
|
||||
@@ -100,6 +100,10 @@ var (
|
||||
// output. If not set, an error is printed on the stderr. This handler must
|
||||
// be thread safe and non-blocking.
|
||||
ErrorHandler func(err error)
|
||||
|
||||
// DefaultContextLogger is returned from Ctx() if there is no logger associated
|
||||
// with the context.
|
||||
DefaultContextLogger *Logger
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -5,6 +5,6 @@ go 1.15
|
||||
require (
|
||||
github.com/coreos/go-systemd/v22 v22.3.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/xid v1.2.1
|
||||
golang.org/x/tools v0.1.0
|
||||
github.com/rs/xid v1.3.0
|
||||
golang.org/x/tools v0.1.7
|
||||
)
|
||||
|
||||
@@ -3,30 +3,31 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.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=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
|
||||
+2
-3
@@ -3,7 +3,6 @@ package hlog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -79,10 +78,10 @@ func RequestHandler(fieldKey string) func(next http.Handler) http.Handler {
|
||||
func RemoteAddrHandler(fieldKey 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 host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
|
||||
if r.RemoteAddr != "" {
|
||||
log := zerolog.Ctx(r.Context())
|
||||
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str(fieldKey, host)
|
||||
return c.Str(fieldKey, r.RemoteAddr)
|
||||
})
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
+2
-2
@@ -100,7 +100,7 @@ func TestRemoteAddrHandler(t *testing.T) {
|
||||
}))
|
||||
h = NewHandler(zerolog.New(out))(h)
|
||||
h.ServeHTTP(nil, r)
|
||||
if want, got := `{"ip":"1.2.3.4"}`+"\n", decodeIfBinary(out); want != got {
|
||||
if want, got := `{"ip":"1.2.3.4:1234"}`+"\n", decodeIfBinary(out); want != got {
|
||||
t.Errorf("Invalid log output, got: %s, want: %s", got, want)
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ func TestRemoteAddrHandlerIPv6(t *testing.T) {
|
||||
}))
|
||||
h = NewHandler(zerolog.New(out))(h)
|
||||
h.ServeHTTP(nil, r)
|
||||
if want, got := `{"ip":"2001:db8:a0b:12f0::1"}`+"\n", decodeIfBinary(out); want != got {
|
||||
if want, got := `{"ip":"[2001:db8:a0b:12f0::1]:1234"}`+"\n", decodeIfBinary(out); want != got {
|
||||
t.Errorf("Invalid log output, got: %s, want: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package cbor
|
||||
|
||||
import "fmt"
|
||||
|
||||
// AppendStrings encodes and adds an array of strings to the dst byte array.
|
||||
func (e Encoder) AppendStrings(dst []byte, vals []string) []byte {
|
||||
major := majorTypeArray
|
||||
@@ -30,6 +32,31 @@ func (Encoder) AppendString(dst []byte, s string) []byte {
|
||||
return append(dst, s...)
|
||||
}
|
||||
|
||||
// AppendStringers encodes and adds an array of Stringer values
|
||||
// to the dst byte array.
|
||||
func (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte {
|
||||
if len(vals) == 0 {
|
||||
return e.AppendArrayEnd(e.AppendArrayStart(dst))
|
||||
}
|
||||
dst = e.AppendArrayStart(dst)
|
||||
dst = e.AppendStringer(dst, vals[0])
|
||||
if len(vals) > 1 {
|
||||
for _, val := range vals[1:] {
|
||||
dst = e.AppendStringer(dst, val)
|
||||
}
|
||||
}
|
||||
return e.AppendArrayEnd(dst)
|
||||
}
|
||||
|
||||
// AppendStringer encodes and adds the Stringer value to the dst
|
||||
// byte array.
|
||||
func (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte {
|
||||
if val == nil {
|
||||
return e.AppendNil(dst)
|
||||
}
|
||||
return e.AppendString(dst, val.String())
|
||||
}
|
||||
|
||||
// AppendBytes encodes and adds an array of bytes to the dst byte array.
|
||||
func (Encoder) AppendBytes(dst, s []byte) []byte {
|
||||
major := majorTypeByteString
|
||||
|
||||
+30
-2
@@ -1,6 +1,9 @@
|
||||
package json
|
||||
|
||||
import "unicode/utf8"
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const hex = "0123456789abcdef"
|
||||
|
||||
@@ -60,7 +63,32 @@ func (Encoder) AppendString(dst []byte, s string) []byte {
|
||||
return append(dst, '"')
|
||||
}
|
||||
|
||||
// appendStringComplex is used by appendString to take over an in
|
||||
// AppendStringers encodes the provided Stringer list to json and
|
||||
// appends the encoded Stringer list to the input byte slice.
|
||||
func (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte {
|
||||
if len(vals) == 0 {
|
||||
return append(dst, '[', ']')
|
||||
}
|
||||
dst = append(dst, '[')
|
||||
dst = e.AppendStringer(dst, vals[0])
|
||||
if len(vals) > 1 {
|
||||
for _, val := range vals[1:] {
|
||||
dst = e.AppendStringer(append(dst, ','), val)
|
||||
}
|
||||
}
|
||||
return append(dst, ']')
|
||||
}
|
||||
|
||||
// AppendStringer encodes the input Stringer to json and appends the
|
||||
// encoded Stringer value to the input byte slice.
|
||||
func (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte {
|
||||
if val == nil {
|
||||
return e.AppendInterface(dst, nil)
|
||||
}
|
||||
return e.AppendString(dst, val.String())
|
||||
}
|
||||
|
||||
//// appendStringComplex is used by appendString to take over an in
|
||||
// progress JSON string encoding that encountered a character that needs
|
||||
// to be encoded.
|
||||
func appendStringComplex(dst []byte, s string, i int) []byte {
|
||||
|
||||
@@ -129,6 +129,7 @@ const (
|
||||
|
||||
// TraceLevel defines trace log level.
|
||||
TraceLevel Level = -1
|
||||
// Values less than TraceLevel are handled as numbers.
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
@@ -152,7 +153,7 @@ func (l Level) String() string {
|
||||
case NoLevel:
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
return strconv.Itoa(int(l))
|
||||
}
|
||||
|
||||
// ParseLevel converts a level string into a zerolog Level value.
|
||||
@@ -178,7 +179,14 @@ func ParseLevel(levelStr string) (Level, error) {
|
||||
case LevelFieldMarshalFunc(NoLevel):
|
||||
return NoLevel, nil
|
||||
}
|
||||
return NoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
|
||||
i, err := strconv.Atoi(levelStr)
|
||||
if err != nil {
|
||||
return NoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
|
||||
}
|
||||
if i > 127 || i < -128 {
|
||||
return NoLevel, fmt.Errorf("Out-Of-Bounds Level: '%d', defaulting to NoLevel", i)
|
||||
}
|
||||
return Level(i), nil
|
||||
}
|
||||
|
||||
// A Logger represents an active logging object that generates lines
|
||||
@@ -377,7 +385,7 @@ func (l *Logger) WithLevel(level Level) *Event {
|
||||
case Disabled:
|
||||
return nil
|
||||
default:
|
||||
panic("zerolog: WithLevel(): invalid level: " + strconv.Itoa(int(level)))
|
||||
return l.newEvent(level, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+70
-2
@@ -238,11 +238,15 @@ func ExampleEvent_Array() {
|
||||
Str("foo", "bar").
|
||||
Array("array", zerolog.Arr().
|
||||
Str("baz").
|
||||
Int(1),
|
||||
Int(1).
|
||||
Dict(zerolog.Dict().
|
||||
Str("bar", "baz").
|
||||
Int("n", 1),
|
||||
),
|
||||
).
|
||||
Msg("hello world")
|
||||
|
||||
// Output: {"foo":"bar","array":["baz",1],"message":"hello world"}
|
||||
// Output: {"foo":"bar","array":["baz",1,{"bar":"baz","n":1}],"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleEvent_Array_object() {
|
||||
@@ -335,6 +339,38 @@ func ExampleEvent_Durs() {
|
||||
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleEvent_Fields_map() {
|
||||
fields := map[string]interface{}{
|
||||
"bar": "baz",
|
||||
"n": 1,
|
||||
}
|
||||
|
||||
log := zerolog.New(os.Stdout)
|
||||
|
||||
log.Log().
|
||||
Str("foo", "bar").
|
||||
Fields(fields).
|
||||
Msg("hello world")
|
||||
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleEvent_Fields_slice() {
|
||||
fields := []interface{}{
|
||||
"bar", "baz",
|
||||
"n", 1,
|
||||
}
|
||||
|
||||
log := zerolog.New(os.Stdout)
|
||||
|
||||
log.Log().
|
||||
Str("foo", "bar").
|
||||
Fields(fields).
|
||||
Msg("hello world")
|
||||
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_Dict() {
|
||||
log := zerolog.New(os.Stdout).With().
|
||||
Str("foo", "bar").
|
||||
@@ -484,3 +520,35 @@ func ExampleContext_MacAddr() {
|
||||
|
||||
// Output: {"hostMAC":"00:14:22:01:23:45","message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_Fields_map() {
|
||||
fields := map[string]interface{}{
|
||||
"bar": "baz",
|
||||
"n": 1,
|
||||
}
|
||||
|
||||
log := zerolog.New(os.Stdout).With().
|
||||
Str("foo", "bar").
|
||||
Fields(fields).
|
||||
Logger()
|
||||
|
||||
log.Log().Msg("hello world")
|
||||
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
func ExampleContext_Fields_slice() {
|
||||
fields := []interface{}{
|
||||
"bar", "baz",
|
||||
"n", 1,
|
||||
}
|
||||
|
||||
log := zerolog.New(os.Stdout).With().
|
||||
Str("foo", "bar").
|
||||
Fields(fields).
|
||||
Logger()
|
||||
|
||||
log.Log().Msg("hello world")
|
||||
|
||||
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||
}
|
||||
|
||||
+82
-6
@@ -246,6 +246,66 @@ func TestFieldsMapNilPnt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsSlice(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
log.Log().Fields([]interface{}{
|
||||
"nil", nil,
|
||||
"string", "foo",
|
||||
"bytes", []byte("bar"),
|
||||
"error", errors.New("some error"),
|
||||
"bool", true,
|
||||
"int", int(1),
|
||||
"int8", int8(2),
|
||||
"int16", int16(3),
|
||||
"int32", int32(4),
|
||||
"int64", int64(5),
|
||||
"uint", uint(6),
|
||||
"uint8", uint8(7),
|
||||
"uint16", uint16(8),
|
||||
"uint32", uint32(9),
|
||||
"uint64", uint64(10),
|
||||
"float32", float32(11),
|
||||
"float64", float64(12),
|
||||
"ipv6", net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},
|
||||
"dur", 1 * time.Second,
|
||||
"time", time.Time{},
|
||||
"obj", obj{"a", "b", 1},
|
||||
}).Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"nil":null,"string":"foo","bytes":"bar","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,"float64":12,"ipv6":"2001:db8:85a3::8a2e:370:7334","dur":1000,"time":"0001-01-01T00:00:00Z","obj":{"Pub":"a","Tag":"b","priv":1}}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsSliceExtraneous(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
log.Log().Fields([]interface{}{
|
||||
"string", "foo",
|
||||
"error", errors.New("some error"),
|
||||
32, "valueForNonStringKey",
|
||||
"bool", true,
|
||||
"int", int(1),
|
||||
"keyWithoutValue",
|
||||
}).Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","error":"some error","bool":true,"int":1}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsNotMapSlice(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
log.Log().
|
||||
Fields(obj{"a", "b", 1}).
|
||||
Fields("string").
|
||||
Fields(1).
|
||||
Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFields(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
@@ -294,6 +354,7 @@ func TestFieldsArrayEmpty(t *testing.T) {
|
||||
log := New(out)
|
||||
log.Log().
|
||||
Strs("string", []string{}).
|
||||
Stringers("stringer", []fmt.Stringer{}).
|
||||
Errs("err", []error{}).
|
||||
Bools("bool", []bool{}).
|
||||
Ints("int", []int{}).
|
||||
@@ -311,7 +372,7 @@ func TestFieldsArrayEmpty(t *testing.T) {
|
||||
Durs("dur", []time.Duration{}).
|
||||
Times("time", []time.Time{}).
|
||||
Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":[],"err":[],"bool":[],"int":[],"int8":[],"int16":[],"int32":[],"int64":[],"uint":[],"uint8":[],"uint16":[],"uint32":[],"uint64":[],"float32":[],"float64":[],"dur":[],"time":[]}`+"\n"; got != want {
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":[],"stringer":[],"err":[],"bool":[],"int":[],"int8":[],"int16":[],"int32":[],"int64":[],"uint":[],"uint8":[],"uint16":[],"uint32":[],"uint64":[],"float32":[],"float64":[],"dur":[],"time":[]}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -321,6 +382,7 @@ func TestFieldsArraySingleElement(t *testing.T) {
|
||||
log := New(out)
|
||||
log.Log().
|
||||
Strs("string", []string{"foo"}).
|
||||
Stringers("stringer", []fmt.Stringer{net.IP{127, 0, 0, 1}}).
|
||||
Errs("err", []error{errors.New("some error")}).
|
||||
Bools("bool", []bool{true}).
|
||||
Ints("int", []int{1}).
|
||||
@@ -336,9 +398,9 @@ func TestFieldsArraySingleElement(t *testing.T) {
|
||||
Floats32("float32", []float32{11}).
|
||||
Floats64("float64", []float64{12}).
|
||||
Durs("dur", []time.Duration{1 * time.Second}).
|
||||
Times("time", []time.Time{time.Time{}}).
|
||||
Times("time", []time.Time{{}}).
|
||||
Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo"],"err":["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],"float64":[12],"dur":[1000],"time":["0001-01-01T00:00:00Z"]}`+"\n"; got != want {
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo"],"stringer":["127.0.0.1"],"err":["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],"float64":[12],"dur":[1000],"time":["0001-01-01T00:00:00Z"]}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -348,6 +410,7 @@ func TestFieldsArrayMultipleElement(t *testing.T) {
|
||||
log := New(out)
|
||||
log.Log().
|
||||
Strs("string", []string{"foo", "bar"}).
|
||||
Stringers("stringer", []fmt.Stringer{nil, net.IP{127, 0, 0, 1}}).
|
||||
Errs("err", []error{errors.New("some error"), nil}).
|
||||
Bools("bool", []bool{true, false}).
|
||||
Ints("int", []int{1, 0}).
|
||||
@@ -363,9 +426,9 @@ func TestFieldsArrayMultipleElement(t *testing.T) {
|
||||
Floats32("float32", []float32{11, 0}).
|
||||
Floats64("float64", []float64{12, 0}).
|
||||
Durs("dur", []time.Duration{1 * time.Second, 0}).
|
||||
Times("time", []time.Time{time.Time{}, time.Time{}}).
|
||||
Times("time", []time.Time{{}, {}}).
|
||||
Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo","bar"],"err":["some error",null],"bool":[true,false],"int":[1,0],"int8":[2,0],"int16":[3,0],"int32":[4,0],"int64":[5,0],"uint":[6,0],"uint8":[7,0],"uint16":[8,0],"uint32":[9,0],"uint64":[10,0],"float32":[11,0],"float64":[12,0],"dur":[1000,0],"time":["0001-01-01T00:00:00Z","0001-01-01T00:00:00Z"]}`+"\n"; got != want {
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo","bar"],"stringer":[null,"127.0.0.1"],"err":["some error",null],"bool":[true,false],"int":[1,0],"int8":[2,0],"int16":[3,0],"int32":[4,0],"int64":[5,0],"uint":[6,0],"uint8":[7,0],"uint16":[8,0],"uint32":[9,0],"uint64":[10,0],"float32":[11,0],"float64":[12,0],"dur":[1000,0],"time":["0001-01-01T00:00:00Z","0001-01-01T00:00:00Z"]}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -376,6 +439,7 @@ func TestFieldsDisabled(t *testing.T) {
|
||||
now := time.Now()
|
||||
log.Debug().
|
||||
Str("string", "foo").
|
||||
Stringer("stringer", net.IP{127, 0, 0, 1}).
|
||||
Bytes("bytes", []byte("bar")).
|
||||
Hex("hex", []byte{0x12, 0xef}).
|
||||
AnErr("some_err", nil).
|
||||
@@ -549,7 +613,11 @@ func TestLevelWriter(t *testing.T) {
|
||||
p string
|
||||
}{},
|
||||
}
|
||||
log := New(lw)
|
||||
|
||||
// Allow extra-verbose logs.
|
||||
SetGlobalLevel(TraceLevel - 1)
|
||||
log := New(lw).Level(TraceLevel - 1)
|
||||
|
||||
log.Trace().Msg("0")
|
||||
log.Debug().Msg("1")
|
||||
log.Info().Msg("2")
|
||||
@@ -562,6 +630,9 @@ func TestLevelWriter(t *testing.T) {
|
||||
log.WithLevel(WarnLevel).Msg("8")
|
||||
log.WithLevel(ErrorLevel).Msg("9")
|
||||
log.WithLevel(NoLevel).Msg("nolevel-2")
|
||||
log.WithLevel(-1).Msg("-1") // Same as TraceLevel
|
||||
log.WithLevel(-2).Msg("-2") // Will log
|
||||
log.WithLevel(-3).Msg("-3") // Will not log
|
||||
|
||||
want := []struct {
|
||||
l Level
|
||||
@@ -579,6 +650,8 @@ func TestLevelWriter(t *testing.T) {
|
||||
{WarnLevel, `{"level":"warn","message":"8"}` + "\n"},
|
||||
{ErrorLevel, `{"level":"error","message":"9"}` + "\n"},
|
||||
{NoLevel, `{"message":"nolevel-2"}` + "\n"},
|
||||
{Level(-1), `{"level":"trace","message":"-1"}` + "\n"},
|
||||
{Level(-2), `{"level":"-2","message":"-2"}` + "\n"},
|
||||
}
|
||||
if got := lw.ops; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("invalid ops:\ngot:\n%v\nwant:\n%v", got, want)
|
||||
@@ -853,6 +926,9 @@ func TestParseLevel(t *testing.T) {
|
||||
{"panic", args{"panic"}, PanicLevel, false},
|
||||
{"disabled", args{"disabled"}, Disabled, false},
|
||||
{"nolevel", args{""}, NoLevel, false},
|
||||
{"-1", args{"-1"}, TraceLevel, false},
|
||||
{"-2", args{"-2"}, Level(-2), false},
|
||||
{"-3", args{"-3"}, Level(-3), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ func (s RandomSampler) Sample(lvl Level) bool {
|
||||
}
|
||||
|
||||
// BasicSampler is a sampler that will send every Nth events, regardless of
|
||||
// there level.
|
||||
// their level.
|
||||
type BasicSampler struct {
|
||||
N uint32
|
||||
counter uint32
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -96,3 +101,54 @@ func MultiLevelWriter(writers ...io.Writer) LevelWriter {
|
||||
}
|
||||
return multiLevelWriter{lwriters}
|
||||
}
|
||||
|
||||
// TestingLog is the logging interface of testing.TB.
|
||||
type TestingLog interface {
|
||||
Log(args ...interface{})
|
||||
Logf(format string, args ...interface{})
|
||||
Helper()
|
||||
}
|
||||
|
||||
// TestWriter is a writer that writes to testing.TB.
|
||||
type TestWriter struct {
|
||||
T TestingLog
|
||||
|
||||
// Frame skips caller frames to capture the original file and line numbers.
|
||||
Frame int
|
||||
}
|
||||
|
||||
// NewTestWriter creates a writer that logs to the testing.TB.
|
||||
func NewTestWriter(t TestingLog) TestWriter {
|
||||
return TestWriter{T: t}
|
||||
}
|
||||
|
||||
// Write to testing.TB.
|
||||
func (t TestWriter) Write(p []byte) (n int, err error) {
|
||||
t.T.Helper()
|
||||
|
||||
n = len(p)
|
||||
|
||||
// Strip trailing newline because t.Log always adds one.
|
||||
p = bytes.TrimRight(p, "\n")
|
||||
|
||||
// Try to correct the log file and line number to the caller.
|
||||
if t.Frame > 0 {
|
||||
_, origFile, origLine, _ := runtime.Caller(1)
|
||||
_, frameFile, frameLine, ok := runtime.Caller(1 + t.Frame)
|
||||
if ok {
|
||||
erase := strings.Repeat("\b", len(path.Base(origFile))+len(strconv.Itoa(origLine))+3)
|
||||
t.T.Logf("%s%s:%d: %s", erase, path.Base(frameFile), frameLine, p)
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
t.T.Log(string(p))
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ConsoleTestWriter creates an option that correctly sets the file frame depth for testing.TB log.
|
||||
func ConsoleTestWriter(t TestingLog) func(w *ConsoleWriter) {
|
||||
return func(w *ConsoleWriter) {
|
||||
w.Out = TestWriter{T: t, Frame: 6}
|
||||
}
|
||||
}
|
||||
|
||||
+79
-15
@@ -1,10 +1,12 @@
|
||||
// +build !binary_log
|
||||
// +build !windows
|
||||
//go:build !binary_log && !windows
|
||||
// +build !binary_log,!windows
|
||||
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
@@ -53,45 +55,45 @@ func TestResilientMultiWriter(t *testing.T) {
|
||||
writers []io.Writer
|
||||
}{
|
||||
{
|
||||
name: "All valid writers",
|
||||
name: "All valid writers",
|
||||
writers: []io.Writer{
|
||||
mockedWriter {
|
||||
mockedWriter{
|
||||
wantErr: false,
|
||||
},
|
||||
mockedWriter {
|
||||
mockedWriter{
|
||||
wantErr: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "All invalid writers",
|
||||
name: "All invalid writers",
|
||||
writers: []io.Writer{
|
||||
mockedWriter {
|
||||
mockedWriter{
|
||||
wantErr: true,
|
||||
},
|
||||
mockedWriter {
|
||||
mockedWriter{
|
||||
wantErr: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "First invalid writer",
|
||||
name: "First invalid writer",
|
||||
writers: []io.Writer{
|
||||
mockedWriter {
|
||||
mockedWriter{
|
||||
wantErr: true,
|
||||
},
|
||||
mockedWriter {
|
||||
mockedWriter{
|
||||
wantErr: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "First valid writer",
|
||||
name: "First valid writer",
|
||||
writers: []io.Writer{
|
||||
mockedWriter {
|
||||
mockedWriter{
|
||||
wantErr: false,
|
||||
},
|
||||
mockedWriter {
|
||||
mockedWriter{
|
||||
wantErr: true,
|
||||
},
|
||||
},
|
||||
@@ -110,4 +112,66 @@ func TestResilientMultiWriter(t *testing.T) {
|
||||
}
|
||||
writeCalls = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testingLog struct {
|
||||
testing.TB
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (t *testingLog) Log(args ...interface{}) {
|
||||
if _, err := t.buf.WriteString(fmt.Sprint(args...)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *testingLog) Logf(format string, args ...interface{}) {
|
||||
if _, err := t.buf.WriteString(fmt.Sprintf(format, args...)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestWriter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
write []byte
|
||||
want []byte
|
||||
}{{
|
||||
name: "newline",
|
||||
write: []byte("newline\n"),
|
||||
want: []byte("newline"),
|
||||
}, {
|
||||
name: "oneline",
|
||||
write: []byte("oneline"),
|
||||
want: []byte("oneline"),
|
||||
}, {
|
||||
name: "twoline",
|
||||
write: []byte("twoline\n\n"),
|
||||
want: []byte("twoline"),
|
||||
}}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tb := &testingLog{TB: t} // Capture TB log buffer.
|
||||
w := TestWriter{T: tb}
|
||||
|
||||
n, err := w.Write(tt.write)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if n != len(tt.write) {
|
||||
t.Errorf("Expected %d write length but got %d", len(tt.write), n)
|
||||
}
|
||||
p := tb.buf.Bytes()
|
||||
if !bytes.Equal(tt.want, p) {
|
||||
t.Errorf("Expected %q, got %q.", tt.want, p)
|
||||
}
|
||||
|
||||
log := New(NewConsoleWriter(ConsoleTestWriter(t)))
|
||||
log.Info().Str("name", tt.name).Msg("Success!")
|
||||
|
||||
tb.buf.Reset()
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user