123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- // Copyright (c) 2016 Uber Technologies, Inc.
- //
- // 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.
- package zapcore
- import (
- "time"
- "go.uber.org/zap/buffer"
- )
- // DefaultLineEnding defines the default line ending when writing logs.
- // Alternate line endings specified in EncoderConfig can override this
- // behavior.
- const DefaultLineEnding = "\n"
- // OmitKey defines the key to use when callers want to remove a key from log output.
- const OmitKey = ""
- // A LevelEncoder serializes a Level to a primitive type.
- type LevelEncoder func(Level, PrimitiveArrayEncoder)
- // LowercaseLevelEncoder serializes a Level to a lowercase string. For example,
- // InfoLevel is serialized to "info".
- func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
- enc.AppendString(l.String())
- }
- // LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring.
- // For example, InfoLevel is serialized to "info" and colored blue.
- func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
- s, ok := _levelToLowercaseColorString[l]
- if !ok {
- s = _unknownLevelColor.Add(l.String())
- }
- enc.AppendString(s)
- }
- // CapitalLevelEncoder serializes a Level to an all-caps string. For example,
- // InfoLevel is serialized to "INFO".
- func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
- enc.AppendString(l.CapitalString())
- }
- // CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color.
- // For example, InfoLevel is serialized to "INFO" and colored blue.
- func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
- s, ok := _levelToCapitalColorString[l]
- if !ok {
- s = _unknownLevelColor.Add(l.CapitalString())
- }
- enc.AppendString(s)
- }
- // UnmarshalText unmarshals text to a LevelEncoder. "capital" is unmarshaled to
- // CapitalLevelEncoder, "coloredCapital" is unmarshaled to CapitalColorLevelEncoder,
- // "colored" is unmarshaled to LowercaseColorLevelEncoder, and anything else
- // is unmarshaled to LowercaseLevelEncoder.
- func (e *LevelEncoder) UnmarshalText(text []byte) error {
- switch string(text) {
- case "capital":
- *e = CapitalLevelEncoder
- case "capitalColor":
- *e = CapitalColorLevelEncoder
- case "color":
- *e = LowercaseColorLevelEncoder
- default:
- *e = LowercaseLevelEncoder
- }
- return nil
- }
- // A TimeEncoder serializes a time.Time to a primitive type.
- type TimeEncoder func(time.Time, PrimitiveArrayEncoder)
- // EpochTimeEncoder serializes a time.Time to a floating-point number of seconds
- // since the Unix epoch.
- func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
- nanos := t.UnixNano()
- sec := float64(nanos) / float64(time.Second)
- enc.AppendFloat64(sec)
- }
- // EpochMillisTimeEncoder serializes a time.Time to a floating-point number of
- // milliseconds since the Unix epoch.
- func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
- nanos := t.UnixNano()
- millis := float64(nanos) / float64(time.Millisecond)
- enc.AppendFloat64(millis)
- }
- // EpochNanosTimeEncoder serializes a time.Time to an integer number of
- // nanoseconds since the Unix epoch.
- func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
- enc.AppendInt64(t.UnixNano())
- }
- func encodeTimeLayout(t time.Time, layout string, enc PrimitiveArrayEncoder) {
- type appendTimeEncoder interface {
- AppendTimeLayout(time.Time, string)
- }
- if enc, ok := enc.(appendTimeEncoder); ok {
- enc.AppendTimeLayout(t, layout)
- return
- }
- enc.AppendString(t.Format(layout))
- }
- // ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string
- // with millisecond precision.
- //
- // If enc supports AppendTimeLayout(t time.Time,layout string), it's used
- // instead of appending a pre-formatted string value.
- func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
- encodeTimeLayout(t, "2006-01-02T15:04:05.000Z0700", enc)
- }
- // RFC3339TimeEncoder serializes a time.Time to an RFC3339-formatted string.
- //
- // If enc supports AppendTimeLayout(t time.Time,layout string), it's used
- // instead of appending a pre-formatted string value.
- func RFC3339TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
- encodeTimeLayout(t, time.RFC3339, enc)
- }
- // RFC3339NanoTimeEncoder serializes a time.Time to an RFC3339-formatted string
- // with nanosecond precision.
- //
- // If enc supports AppendTimeLayout(t time.Time,layout string), it's used
- // instead of appending a pre-formatted string value.
- func RFC3339NanoTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
- encodeTimeLayout(t, time.RFC3339Nano, enc)
- }
- // UnmarshalText unmarshals text to a TimeEncoder.
- // "rfc3339nano" and "RFC3339Nano" are unmarshaled to RFC3339NanoTimeEncoder.
- // "rfc3339" and "RFC3339" are unmarshaled to RFC3339TimeEncoder.
- // "iso8601" and "ISO8601" are unmarshaled to ISO8601TimeEncoder.
- // "millis" is unmarshaled to EpochMillisTimeEncoder.
- // "nanos" is unmarshaled to EpochNanosEncoder.
- // Anything else is unmarshaled to EpochTimeEncoder.
- func (e *TimeEncoder) UnmarshalText(text []byte) error {
- switch string(text) {
- case "rfc3339nano", "RFC3339Nano":
- *e = RFC3339NanoTimeEncoder
- case "rfc3339", "RFC3339":
- *e = RFC3339TimeEncoder
- case "iso8601", "ISO8601":
- *e = ISO8601TimeEncoder
- case "millis":
- *e = EpochMillisTimeEncoder
- case "nanos":
- *e = EpochNanosTimeEncoder
- default:
- *e = EpochTimeEncoder
- }
- return nil
- }
- // A DurationEncoder serializes a time.Duration to a primitive type.
- type DurationEncoder func(time.Duration, PrimitiveArrayEncoder)
- // SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed.
- func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
- enc.AppendFloat64(float64(d) / float64(time.Second))
- }
- // NanosDurationEncoder serializes a time.Duration to an integer number of
- // nanoseconds elapsed.
- func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
- enc.AppendInt64(int64(d))
- }
- // MillisDurationEncoder serializes a time.Duration to an integer number of
- // milliseconds elapsed.
- func MillisDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
- enc.AppendInt64(d.Nanoseconds() / 1e6)
- }
- // StringDurationEncoder serializes a time.Duration using its built-in String
- // method.
- func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
- enc.AppendString(d.String())
- }
- // UnmarshalText unmarshals text to a DurationEncoder. "string" is unmarshaled
- // to StringDurationEncoder, and anything else is unmarshaled to
- // NanosDurationEncoder.
- func (e *DurationEncoder) UnmarshalText(text []byte) error {
- switch string(text) {
- case "string":
- *e = StringDurationEncoder
- case "nanos":
- *e = NanosDurationEncoder
- case "ms":
- *e = MillisDurationEncoder
- default:
- *e = SecondsDurationEncoder
- }
- return nil
- }
- // A CallerEncoder serializes an EntryCaller to a primitive type.
- type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder)
- // FullCallerEncoder serializes a caller in /full/path/to/package/file:line
- // format.
- func FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
- // TODO: consider using a byte-oriented API to save an allocation.
- enc.AppendString(caller.String())
- }
- // ShortCallerEncoder serializes a caller in package/file:line format, trimming
- // all but the final directory from the full path.
- func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
- // TODO: consider using a byte-oriented API to save an allocation.
- enc.AppendString(caller.TrimmedPath())
- }
- // UnmarshalText unmarshals text to a CallerEncoder. "full" is unmarshaled to
- // FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder.
- func (e *CallerEncoder) UnmarshalText(text []byte) error {
- switch string(text) {
- case "full":
- *e = FullCallerEncoder
- default:
- *e = ShortCallerEncoder
- }
- return nil
- }
- // A NameEncoder serializes a period-separated logger name to a primitive
- // type.
- type NameEncoder func(string, PrimitiveArrayEncoder)
- // FullNameEncoder serializes the logger name as-is.
- func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
- enc.AppendString(loggerName)
- }
- // UnmarshalText unmarshals text to a NameEncoder. Currently, everything is
- // unmarshaled to FullNameEncoder.
- func (e *NameEncoder) UnmarshalText(text []byte) error {
- switch string(text) {
- case "full":
- *e = FullNameEncoder
- default:
- *e = FullNameEncoder
- }
- return nil
- }
- // An EncoderConfig allows users to configure the concrete encoders supplied by
- // zapcore.
- type EncoderConfig struct {
- // Set the keys used for each log entry. If any key is empty, that portion
- // of the entry is omitted.
- MessageKey string `json:"messageKey" yaml:"messageKey"`
- LevelKey string `json:"levelKey" yaml:"levelKey"`
- TimeKey string `json:"timeKey" yaml:"timeKey"`
- NameKey string `json:"nameKey" yaml:"nameKey"`
- CallerKey string `json:"callerKey" yaml:"callerKey"`
- StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
- LineEnding string `json:"lineEnding" yaml:"lineEnding"`
- // Configure the primitive representations of common complex types. For
- // example, some users may want all time.Times serialized as floating-point
- // seconds since epoch, while others may prefer ISO8601 strings.
- EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"`
- EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"`
- EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
- EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"`
- // Unlike the other primitive type encoders, EncodeName is optional. The
- // zero value falls back to FullNameEncoder.
- EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
- }
- // ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a
- // map- or struct-like object to the logging context. Like maps, ObjectEncoders
- // aren't safe for concurrent use (though typical use shouldn't require locks).
- type ObjectEncoder interface {
- // Logging-specific marshalers.
- AddArray(key string, marshaler ArrayMarshaler) error
- AddObject(key string, marshaler ObjectMarshaler) error
- // Built-in types.
- AddBinary(key string, value []byte) // for arbitrary bytes
- AddByteString(key string, value []byte) // for UTF-8 encoded bytes
- AddBool(key string, value bool)
- AddComplex128(key string, value complex128)
- AddComplex64(key string, value complex64)
- AddDuration(key string, value time.Duration)
- AddFloat64(key string, value float64)
- AddFloat32(key string, value float32)
- AddInt(key string, value int)
- AddInt64(key string, value int64)
- AddInt32(key string, value int32)
- AddInt16(key string, value int16)
- AddInt8(key string, value int8)
- AddString(key, value string)
- AddTime(key string, value time.Time)
- AddUint(key string, value uint)
- AddUint64(key string, value uint64)
- AddUint32(key string, value uint32)
- AddUint16(key string, value uint16)
- AddUint8(key string, value uint8)
- AddUintptr(key string, value uintptr)
- // AddReflected uses reflection to serialize arbitrary objects, so it can be
- // slow and allocation-heavy.
- AddReflected(key string, value interface{}) error
- // OpenNamespace opens an isolated namespace where all subsequent fields will
- // be added. Applications can use namespaces to prevent key collisions when
- // injecting loggers into sub-components or third-party libraries.
- OpenNamespace(key string)
- }
- // ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding
- // array-like objects to the logging context. Of note, it supports mixed-type
- // arrays even though they aren't typical in Go. Like slices, ArrayEncoders
- // aren't safe for concurrent use (though typical use shouldn't require locks).
- type ArrayEncoder interface {
- // Built-in types.
- PrimitiveArrayEncoder
- // Time-related types.
- AppendDuration(time.Duration)
- AppendTime(time.Time)
- // Logging-specific marshalers.
- AppendArray(ArrayMarshaler) error
- AppendObject(ObjectMarshaler) error
- // AppendReflected uses reflection to serialize arbitrary objects, so it's
- // slow and allocation-heavy.
- AppendReflected(value interface{}) error
- }
- // PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals
- // only in Go's built-in types. It's included only so that Duration- and
- // TimeEncoders cannot trigger infinite recursion.
- type PrimitiveArrayEncoder interface {
- // Built-in types.
- AppendBool(bool)
- AppendByteString([]byte) // for UTF-8 encoded bytes
- AppendComplex128(complex128)
- AppendComplex64(complex64)
- AppendFloat64(float64)
- AppendFloat32(float32)
- AppendInt(int)
- AppendInt64(int64)
- AppendInt32(int32)
- AppendInt16(int16)
- AppendInt8(int8)
- AppendString(string)
- AppendUint(uint)
- AppendUint64(uint64)
- AppendUint32(uint32)
- AppendUint16(uint16)
- AppendUint8(uint8)
- AppendUintptr(uintptr)
- }
- // Encoder is a format-agnostic interface for all log entry marshalers. Since
- // log encoders don't need to support the same wide range of use cases as
- // general-purpose marshalers, it's possible to make them faster and
- // lower-allocation.
- //
- // Implementations of the ObjectEncoder interface's methods can, of course,
- // freely modify the receiver. However, the Clone and EncodeEntry methods will
- // be called concurrently and shouldn't modify the receiver.
- type Encoder interface {
- ObjectEncoder
- // Clone copies the encoder, ensuring that adding fields to the copy doesn't
- // affect the original.
- Clone() Encoder
- // EncodeEntry encodes an entry and fields, along with any accumulated
- // context, into a byte buffer and returns it. Any fields that are empty,
- // including fields on the `Entry` type, should be omitted.
- EncodeEntry(Entry, []Field) (*buffer.Buffer, error)
- }
|