mirror of
https://github.com/rs/zerolog
synced 2026-06-08 17:13:30 +00:00
f6fbd330be
* Test coverage improvements Implements #397 and #591, might help with #473 Test coverage for core is 97.6% with only real fringe cases remaining. Improve `Fields` `isNilValue()` portability. Added a new global handler `FatalExitFunc` to allow intercepting `Fatal()` messages. Fixed CBOR float constants (removed the CBOR prefix) Added tests for: - global `FatalExitFunc` to allow intercepting Fatal messages (both for testing and public use). - `Logger` - `DisableSampling` - `.With()` copying existing context if present. - `.With().Fields()` all forms of `ErrorStackMarshaler` returns. - `.WithLevel()` for `FatalLevel`, `PanicLevel`, and `DisabledLevel`. - `.Err()` with `nil` and non-`nil` `error` and test the resulting log level. - `.should()` covering `nil` writer. - `.Output()` gets context values. - `.UpdateContext()` on a disabled logger doesn't panic and is a nop. - `.With()` all forms of `ErrorStackMarshaler` returns. - with a `nil`writer. - `.Hook()` passing no hooks. - `Array` - `.MarshalZerologArray` is a nop that won't panic. - `Context` - ` .Err()` and `.AnErr()` for `nil` errors and all forms of `ErrorStackMarshaler` returns. - `Event` - `.Caller()` to ensure we don't panic or add invalid information if `runtime.Caller()` fails -` .Err()` and `.AnErr()` for `nil` errors and all forms of `ErrorStackMarshaler` returns. - `Fields` - ` .appendFields()` all forms of `ErrorStackMarshaler` returns. - `HookLevel` - `.Run()` methods. - `LevelSampler` - `.Sample() methods. - `Syslog` - `.Write()`, `.WriteLevel()`, and `.Close()` methods. - `.WriteLevel()` with an `InvalidLevel`. - `Writer` - `.Write()` short write and error cases. - `MultiLevelWriter` `.WriteLevel()` and for `.Write()` error and `.Close()` cases. - test of unmarshalling a level byte returns correct error. - CBOR decodeStream - `.decodeFloat()`, `.binaryFmt()`, `.DecodeIfBinaryToString()`, `.DecodeObjectToStr()`, `.DecodeIfBinaryToBytes()`, `.decodeTagData()`, and `.decodeSimpleFloat()` - handling of invalid UTF-8 sequences - handling of UTC times. - handling of timestamps - handling of various map lengths Restructure `Event` `.caller()` so we test for ok and eliminate untestable coverage hole. Restructure `Context` `.Err()` when the `ErrorStackMarshaler` returns a `nil` so there's code to cover. Inverted logic for`Event` `.Caller()`'s call to `runtime.Caller()` for simpler testing. Inverted logic for `Array` `.putArray()` and `Event` `.putEvent()` so there isn't uncoverable code. Restructure `Field` `.appendFieldList()` to early return when `ErrorStackMarshaler` returns a `nil` Added comments for things we can't get coverage on. Did a go fmt ./... Coverage of core is now 100% on `Array`, `Context`, `Ctx`, `Event`, `Field`, `Hook`, and `Syslog`. Coverage of `Globals`, `Log`, `Sampler`, and `Writer` is almost all except some real edge-cases. JSON encoder coverage is 100% CBOR encoder coverage is 96.3% with base, cbor, string, time and types at 100% and decode_stream (which is lacks coverage on some panic states, and two incorrect coverage-tool lapses) * Fix CBOR tests for StackMarshaler Forgot to use the `decodeIfBinaryToString()`
209 lines
6.4 KiB
Go
209 lines
6.4 KiB
Go
package zerolog
|
|
|
|
import (
|
|
"bytes"
|
|
"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"
|
|
|
|
// TimeFormatUnixNano defines a time format that makes time fields to be
|
|
// serialized as Unix timestamp integers in nanoseconds.
|
|
TimeFormatUnixNano = "UNIXNANO"
|
|
|
|
// DurationFormatFloat defines a format for Duration fields that makes duration fields to be
|
|
// serialized as floating point numbers.
|
|
DurationFormatFloat = "float"
|
|
// DurationFormatInt defines a format for Duration fields that makes duration fields to be
|
|
// serialized as integers.
|
|
DurationFormatInt = "int"
|
|
// DurationFormatString defines a format for Duration fields that makes duration fields to be
|
|
// serialized as string.
|
|
DurationFormatString = "string"
|
|
)
|
|
|
|
var (
|
|
// TimestampFieldName is the field name used for the timestamp field.
|
|
TimestampFieldName = "time"
|
|
|
|
// 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"
|
|
|
|
// ErrorFieldName is the field name used for error fields.
|
|
ErrorFieldName = "error"
|
|
|
|
// CallerFieldName is the field name used for caller field.
|
|
CallerFieldName = "caller"
|
|
|
|
// CallerSkipFrameCount is the number of stack frames to skip to find the caller.
|
|
CallerSkipFrameCount = 2
|
|
|
|
// CallerMarshalFunc allows customization of global caller marshaling
|
|
CallerMarshalFunc = func(pc uintptr, file string, line int) string {
|
|
return file + ":" + strconv.Itoa(line)
|
|
}
|
|
|
|
// ErrorStackFieldName is the field name used for error stacks.
|
|
ErrorStackFieldName = "stack"
|
|
|
|
// ErrorStackMarshaler extract the stack from err if any.
|
|
ErrorStackMarshaler func(err error) interface{}
|
|
|
|
// ErrorMarshalFunc allows customization of global error marshaling
|
|
ErrorMarshalFunc = func(err error) interface{} {
|
|
return err
|
|
}
|
|
|
|
// InterfaceMarshalFunc allows customization of interface marshaling.
|
|
// Default: "encoding/json.Marshal" with disabled HTML escaping
|
|
InterfaceMarshalFunc = func(v interface{}) ([]byte, error) {
|
|
var buf bytes.Buffer
|
|
encoder := json.NewEncoder(&buf)
|
|
encoder.SetEscapeHTML(false)
|
|
err := encoder.Encode(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b := buf.Bytes()
|
|
if len(b) > 0 {
|
|
// Remove trailing \n which is added by Encode.
|
|
return b[:len(b)-1], nil
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// TimeFieldFormat defines the time format of the Time field type. If set to
|
|
// TimeFormatUnix, TimeFormatUnixMs, TimeFormatUnixMicro or TimeFormatUnixNano, the time is formatted as a UNIX
|
|
// timestamp as integer.
|
|
TimeFieldFormat = time.RFC3339
|
|
|
|
// TimestampFunc defines the function called to generate a timestamp.
|
|
TimestampFunc = time.Now
|
|
|
|
// DurationFieldFormat defines the format of the Duration field type.
|
|
DurationFieldFormat = DurationFormatFloat
|
|
|
|
// DurationFieldUnit defines the unit for time.Duration type fields added
|
|
// using the Dur method.
|
|
DurationFieldUnit = time.Millisecond
|
|
|
|
// DurationFieldInteger renders Dur fields as integer instead of float if
|
|
// set to true.
|
|
// Deprecated: use DurationFieldFormat with DurationFormatInt instead.
|
|
DurationFieldInteger = false
|
|
|
|
// ErrorHandler is 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.
|
|
ErrorHandler func(err error)
|
|
|
|
// FatalExitFunc is called by log.Fatal() instead of os.Exit(1). If not set,
|
|
// os.Exit(1) is called.
|
|
FatalExitFunc func()
|
|
|
|
// DefaultContextLogger is returned from Ctx() if there is no logger associated
|
|
// with the context.
|
|
DefaultContextLogger *Logger
|
|
|
|
// LevelColors are used by ConsoleWriter's consoleDefaultFormatLevel to color
|
|
// log levels.
|
|
LevelColors = map[Level]int{
|
|
TraceLevel: colorBlue,
|
|
DebugLevel: 0,
|
|
InfoLevel: colorGreen,
|
|
WarnLevel: colorYellow,
|
|
ErrorLevel: colorRed,
|
|
FatalLevel: colorRed,
|
|
PanicLevel: colorRed,
|
|
}
|
|
|
|
// FormattedLevels are used by ConsoleWriter's consoleDefaultFormatLevel
|
|
// for a short level name.
|
|
FormattedLevels = map[Level]string{
|
|
TraceLevel: "TRC",
|
|
DebugLevel: "DBG",
|
|
InfoLevel: "INF",
|
|
WarnLevel: "WRN",
|
|
ErrorLevel: "ERR",
|
|
FatalLevel: "FTL",
|
|
PanicLevel: "PNC",
|
|
}
|
|
|
|
// TriggerLevelWriterBufferReuseLimit is a limit in bytes that a buffer is dropped
|
|
// from the TriggerLevelWriter buffer pool if the buffer grows above the limit.
|
|
TriggerLevelWriterBufferReuseLimit = 64 * 1024
|
|
|
|
// FloatingPointPrecision, if set to a value other than -1, controls the number
|
|
// of digits when formatting float numbers in JSON. See strconv.FormatFloat for
|
|
// more details.
|
|
FloatingPointPrecision = -1
|
|
)
|
|
|
|
var (
|
|
gLevel = new(int32)
|
|
disableSampling = new(int32)
|
|
)
|
|
|
|
// SetGlobalLevel sets the global override for log level. If this
|
|
// values is raised, all Loggers will use at least this value.
|
|
//
|
|
// To globally disable logs, set GlobalLevel to Disabled.
|
|
func SetGlobalLevel(l Level) {
|
|
atomic.StoreInt32(gLevel, int32(l))
|
|
}
|
|
|
|
// GlobalLevel returns the current global log level
|
|
func GlobalLevel() Level {
|
|
return Level(atomic.LoadInt32(gLevel))
|
|
}
|
|
|
|
// DisableSampling will disable sampling in all Loggers if true.
|
|
func DisableSampling(v bool) {
|
|
var i int32
|
|
if v {
|
|
i = 1
|
|
}
|
|
atomic.StoreInt32(disableSampling, i)
|
|
}
|
|
|
|
func samplingDisabled() bool {
|
|
return atomic.LoadInt32(disableSampling) == 1
|
|
}
|