mirror of
https://github.com/rs/zerolog
synced 2026-06-08 17:13:30 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f1f25d2fa | |||
| 6d2153805b | |||
| 2988c1e444 | |||
| adb19ff852 | |||
| 3786fbfa73 |
@@ -1,10 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
@@ -1,27 +0,0 @@
|
||||
on: [push, pull_request]
|
||||
name: Test
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.15.x, 1.16.x]
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
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
|
||||
run: go test -tags binary_log ./...
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
language: go
|
||||
go:
|
||||
- "1.7"
|
||||
- "1.8"
|
||||
- "1.9"
|
||||
- "1.10"
|
||||
- "master"
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: "master"
|
||||
script:
|
||||
- go test -v -race -cpu=1,2,4 -bench . -benchmem ./...
|
||||
- go test -v -tags binary_log -race -cpu=1,2,4 -bench . -benchmem ./...
|
||||
@@ -18,21 +18,20 @@ Find out [who uses zerolog](https://github.com/rs/zerolog/wiki/Who-uses-zerolog)
|
||||
|
||||
## Features
|
||||
|
||||
* [Blazing fast](#benchmarks)
|
||||
* [Low to zero allocation](#benchmarks)
|
||||
* [Leveled logging](#leveled-logging)
|
||||
* [Sampling](#log-sampling)
|
||||
* [Hooks](#hooks)
|
||||
* [Contextual fields](#contextual-logging)
|
||||
* Blazing fast
|
||||
* Low to zero allocation
|
||||
* Level logging
|
||||
* Sampling
|
||||
* Hooks
|
||||
* Contextual fields
|
||||
* `context.Context` integration
|
||||
* [Integration with `net/http`](#integration-with-nethttp)
|
||||
* [JSON and CBOR encoding formats](#binary-encoding)
|
||||
* [Pretty logging for development](#pretty-logging)
|
||||
* [Error Logging (with optional Stacktrace)](#error-logging)
|
||||
* `net/http` helpers
|
||||
* JSON and CBOR encoding formats
|
||||
* Pretty logging for development
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
```go
|
||||
go get -u github.com/rs/zerolog/log
|
||||
```
|
||||
|
||||
@@ -52,7 +51,9 @@ import (
|
||||
|
||||
func main() {
|
||||
// UNIX Time is faster and smaller than most timestamps
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
// If you set zerolog.TimeFieldFormat to an empty string,
|
||||
// logs will write with UNIX time
|
||||
zerolog.TimeFieldFormat = ""
|
||||
|
||||
log.Print("hello world")
|
||||
}
|
||||
@@ -75,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)
|
||||
@@ -106,7 +102,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
zerolog.TimeFieldFormat = ""
|
||||
|
||||
log.Info().Msg("hello world")
|
||||
}
|
||||
@@ -124,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.
|
||||
|
||||
@@ -143,7 +138,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
zerolog.TimeFieldFormat = ""
|
||||
debug := flag.Bool("debug", false, "sets log level to debug")
|
||||
|
||||
flag.Parse()
|
||||
@@ -194,7 +189,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
zerolog.TimeFieldFormat = ""
|
||||
|
||||
log.Log().
|
||||
Str("foo", "bar").
|
||||
@@ -204,80 +199,6 @@ func main() {
|
||||
// Output: {"time":1494567715,"foo":"bar"}
|
||||
```
|
||||
|
||||
### Error Logging
|
||||
|
||||
You can log errors using the `Err` method
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
|
||||
err := errors.New("seems we have an error here")
|
||||
log.Error().Err(err).Msg("")
|
||||
}
|
||||
|
||||
// Output: {"level":"error","error":"seems we have an error here","time":1609085256}
|
||||
```
|
||||
|
||||
> The default field name for errors is `error`, you can change this by setting `zerolog.ErrorFieldName` to meet your needs.
|
||||
|
||||
#### Error Logging with Stacktrace
|
||||
|
||||
Using `github.com/pkg/errors`, you can add a formatted stacktrace to your errors.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/pkgerrors"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
|
||||
|
||||
err := outer()
|
||||
log.Error().Stack().Err(err).Msg("")
|
||||
}
|
||||
|
||||
func inner() error {
|
||||
return errors.New("seems we have an error here")
|
||||
}
|
||||
|
||||
func middle() error {
|
||||
err := inner()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func outer() error {
|
||||
err := middle()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Output: {"level":"error","stack":[{"func":"inner","line":"20","source":"errors.go"},{"func":"middle","line":"24","source":"errors.go"},{"func":"outer","line":"32","source":"errors.go"},{"func":"main","line":"15","source":"errors.go"},{"func":"main","line":"204","source":"proc.go"},{"func":"goexit","line":"1374","source":"asm_amd64.s"}],"error":"seems we have an error here","time":1609086683}
|
||||
```
|
||||
|
||||
> zerolog.ErrorStackMarshaler must be set in order for the stack to output anything.
|
||||
|
||||
#### Logging Fatal Messages
|
||||
|
||||
```go
|
||||
@@ -294,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).
|
||||
@@ -308,7 +229,6 @@ func main() {
|
||||
|
||||
> NOTE: Using `Msgf` generates one allocation even when the logger is disabled.
|
||||
|
||||
|
||||
### Create logger instance to manage different outputs
|
||||
|
||||
```go
|
||||
@@ -415,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")
|
||||
```
|
||||
|
||||
@@ -509,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).
|
||||
@@ -543,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
|
||||
|
||||
@@ -590,18 +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.
|
||||
* `Func`: Run a `func` only if the level is enabled.
|
||||
* `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:
|
||||
@@ -616,8 +514,6 @@ with zerolog library is [CSD](https://github.com/toravir/csd/).
|
||||
## Related Projects
|
||||
|
||||
* [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/hn8/zerologr): Implementation of `logr.LogSink` interface using `zerolog`
|
||||
|
||||
## Benchmarks
|
||||
|
||||
@@ -682,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()
|
||||
@@ -692,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, it’s not a big deal as JSON accepts dup keys; the last one prevails.
|
||||
|
||||
@@ -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
@@ -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
-6
@@ -3,7 +3,6 @@ package zerolog
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -235,13 +234,12 @@ 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}
|
||||
floats := []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
strings := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
|
||||
stringer := net.IP{127, 0, 0, 1}
|
||||
durations := []time.Duration{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
times := []time.Time{
|
||||
time.Unix(0, 0),
|
||||
@@ -309,9 +307,6 @@ func BenchmarkContextFieldType(b *testing.B) {
|
||||
"Strs": func(c Context) Context {
|
||||
return c.Strs("k", strings)
|
||||
},
|
||||
"Stringer": func(c Context) Context {
|
||||
return c.Stringer("k", stringer)
|
||||
},
|
||||
"Err": func(c Context) Context {
|
||||
return c.Err(errs[0])
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
|
||||
+20
-54
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -14,6 +13,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
colorBold = iota + 1
|
||||
colorFaint
|
||||
)
|
||||
|
||||
const (
|
||||
colorBlack = iota + 30
|
||||
colorRed
|
||||
@@ -23,9 +27,6 @@ const (
|
||||
colorMagenta
|
||||
colorCyan
|
||||
colorWhite
|
||||
|
||||
colorBold = 1
|
||||
colorDarkGray = 90
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -58,9 +59,6 @@ type ConsoleWriter struct {
|
||||
// PartsOrder defines the order of parts in output.
|
||||
PartsOrder []string
|
||||
|
||||
// PartsExclude defines parts to not display in output.
|
||||
PartsExclude []string
|
||||
|
||||
FormatTimestamp Formatter
|
||||
FormatLevel Formatter
|
||||
FormatCaller Formatter
|
||||
@@ -212,14 +210,6 @@ func (w ConsoleWriter) writeFields(evt map[string]interface{}, buf *bytes.Buffer
|
||||
func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, p string) {
|
||||
var f Formatter
|
||||
|
||||
if w.PartsExclude != nil && len(w.PartsExclude) > 0 {
|
||||
for _, exclude := range w.PartsExclude {
|
||||
if exclude == p {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch p {
|
||||
case LevelFieldName:
|
||||
if w.FormatLevel == nil {
|
||||
@@ -307,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,29 +308,23 @@ func consoleDefaultFormatLevel(noColor bool) Formatter {
|
||||
var l string
|
||||
if ll, ok := i.(string); ok {
|
||||
switch ll {
|
||||
case LevelTraceValue:
|
||||
l = colorize("TRC", colorMagenta, noColor)
|
||||
case LevelDebugValue:
|
||||
case "debug":
|
||||
l = colorize("DBG", colorYellow, noColor)
|
||||
case LevelInfoValue:
|
||||
case "info":
|
||||
l = colorize("INF", colorGreen, noColor)
|
||||
case LevelWarnValue:
|
||||
case "warn":
|
||||
l = colorize("WRN", colorRed, noColor)
|
||||
case LevelErrorValue:
|
||||
case "error":
|
||||
l = colorize(colorize("ERR", colorRed, noColor), colorBold, noColor)
|
||||
case LevelFatalValue:
|
||||
case "fatal":
|
||||
l = colorize(colorize("FTL", colorRed, noColor), colorBold, noColor)
|
||||
case LevelPanicValue:
|
||||
case "panic":
|
||||
l = colorize(colorize("PNC", colorRed, noColor), colorBold, noColor)
|
||||
default:
|
||||
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
|
||||
}
|
||||
@@ -368,27 +337,24 @@ func consoleDefaultFormatCaller(noColor bool) Formatter {
|
||||
c = cc
|
||||
}
|
||||
if len(c) > 0 {
|
||||
if cwd, err := os.Getwd(); err == nil {
|
||||
if rel, err := filepath.Rel(cwd, c); err == nil {
|
||||
c = rel
|
||||
}
|
||||
cwd, err := os.Getwd()
|
||||
if err == nil {
|
||||
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
-142
@@ -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)
|
||||
@@ -336,31 +222,13 @@ func TestConsoleWriterConfiguration(t *testing.T) {
|
||||
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Sets PartsExclude", func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := zerolog.ConsoleWriter{Out: buf, NoColor: true, PartsExclude: []string{"time"}}
|
||||
|
||||
d := time.Unix(0, 0).UTC().Format(time.RFC3339)
|
||||
evt := `{"time": "` + d + `", "level": "info", "message": "Foobar"}`
|
||||
_, err := w.Write([]byte(evt))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when writing output: %s", err)
|
||||
}
|
||||
|
||||
expectedOutput := "INF Foobar\n"
|
||||
actualOutput := buf.String()
|
||||
if actualOutput != expectedOutput {
|
||||
t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
|
||||
+17
-53
@@ -1,9 +1,7 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
@@ -82,17 +80,6 @@ func (c Context) Strs(key string, vals []string) Context {
|
||||
return c
|
||||
}
|
||||
|
||||
// Stringer adds the field key with val.String() (or null if val is nil) to the logger context.
|
||||
func (c Context) Stringer(key string, val fmt.Stringer) Context {
|
||||
if val != nil {
|
||||
c.l.context = enc.AppendString(enc.AppendKey(c.l.context, key), val.String())
|
||||
return c
|
||||
}
|
||||
|
||||
c.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), nil)
|
||||
return c
|
||||
}
|
||||
|
||||
// Bytes adds the field key with val as a []byte to the logger context.
|
||||
func (c Context) Bytes(key string, val []byte) Context {
|
||||
c.l.context = enc.AppendBytes(enc.AppendKey(c.l.context, key), val)
|
||||
@@ -116,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:
|
||||
@@ -139,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:
|
||||
@@ -366,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 {
|
||||
@@ -398,17 +362,17 @@ func (c Context) Caller() Context {
|
||||
return c
|
||||
}
|
||||
|
||||
// CallerWithSkipFrameCount adds the file:line of the caller with the zerolog.CallerFieldName key.
|
||||
// The specified skipFrameCount int will override the global CallerSkipFrameCount for this context's respective logger.
|
||||
// If set to -1 the global CallerSkipFrameCount will be used.
|
||||
func (c Context) CallerWithSkipFrameCount(skipFrameCount int) Context {
|
||||
c.l = c.l.Hook(newCallerHook(skipFrameCount))
|
||||
return c
|
||||
type stackTraceHook struct{}
|
||||
|
||||
func (sh stackTraceHook) Run(e *Event, level Level, msg string) {
|
||||
e.Stack()
|
||||
}
|
||||
|
||||
var sh = stackTraceHook{}
|
||||
|
||||
// Stack enables stack trace printing for the error passed to Err().
|
||||
func (c Context) Stack() Context {
|
||||
c.l.stack = true
|
||||
c.l = c.l.Hook(sh)
|
||||
return c
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
var disabledLogger *Logger
|
||||
|
||||
func init() {
|
||||
SetGlobalLevel(TraceLevel)
|
||||
l := Nop()
|
||||
disabledLogger = &l
|
||||
}
|
||||
@@ -39,13 +38,10 @@ func (l *Logger) WithContext(ctx context.Context) context.Context {
|
||||
}
|
||||
|
||||
// Ctx returns the Logger associated with the ctx. If no logger
|
||||
// is associated, DefaultContextLogger is returned, unless DefaultContextLogger
|
||||
// is nil, in which case a disabled logger is returned.
|
||||
// is associated, 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,13 +27,6 @@ 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) {
|
||||
|
||||
+3
-3
@@ -48,7 +48,7 @@ type Writer struct {
|
||||
// used.
|
||||
//
|
||||
// See code.cloudfoundry.org/go-diodes for more info on diode.
|
||||
func NewWriter(w io.Writer, size int, pollInterval time.Duration, f Alerter) Writer {
|
||||
func NewWriter(w io.Writer, size int, poolInterval time.Duration, f Alerter) Writer {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
dw := Writer{
|
||||
w: w,
|
||||
@@ -59,9 +59,9 @@ func NewWriter(w io.Writer, size int, pollInterval time.Duration, f Alerter) Wri
|
||||
f = func(int) {}
|
||||
}
|
||||
d := diodes.NewManyToOne(size, diodes.AlertFunc(f))
|
||||
if pollInterval > 0 {
|
||||
if poolInterval > 0 {
|
||||
dw.d = diodes.NewPoller(d,
|
||||
diodes.WithPollingInterval(pollInterval),
|
||||
diodes.WithPollingInterval(poolInterval),
|
||||
diodes.WithPollingContext(ctx))
|
||||
} else {
|
||||
dw.d = diodes.NewWaiter(d,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,6 @@ var (
|
||||
enc = cbor.Encoder{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// using closure to reflect the changes at runtime.
|
||||
cbor.JSONMarshalFunc = func(v interface{}) ([]byte, error) {
|
||||
return InterfaceMarshalFunc(v)
|
||||
}
|
||||
}
|
||||
|
||||
func appendJSON(dst []byte, j []byte) []byte {
|
||||
return cbor.AppendEmbeddedJSON(dst, j)
|
||||
}
|
||||
|
||||
@@ -15,13 +15,6 @@ var (
|
||||
enc = json.Encoder{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// using closure to reflect the changes at runtime.
|
||||
json.JSONMarshalFunc = func(v interface{}) ([]byte, error) {
|
||||
return InterfaceMarshalFunc(v)
|
||||
}
|
||||
}
|
||||
|
||||
func appendJSON(dst []byte, j []byte) []byte {
|
||||
return append(dst, j...)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -20,13 +21,12 @@ var eventPool = &sync.Pool{
|
||||
// Event represents a log event. It is instanced by one of the level method of
|
||||
// Logger and finalized by the Msg or Msgf method.
|
||||
type Event struct {
|
||||
buf []byte
|
||||
w LevelWriter
|
||||
level Level
|
||||
done func(msg string)
|
||||
stack bool // enable error stack trace
|
||||
ch []Hook // hooks from context
|
||||
skipFrame int // The number of additional frames to skip when printing the caller.
|
||||
buf []byte
|
||||
w LevelWriter
|
||||
level Level
|
||||
done func(msg string)
|
||||
stack bool // enable error stack trace
|
||||
ch []Hook // hooks from context
|
||||
}
|
||||
|
||||
func putEvent(e *Event) {
|
||||
@@ -62,8 +62,6 @@ func newEvent(w LevelWriter, level Level) *Event {
|
||||
e.buf = enc.AppendBeginMarker(e.buf)
|
||||
e.w = w
|
||||
e.level = level
|
||||
e.stack = false
|
||||
e.skipFrame = 0
|
||||
return e
|
||||
}
|
||||
|
||||
@@ -108,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
|
||||
@@ -130,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)
|
||||
@@ -207,32 +200,15 @@ 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
|
||||
}
|
||||
|
||||
// Func allows an anonymous func to run only if the event is enabled.
|
||||
func (e *Event) Func(f func(e *Event)) *Event {
|
||||
if e != nil && e.Enabled() {
|
||||
f(e)
|
||||
}
|
||||
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
|
||||
}
|
||||
if obj == nil {
|
||||
return e
|
||||
}
|
||||
obj.MarshalZerologObject(e)
|
||||
return e
|
||||
}
|
||||
@@ -255,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
|
||||
@@ -315,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:
|
||||
@@ -352,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.
|
||||
//
|
||||
@@ -368,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:
|
||||
@@ -704,36 +660,20 @@ func (e *Event) Interface(key string, i interface{}) *Event {
|
||||
return e
|
||||
}
|
||||
|
||||
// CallerSkipFrame instructs any future Caller calls to skip the specified number of frames.
|
||||
// This includes those added via hooks from the context.
|
||||
func (e *Event) CallerSkipFrame(skip int) *Event {
|
||||
if e == nil {
|
||||
return e
|
||||
}
|
||||
e.skipFrame += skip
|
||||
return e
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if e == nil {
|
||||
return e
|
||||
}
|
||||
_, file, line, ok := runtime.Caller(skip + e.skipFrame)
|
||||
_, file, line, ok := runtime.Caller(skip)
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,65 +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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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,17 +1,11 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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 {
|
||||
@@ -35,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]
|
||||
@@ -43,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:
|
||||
@@ -56,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]
|
||||
@@ -64,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:
|
||||
@@ -246,8 +234,6 @@ 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)
|
||||
}
|
||||
|
||||
+12
-63
@@ -1,25 +1,7 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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.
|
||||
@@ -28,26 +10,6 @@ var (
|
||||
// LevelFieldName is the field name used for the level field.
|
||||
LevelFieldName = "level"
|
||||
|
||||
// LevelTraceValue is the value used for the trace level field.
|
||||
LevelTraceValue = "trace"
|
||||
// LevelDebugValue is the value used for the debug level field.
|
||||
LevelDebugValue = "debug"
|
||||
// LevelInfoValue is the value used for the info level field.
|
||||
LevelInfoValue = "info"
|
||||
// LevelWarnValue is the value used for the warn level field.
|
||||
LevelWarnValue = "warn"
|
||||
// LevelErrorValue is the value used for the error level field.
|
||||
LevelErrorValue = "error"
|
||||
// LevelFatalValue is the value used for the fatal level field.
|
||||
LevelFatalValue = "fatal"
|
||||
// LevelPanicValue is the value used for the panic level field.
|
||||
LevelPanicValue = "panic"
|
||||
|
||||
// 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"
|
||||
|
||||
@@ -60,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"
|
||||
|
||||
@@ -76,13 +33,9 @@ var (
|
||||
return err
|
||||
}
|
||||
|
||||
// InterfaceMarshalFunc allows customization of interface marshaling.
|
||||
// Default: "encoding/json.Marshal"
|
||||
InterfaceMarshalFunc = json.Marshal
|
||||
|
||||
// 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.
|
||||
@@ -100,15 +53,11 @@ 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 (
|
||||
gLevel = new(int32)
|
||||
disableSampling = new(int32)
|
||||
gLevel = new(uint32)
|
||||
disableSampling = new(uint32)
|
||||
)
|
||||
|
||||
// SetGlobalLevel sets the global override for log level. If this
|
||||
@@ -116,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
|
||||
}
|
||||
|
||||
@@ -1,10 +1 @@
|
||||
module github.com/rs/zerolog
|
||||
|
||||
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.3.0
|
||||
golang.org/x/tools v0.1.5
|
||||
)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
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.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/yuin/goldmark v1.3.5/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/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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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/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.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -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
|
||||
+5
-25
@@ -3,13 +3,14 @@ package hlog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"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.
|
||||
@@ -78,10 +79,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 r.RemoteAddr != "" {
|
||||
if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
|
||||
log := zerolog.Ctx(r.Context())
|
||||
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str(fieldKey, r.RemoteAddr)
|
||||
return c.Str(fieldKey, host)
|
||||
})
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
@@ -137,11 +138,6 @@ func IDFromCtx(ctx context.Context) (id xid.ID, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// CtxWithID adds the given xid.ID to the context
|
||||
func CtxWithID(ctx context.Context, id xid.ID) context.Context {
|
||||
return context.WithValue(ctx, idKey{}, id)
|
||||
}
|
||||
|
||||
// RequestIDHandler returns a handler setting a unique id to the request which can
|
||||
// be gathered using IDFromRequest(req). This generated id is added as a field to the
|
||||
// logger using the passed fieldKey as field name. The id is also added as a response
|
||||
@@ -158,7 +154,7 @@ func RequestIDHandler(fieldKey, headerName string) func(next http.Handler) http.
|
||||
id, ok := IDFromRequest(r)
|
||||
if !ok {
|
||||
id = xid.New()
|
||||
ctx = CtxWithID(ctx, id)
|
||||
ctx = context.WithValue(ctx, idKey{}, id)
|
||||
r = r.WithContext(ctx)
|
||||
}
|
||||
if fieldKey != "" {
|
||||
@@ -175,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 {
|
||||
|
||||
+6
-37
@@ -4,16 +4,16 @@ package hlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/xid"
|
||||
"reflect"
|
||||
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/internal/cbor"
|
||||
)
|
||||
@@ -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:1234"}`+"\n", decodeIfBinary(out); want != got {
|
||||
if want, got := `{"ip":"1.2.3.4"}`+"\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]:1234"}`+"\n", decodeIfBinary(out); want != got {
|
||||
if want, got := `{"ip":"2001:db8:a0b:12f0::1"}`+"\n", decodeIfBinary(out); want != got {
|
||||
t.Errorf("Invalid log output, got: %s, want: %s", got, want)
|
||||
}
|
||||
}
|
||||
@@ -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{
|
||||
@@ -262,16 +244,3 @@ func BenchmarkDataRace(b *testing.B) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCtxWithID(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
id, _ := xid.FromString(`c0umremcie6smuu506pg`)
|
||||
|
||||
want := context.Background()
|
||||
want = context.WithValue(want, idKey{}, id)
|
||||
|
||||
if got := CtxWithID(ctx, id); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("CtxWithID() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -1,154 +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 {
|
||||
n, err := io.Copy(&f.basicWriter, r)
|
||||
f.bytes += int(n)
|
||||
return n, err
|
||||
}
|
||||
rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
|
||||
f.basicWriter.maybeWriteHeader()
|
||||
|
||||
n, err := rf.ReadFrom(r)
|
||||
f.bytes += int(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
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{}
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
package cbor
|
||||
|
||||
// JSONMarshalFunc is used to marshal interface to JSON encoded byte slice.
|
||||
// Making it package level instead of embedded in Encoder brings
|
||||
// some extra efforts at importing, but avoids value copy when the functions
|
||||
// of Encoder being invoked.
|
||||
// DO REMEMBER to set this variable at importing, or
|
||||
// you might get a nil pointer dereference panic at runtime.
|
||||
var JSONMarshalFunc func(v interface{}) ([]byte, error)
|
||||
|
||||
type Encoder struct{}
|
||||
|
||||
// AppendKey adds a key (string) to the binary encoded log message
|
||||
@@ -16,4 +8,4 @@ func (e Encoder) AppendKey(dst []byte, key string) []byte {
|
||||
dst = e.AppendBeginMarker(dst)
|
||||
}
|
||||
return e.AppendString(dst, key)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
@@ -23,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.
|
||||
@@ -431,7 +432,7 @@ func (e Encoder) AppendFloats64(dst []byte, vals []float64) []byte {
|
||||
|
||||
// AppendInterface takes an arbitrary object and converts it to JSON and embeds it dst.
|
||||
func (e Encoder) AppendInterface(dst []byte, i interface{}) []byte {
|
||||
marshaled, err := JSONMarshalFunc(i)
|
||||
marshaled, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return e.AppendString(dst, fmt.Sprintf("marshaling error: %v", err))
|
||||
}
|
||||
|
||||
@@ -1,34 +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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
+4
-11
@@ -1,19 +1,12 @@
|
||||
package json
|
||||
|
||||
// JSONMarshalFunc is used to marshal interface to JSON encoded byte slice.
|
||||
// Making it package level instead of embedded in Encoder brings
|
||||
// some extra efforts at importing, but avoids value copy when the functions
|
||||
// of Encoder being invoked.
|
||||
// DO REMEMBER to set this variable at importing, or
|
||||
// you might get a nil pointer dereference panic at runtime.
|
||||
var JSONMarshalFunc func(v interface{}) ([]byte, error)
|
||||
|
||||
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
@@ -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 {
|
||||
|
||||
+8
-11
@@ -1,6 +1,7 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
@@ -349,7 +350,7 @@ func (Encoder) AppendFloats64(dst []byte, vals []float64) []byte {
|
||||
return append(dst, '[', ']')
|
||||
}
|
||||
dst = append(dst, '[')
|
||||
dst = appendFloat(dst, vals[0], 64)
|
||||
dst = appendFloat(dst, vals[0], 32)
|
||||
if len(vals) > 1 {
|
||||
for _, val := range vals[1:] {
|
||||
dst = appendFloat(append(dst, ','), val, 64)
|
||||
@@ -362,7 +363,7 @@ func (Encoder) AppendFloats64(dst []byte, vals []float64) []byte {
|
||||
// AppendInterface marshals the input interface to a string and
|
||||
// appends the encoded string to the input byte slice.
|
||||
func (e Encoder) AppendInterface(dst []byte, i interface{}) []byte {
|
||||
marshaled, err := JSONMarshalFunc(i)
|
||||
marshaled, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return e.AppendString(dst, fmt.Sprintf("marshaling error: %v", err))
|
||||
}
|
||||
@@ -372,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, ',')
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+4
-14
@@ -20,12 +20,11 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-systemd/v22/journal"
|
||||
"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
|
||||
}
|
||||
|
||||
@@ -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!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, it’s 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,72 +126,53 @@ const (
|
||||
NoLevel
|
||||
// Disabled disables the logger.
|
||||
Disabled
|
||||
|
||||
// TraceLevel defines trace log level.
|
||||
TraceLevel Level = -1
|
||||
// Values less than TraceLevel are handled as numbers.
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case TraceLevel:
|
||||
return LevelTraceValue
|
||||
case DebugLevel:
|
||||
return LevelDebugValue
|
||||
return "debug"
|
||||
case InfoLevel:
|
||||
return LevelInfoValue
|
||||
return "info"
|
||||
case WarnLevel:
|
||||
return LevelWarnValue
|
||||
return "warn"
|
||||
case ErrorLevel:
|
||||
return LevelErrorValue
|
||||
return "error"
|
||||
case FatalLevel:
|
||||
return LevelFatalValue
|
||||
return "fatal"
|
||||
case PanicLevel:
|
||||
return LevelPanicValue
|
||||
case Disabled:
|
||||
return "disabled"
|
||||
return "panic"
|
||||
case NoLevel:
|
||||
return ""
|
||||
}
|
||||
return strconv.Itoa(int(l))
|
||||
return ""
|
||||
}
|
||||
|
||||
// ParseLevel converts a level string into a zerolog Level value.
|
||||
// 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(Disabled):
|
||||
return Disabled, nil
|
||||
case LevelFieldMarshalFunc(NoLevel):
|
||||
case NoLevel.String():
|
||||
return NoLevel, nil
|
||||
}
|
||||
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
|
||||
return NoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -200,7 +181,6 @@ type Logger struct {
|
||||
sampler Sampler
|
||||
context []byte
|
||||
hooks []Hook
|
||||
stack bool
|
||||
}
|
||||
|
||||
// New creates a root logger with given output writer. If the output writer implements
|
||||
@@ -208,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 {
|
||||
@@ -218,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.
|
||||
@@ -231,7 +211,6 @@ func (l Logger) Output(w io.Writer) Logger {
|
||||
l2 := New(w)
|
||||
l2.level = l.level
|
||||
l2.sampler = l.sampler
|
||||
l2.stack = l.stack
|
||||
if len(l.hooks) > 0 {
|
||||
l2.hooks = append(l2.hooks, l.hooks...)
|
||||
}
|
||||
@@ -248,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}
|
||||
}
|
||||
@@ -266,9 +241,6 @@ func (l *Logger) UpdateContext(update func(c Context) Context) {
|
||||
if cap(l.context) == 0 {
|
||||
l.context = make([]byte, 0, 500)
|
||||
}
|
||||
if len(l.context) == 0 {
|
||||
l.context = enc.AppendBeginMarker(l.context)
|
||||
}
|
||||
c := update(Context{*l})
|
||||
l.context = c.l.context
|
||||
}
|
||||
@@ -279,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
|
||||
@@ -296,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.
|
||||
@@ -331,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.
|
||||
//
|
||||
@@ -366,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:
|
||||
@@ -385,7 +331,7 @@ func (l *Logger) WithLevel(level Level) *Event {
|
||||
case Disabled:
|
||||
return nil
|
||||
default:
|
||||
return l.newEvent(level, nil)
|
||||
panic("zerolog: WithLevel(): invalid level: " + strconv.Itoa(int(level)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +347,7 @@ func (l *Logger) Log() *Event {
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func (l *Logger) Print(v ...interface{}) {
|
||||
if e := l.Debug(); e.Enabled() {
|
||||
e.CallerSkipFrame(1).Msg(fmt.Sprint(v...))
|
||||
e.Msg(fmt.Sprint(v...))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +355,7 @@ func (l *Logger) Print(v ...interface{}) {
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Printf(format string, v ...interface{}) {
|
||||
if e := l.Debug(); e.Enabled() {
|
||||
e.CallerSkipFrame(1).Msg(fmt.Sprintf(format, v...))
|
||||
e.Msg(fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,7 +367,7 @@ func (l Logger) Write(p []byte) (n int, err error) {
|
||||
// Trim CR added by stdlog.
|
||||
p = p[0 : n-1]
|
||||
}
|
||||
l.Log().CallerSkipFrame(1).Msg(string(p))
|
||||
l.Log().Msg(string(p))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -433,15 +379,12 @@ func (l *Logger) newEvent(level Level, done func(string)) *Event {
|
||||
e := newEvent(l.w, level)
|
||||
e.done = done
|
||||
e.ch = l.hooks
|
||||
if level != NoLevel && LevelFieldName != "" {
|
||||
e.Str(LevelFieldName, LevelFieldMarshalFunc(level))
|
||||
if level != NoLevel {
|
||||
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)
|
||||
}
|
||||
if l.stack {
|
||||
e.Stack()
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
|
||||
+2
-18
@@ -3,7 +3,6 @@ package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
@@ -38,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.
|
||||
@@ -115,13 +99,13 @@ func Log() *zerolog.Event {
|
||||
// Print sends a log event using debug level and no extra field.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func Print(v ...interface{}) {
|
||||
Logger.Debug().CallerSkipFrame(1).Msg(fmt.Sprint(v...))
|
||||
Logger.Print(v...)
|
||||
}
|
||||
|
||||
// Printf sends a log event using debug level and no extra field.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func Printf(format string, v ...interface{}) {
|
||||
Logger.Debug().CallerSkipFrame(1).Msgf(format, v...)
|
||||
Logger.Printf(format, v...)
|
||||
}
|
||||
|
||||
// Ctx returns the Logger associated with the ctx. If no logger
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
+11
-229
@@ -7,8 +7,6 @@ import (
|
||||
"net"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -77,30 +75,10 @@ func TestInfo(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEmptyLevelFieldName(t *testing.T) {
|
||||
fieldName := LevelFieldName
|
||||
LevelFieldName = ""
|
||||
|
||||
t.Run("empty setting", func(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
log := New(out)
|
||||
log.Info().
|
||||
Str("foo", "bar").
|
||||
Int("n", 123).
|
||||
Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","n":123}`+"\n"; got != want {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
})
|
||||
LevelFieldName = fieldName
|
||||
}
|
||||
|
||||
func TestWith(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
ctx := New(out).With().
|
||||
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"}`)).
|
||||
@@ -124,21 +102,7 @@ func TestWith(t *testing.T) {
|
||||
caller := fmt.Sprintf("%s:%d", file, line+3)
|
||||
log := ctx.Caller().Logger()
|
||||
log.Log().Msg("")
|
||||
if got, want := decodeIfBinaryToString(out.Bytes()), `{"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,"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","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,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -255,12 +219,9 @@ 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"}`)).
|
||||
Func(func(e *Event) { e.Str("func", "func_output") }).
|
||||
AnErr("some_err", nil).
|
||||
Err(errors.New("some error")).
|
||||
Bool("bool", true).
|
||||
@@ -284,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"},"func":"func_output","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)
|
||||
}
|
||||
}
|
||||
@@ -380,7 +341,6 @@ func TestFieldsDisabled(t *testing.T) {
|
||||
Hex("hex", []byte{0x12, 0xef}).
|
||||
AnErr("some_err", nil).
|
||||
Err(errors.New("some error")).
|
||||
Func(func(e *Event) { e.Str("func", "func_output") }).
|
||||
Bool("bool", true).
|
||||
Int("int", 1).
|
||||
Int8("int8", 2).
|
||||
@@ -477,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})
|
||||
@@ -549,45 +491,32 @@ func TestLevelWriter(t *testing.T) {
|
||||
p string
|
||||
}{},
|
||||
}
|
||||
|
||||
// Allow extra-verbose logs.
|
||||
SetGlobalLevel(TraceLevel - 1)
|
||||
log := New(lw).Level(TraceLevel - 1)
|
||||
|
||||
log.Trace().Msg("0")
|
||||
log := New(lw)
|
||||
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")
|
||||
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
|
||||
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"},
|
||||
{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)
|
||||
@@ -712,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
|
||||
}
|
||||
@@ -801,81 +661,3 @@ func TestErrorHandler(t *testing.T) {
|
||||
t.Errorf("ErrorHandler err = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateEmptyContext(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
log := New(&buf)
|
||||
|
||||
log.UpdateContext(func(c Context) Context {
|
||||
return c.Str("foo", "bar")
|
||||
})
|
||||
log.Info().Msg("no panic")
|
||||
|
||||
want := `{"level":"info","foo":"bar","message":"no panic"}` + "\n"
|
||||
|
||||
if got := decodeIfBinaryToString(buf.Bytes()); got != want {
|
||||
t.Errorf("invalid log output:\ngot: %q\nwant: %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLevel_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
l Level
|
||||
want string
|
||||
}{
|
||||
{"trace", TraceLevel, "trace"},
|
||||
{"debug", DebugLevel, "debug"},
|
||||
{"info", InfoLevel, "info"},
|
||||
{"warn", WarnLevel, "warn"},
|
||||
{"error", ErrorLevel, "error"},
|
||||
{"fatal", FatalLevel, "fatal"},
|
||||
{"panic", PanicLevel, "panic"},
|
||||
{"disabled", Disabled, "disabled"},
|
||||
{"nolevel", NoLevel, ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.l.String(); got != tt.want {
|
||||
t.Errorf("String() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLevel(t *testing.T) {
|
||||
type args struct {
|
||||
levelStr string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Level
|
||||
wantErr bool
|
||||
}{
|
||||
{"trace", args{"trace"}, TraceLevel, false},
|
||||
{"debug", args{"debug"}, DebugLevel, false},
|
||||
{"info", args{"info"}, InfoLevel, false},
|
||||
{"warn", args{"warn"}, WarnLevel, false},
|
||||
{"error", args{"error"}, ErrorLevel, false},
|
||||
{"fatal", args{"fatal"}, FatalLevel, false},
|
||||
{"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) {
|
||||
got, err := ParseLevel(tt.args.levelStr)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseLevel() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("ParseLevel() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// +build !go1.12
|
||||
|
||||
package zerolog
|
||||
|
||||
const contextCallerSkipFrameCount = 3
|
||||
@@ -27,22 +27,6 @@ func TestLogStack(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogStackFromContext(t *testing.T) {
|
||||
zerolog.ErrorStackMarshaler = MarshalStack
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
log := zerolog.New(out).With().Stack().Logger() // calling Stack() on log context instead of event
|
||||
|
||||
err := errors.Wrap(errors.New("error message"), "from error")
|
||||
log.Log().Err(err).Msg("") // not explicitly calling Stack()
|
||||
|
||||
got := out.String()
|
||||
want := `\{"stack":\[\{"func":"TestLogStackFromContext","line":"36","source":"stacktrace_test.go"\},.*\],"error":"from error: error message"\}\n`
|
||||
if ok, _ := regexp.MatchString(want, got); !ok {
|
||||
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLogStack(b *testing.B) {
|
||||
zerolog.ErrorStackMarshaler = MarshalStack
|
||||
out := &bytes.Buffer{}
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 141 KiB |
+3
-11
@@ -38,7 +38,7 @@ func (s RandomSampler) Sample(lvl Level) bool {
|
||||
}
|
||||
|
||||
// BasicSampler is a sampler that will send every Nth events, regardless of
|
||||
// their level.
|
||||
// there level.
|
||||
type BasicSampler struct {
|
||||
N uint32
|
||||
counter uint32
|
||||
@@ -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)
|
||||
|
||||
@@ -7,10 +7,6 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// See http://cee.mitre.org/language/1.0-beta1/clt.html#syslog
|
||||
// or https://www.rsyslog.com/json-elasticsearch/
|
||||
const ceePrefix = "@cee:"
|
||||
|
||||
// SyslogWriter is an interface matching a syslog.Writer struct.
|
||||
type SyslogWriter interface {
|
||||
io.Writer
|
||||
@@ -23,58 +19,39 @@ type SyslogWriter interface {
|
||||
}
|
||||
|
||||
type syslogWriter struct {
|
||||
w SyslogWriter
|
||||
prefix string
|
||||
w SyslogWriter
|
||||
}
|
||||
|
||||
// SyslogLevelWriter wraps a SyslogWriter and call the right syslog level
|
||||
// method matching the zerolog level.
|
||||
func SyslogLevelWriter(w SyslogWriter) LevelWriter {
|
||||
return syslogWriter{w, ""}
|
||||
}
|
||||
|
||||
// SyslogCEEWriter wraps a SyslogWriter with a SyslogLevelWriter that adds a
|
||||
// MITRE CEE prefix for JSON syslog entries, compatible with rsyslog
|
||||
// and syslog-ng JSON logging support.
|
||||
// See https://www.rsyslog.com/json-elasticsearch/
|
||||
func SyslogCEEWriter(w SyslogWriter) LevelWriter {
|
||||
return syslogWriter{w, ceePrefix}
|
||||
return syslogWriter{w}
|
||||
}
|
||||
|
||||
func (sw syslogWriter) Write(p []byte) (n int, err error) {
|
||||
var pn int
|
||||
if sw.prefix != "" {
|
||||
pn, err = sw.w.Write([]byte(sw.prefix))
|
||||
if err != nil {
|
||||
return pn, err
|
||||
}
|
||||
}
|
||||
n, err = sw.w.Write(p)
|
||||
return pn + n, err
|
||||
return sw.w.Write(p)
|
||||
}
|
||||
|
||||
// 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(sw.prefix + string(p))
|
||||
err = sw.w.Debug(string(p))
|
||||
case InfoLevel:
|
||||
err = sw.w.Info(sw.prefix + string(p))
|
||||
err = sw.w.Info(string(p))
|
||||
case WarnLevel:
|
||||
err = sw.w.Warning(sw.prefix + string(p))
|
||||
err = sw.w.Warning(string(p))
|
||||
case ErrorLevel:
|
||||
err = sw.w.Err(sw.prefix + string(p))
|
||||
err = sw.w.Err(string(p))
|
||||
case FatalLevel:
|
||||
err = sw.w.Emerg(sw.prefix + string(p))
|
||||
err = sw.w.Emerg(string(p))
|
||||
case PanicLevel:
|
||||
err = sw.w.Crit(sw.prefix + string(p))
|
||||
err = sw.w.Crit(string(p))
|
||||
case NoLevel:
|
||||
err = sw.w.Info(sw.prefix + string(p))
|
||||
err = sw.w.Info(string(p))
|
||||
default:
|
||||
panic("invalid level")
|
||||
}
|
||||
// Any CEE prefix is not part of the message, so we don't include its length
|
||||
n = len(p)
|
||||
return
|
||||
}
|
||||
|
||||
+2
-47
@@ -3,12 +3,8 @@
|
||||
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
import "testing"
|
||||
import "reflect"
|
||||
|
||||
type syslogEvent struct {
|
||||
level string
|
||||
@@ -21,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
|
||||
@@ -53,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")
|
||||
@@ -70,39 +61,3 @@ func TestSyslogWriter(t *testing.T) {
|
||||
t.Errorf("Invalid syslog message routing: want %v, got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
type testCEEwriter struct {
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
// Only implement one method as we're just testing the prefixing
|
||||
func (c testCEEwriter) Debug(m string) error { return nil }
|
||||
|
||||
func (c testCEEwriter) Info(m string) error {
|
||||
_, err := c.buf.Write([]byte(m))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c testCEEwriter) Warning(m string) error { return nil }
|
||||
|
||||
func (c testCEEwriter) Err(m string) error { return nil }
|
||||
|
||||
func (c testCEEwriter) Emerg(m string) error { return nil }
|
||||
|
||||
func (c testCEEwriter) Crit(m string) error { return nil }
|
||||
|
||||
func (c testCEEwriter) Write(b []byte) (int, error) {
|
||||
return c.buf.Write(b)
|
||||
}
|
||||
|
||||
func TestSyslogWriter_WithCEE(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
sw := testCEEwriter{&buf}
|
||||
log := New(SyslogCEEWriter(sw))
|
||||
log.Info().Str("key", "value").Msg("message string")
|
||||
got := string(buf.Bytes())
|
||||
want := "@cee:{"
|
||||
if !strings.HasPrefix(got, want) {
|
||||
t.Errorf("Bad CEE message start: want %v, got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
@@ -56,30 +58,30 @@ type multiLevelWriter struct {
|
||||
|
||||
func (t multiLevelWriter) Write(p []byte) (n int, err error) {
|
||||
for _, w := range t.writers {
|
||||
if _n, _err := w.Write(p); err == nil {
|
||||
n = _n
|
||||
if _err != nil {
|
||||
err = _err
|
||||
} else if _n != len(p) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
n, err = w.Write(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n != len(p) {
|
||||
err = io.ErrShortWrite
|
||||
return
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (t multiLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) {
|
||||
for _, w := range t.writers {
|
||||
if _n, _err := w.WriteLevel(l, p); err == nil {
|
||||
n = _n
|
||||
if _err != nil {
|
||||
err = _err
|
||||
} else if _n != len(p) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
n, err = w.WriteLevel(l, p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n != len(p) {
|
||||
err = io.ErrShortWrite
|
||||
return
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// MultiLevelWriter creates a writer that duplicates its writes to all the
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -29,85 +27,3 @@ func TestMultiSyslogWriter(t *testing.T) {
|
||||
t.Errorf("Invalid syslog message routing: want %v, got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
var writeCalls int
|
||||
|
||||
type mockedWriter struct {
|
||||
wantErr bool
|
||||
}
|
||||
|
||||
func (c mockedWriter) Write(p []byte) (int, error) {
|
||||
writeCalls++
|
||||
|
||||
if c.wantErr {
|
||||
return -1, errors.New("Expected error")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Tests that a new writer is only used if it actually works.
|
||||
func TestResilientMultiWriter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
writers []io.Writer
|
||||
}{
|
||||
{
|
||||
name: "All valid writers",
|
||||
writers: []io.Writer{
|
||||
mockedWriter {
|
||||
wantErr: false,
|
||||
},
|
||||
mockedWriter {
|
||||
wantErr: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "All invalid writers",
|
||||
writers: []io.Writer{
|
||||
mockedWriter {
|
||||
wantErr: true,
|
||||
},
|
||||
mockedWriter {
|
||||
wantErr: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "First invalid writer",
|
||||
writers: []io.Writer{
|
||||
mockedWriter {
|
||||
wantErr: true,
|
||||
},
|
||||
mockedWriter {
|
||||
wantErr: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "First valid writer",
|
||||
writers: []io.Writer{
|
||||
mockedWriter {
|
||||
wantErr: false,
|
||||
},
|
||||
mockedWriter {
|
||||
wantErr: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
writers := tt.writers
|
||||
multiWriter := MultiLevelWriter(writers...)
|
||||
|
||||
logger := New(multiWriter).With().Timestamp().Logger().Level(InfoLevel)
|
||||
logger.Info().Msg("Test msg")
|
||||
|
||||
if len(writers) != writeCalls {
|
||||
t.Errorf("Expected %d writers to have been called but only %d were.", len(writers), writeCalls)
|
||||
}
|
||||
writeCalls = 0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user