...

Source file src/go.uber.org/zap/zapcore/encoder.go

Documentation: go.uber.org/zap/zapcore

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package zapcore
    22  
    23  import (
    24  	"encoding/json"
    25  	"io"
    26  	"time"
    27  
    28  	"go.uber.org/zap/buffer"
    29  )
    30  
    31  // DefaultLineEnding defines the default line ending when writing logs.
    32  // Alternate line endings specified in EncoderConfig can override this
    33  // behavior.
    34  const DefaultLineEnding = "\n"
    35  
    36  // OmitKey defines the key to use when callers want to remove a key from log output.
    37  const OmitKey = ""
    38  
    39  // A LevelEncoder serializes a Level to a primitive type.
    40  //
    41  // This function must make exactly one call
    42  // to a PrimitiveArrayEncoder's Append* method.
    43  type LevelEncoder func(Level, PrimitiveArrayEncoder)
    44  
    45  // LowercaseLevelEncoder serializes a Level to a lowercase string. For example,
    46  // InfoLevel is serialized to "info".
    47  func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
    48  	enc.AppendString(l.String())
    49  }
    50  
    51  // LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring.
    52  // For example, InfoLevel is serialized to "info" and colored blue.
    53  func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
    54  	s, ok := _levelToLowercaseColorString[l]
    55  	if !ok {
    56  		s = _unknownLevelColor.Add(l.String())
    57  	}
    58  	enc.AppendString(s)
    59  }
    60  
    61  // CapitalLevelEncoder serializes a Level to an all-caps string. For example,
    62  // InfoLevel is serialized to "INFO".
    63  func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
    64  	enc.AppendString(l.CapitalString())
    65  }
    66  
    67  // CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color.
    68  // For example, InfoLevel is serialized to "INFO" and colored blue.
    69  func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
    70  	s, ok := _levelToCapitalColorString[l]
    71  	if !ok {
    72  		s = _unknownLevelColor.Add(l.CapitalString())
    73  	}
    74  	enc.AppendString(s)
    75  }
    76  
    77  // UnmarshalText unmarshals text to a LevelEncoder. "capital" is unmarshaled to
    78  // CapitalLevelEncoder, "coloredCapital" is unmarshaled to CapitalColorLevelEncoder,
    79  // "colored" is unmarshaled to LowercaseColorLevelEncoder, and anything else
    80  // is unmarshaled to LowercaseLevelEncoder.
    81  func (e *LevelEncoder) UnmarshalText(text []byte) error {
    82  	switch string(text) {
    83  	case "capital":
    84  		*e = CapitalLevelEncoder
    85  	case "capitalColor":
    86  		*e = CapitalColorLevelEncoder
    87  	case "color":
    88  		*e = LowercaseColorLevelEncoder
    89  	default:
    90  		*e = LowercaseLevelEncoder
    91  	}
    92  	return nil
    93  }
    94  
    95  // A TimeEncoder serializes a time.Time to a primitive type.
    96  //
    97  // This function must make exactly one call
    98  // to a PrimitiveArrayEncoder's Append* method.
    99  type TimeEncoder func(time.Time, PrimitiveArrayEncoder)
   100  
   101  // EpochTimeEncoder serializes a time.Time to a floating-point number of seconds
   102  // since the Unix epoch.
   103  func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
   104  	nanos := t.UnixNano()
   105  	sec := float64(nanos) / float64(time.Second)
   106  	enc.AppendFloat64(sec)
   107  }
   108  
   109  // EpochMillisTimeEncoder serializes a time.Time to a floating-point number of
   110  // milliseconds since the Unix epoch.
   111  func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
   112  	nanos := t.UnixNano()
   113  	millis := float64(nanos) / float64(time.Millisecond)
   114  	enc.AppendFloat64(millis)
   115  }
   116  
   117  // EpochNanosTimeEncoder serializes a time.Time to an integer number of
   118  // nanoseconds since the Unix epoch.
   119  func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
   120  	enc.AppendInt64(t.UnixNano())
   121  }
   122  
   123  func encodeTimeLayout(t time.Time, layout string, enc PrimitiveArrayEncoder) {
   124  	type appendTimeEncoder interface {
   125  		AppendTimeLayout(time.Time, string)
   126  	}
   127  
   128  	if enc, ok := enc.(appendTimeEncoder); ok {
   129  		enc.AppendTimeLayout(t, layout)
   130  		return
   131  	}
   132  
   133  	enc.AppendString(t.Format(layout))
   134  }
   135  
   136  // ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string
   137  // with millisecond precision.
   138  //
   139  // If enc supports AppendTimeLayout(t time.Time,layout string), it's used
   140  // instead of appending a pre-formatted string value.
   141  func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
   142  	encodeTimeLayout(t, "2006-01-02T15:04:05.000Z0700", enc)
   143  }
   144  
   145  // RFC3339TimeEncoder serializes a time.Time to an RFC3339-formatted string.
   146  //
   147  // If enc supports AppendTimeLayout(t time.Time,layout string), it's used
   148  // instead of appending a pre-formatted string value.
   149  func RFC3339TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
   150  	encodeTimeLayout(t, time.RFC3339, enc)
   151  }
   152  
   153  // RFC3339NanoTimeEncoder serializes a time.Time to an RFC3339-formatted string
   154  // with nanosecond precision.
   155  //
   156  // If enc supports AppendTimeLayout(t time.Time,layout string), it's used
   157  // instead of appending a pre-formatted string value.
   158  func RFC3339NanoTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
   159  	encodeTimeLayout(t, time.RFC3339Nano, enc)
   160  }
   161  
   162  // TimeEncoderOfLayout returns TimeEncoder which serializes a time.Time using
   163  // given layout.
   164  func TimeEncoderOfLayout(layout string) TimeEncoder {
   165  	return func(t time.Time, enc PrimitiveArrayEncoder) {
   166  		encodeTimeLayout(t, layout, enc)
   167  	}
   168  }
   169  
   170  // UnmarshalText unmarshals text to a TimeEncoder.
   171  // "rfc3339nano" and "RFC3339Nano" are unmarshaled to RFC3339NanoTimeEncoder.
   172  // "rfc3339" and "RFC3339" are unmarshaled to RFC3339TimeEncoder.
   173  // "iso8601" and "ISO8601" are unmarshaled to ISO8601TimeEncoder.
   174  // "millis" is unmarshaled to EpochMillisTimeEncoder.
   175  // "nanos" is unmarshaled to EpochNanosEncoder.
   176  // Anything else is unmarshaled to EpochTimeEncoder.
   177  func (e *TimeEncoder) UnmarshalText(text []byte) error {
   178  	switch string(text) {
   179  	case "rfc3339nano", "RFC3339Nano":
   180  		*e = RFC3339NanoTimeEncoder
   181  	case "rfc3339", "RFC3339":
   182  		*e = RFC3339TimeEncoder
   183  	case "iso8601", "ISO8601":
   184  		*e = ISO8601TimeEncoder
   185  	case "millis":
   186  		*e = EpochMillisTimeEncoder
   187  	case "nanos":
   188  		*e = EpochNanosTimeEncoder
   189  	default:
   190  		*e = EpochTimeEncoder
   191  	}
   192  	return nil
   193  }
   194  
   195  // UnmarshalYAML unmarshals YAML to a TimeEncoder.
   196  // If value is an object with a "layout" field, it will be unmarshaled to  TimeEncoder with given layout.
   197  //
   198  //	timeEncoder:
   199  //	  layout: 06/01/02 03:04pm
   200  //
   201  // If value is string, it uses UnmarshalText.
   202  //
   203  //	timeEncoder: iso8601
   204  func (e *TimeEncoder) UnmarshalYAML(unmarshal func(interface{}) error) error {
   205  	var o struct {
   206  		Layout string `json:"layout" yaml:"layout"`
   207  	}
   208  	if err := unmarshal(&o); err == nil {
   209  		*e = TimeEncoderOfLayout(o.Layout)
   210  		return nil
   211  	}
   212  
   213  	var s string
   214  	if err := unmarshal(&s); err != nil {
   215  		return err
   216  	}
   217  	return e.UnmarshalText([]byte(s))
   218  }
   219  
   220  // UnmarshalJSON unmarshals JSON to a TimeEncoder as same way UnmarshalYAML does.
   221  func (e *TimeEncoder) UnmarshalJSON(data []byte) error {
   222  	return e.UnmarshalYAML(func(v interface{}) error {
   223  		return json.Unmarshal(data, v)
   224  	})
   225  }
   226  
   227  // A DurationEncoder serializes a time.Duration to a primitive type.
   228  //
   229  // This function must make exactly one call
   230  // to a PrimitiveArrayEncoder's Append* method.
   231  type DurationEncoder func(time.Duration, PrimitiveArrayEncoder)
   232  
   233  // SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed.
   234  func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
   235  	enc.AppendFloat64(float64(d) / float64(time.Second))
   236  }
   237  
   238  // NanosDurationEncoder serializes a time.Duration to an integer number of
   239  // nanoseconds elapsed.
   240  func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
   241  	enc.AppendInt64(int64(d))
   242  }
   243  
   244  // MillisDurationEncoder serializes a time.Duration to an integer number of
   245  // milliseconds elapsed.
   246  func MillisDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
   247  	enc.AppendInt64(d.Nanoseconds() / 1e6)
   248  }
   249  
   250  // StringDurationEncoder serializes a time.Duration using its built-in String
   251  // method.
   252  func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
   253  	enc.AppendString(d.String())
   254  }
   255  
   256  // UnmarshalText unmarshals text to a DurationEncoder. "string" is unmarshaled
   257  // to StringDurationEncoder, and anything else is unmarshaled to
   258  // NanosDurationEncoder.
   259  func (e *DurationEncoder) UnmarshalText(text []byte) error {
   260  	switch string(text) {
   261  	case "string":
   262  		*e = StringDurationEncoder
   263  	case "nanos":
   264  		*e = NanosDurationEncoder
   265  	case "ms":
   266  		*e = MillisDurationEncoder
   267  	default:
   268  		*e = SecondsDurationEncoder
   269  	}
   270  	return nil
   271  }
   272  
   273  // A CallerEncoder serializes an EntryCaller to a primitive type.
   274  //
   275  // This function must make exactly one call
   276  // to a PrimitiveArrayEncoder's Append* method.
   277  type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder)
   278  
   279  // FullCallerEncoder serializes a caller in /full/path/to/package/file:line
   280  // format.
   281  func FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
   282  	// TODO: consider using a byte-oriented API to save an allocation.
   283  	enc.AppendString(caller.String())
   284  }
   285  
   286  // ShortCallerEncoder serializes a caller in package/file:line format, trimming
   287  // all but the final directory from the full path.
   288  func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
   289  	// TODO: consider using a byte-oriented API to save an allocation.
   290  	enc.AppendString(caller.TrimmedPath())
   291  }
   292  
   293  // UnmarshalText unmarshals text to a CallerEncoder. "full" is unmarshaled to
   294  // FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder.
   295  func (e *CallerEncoder) UnmarshalText(text []byte) error {
   296  	switch string(text) {
   297  	case "full":
   298  		*e = FullCallerEncoder
   299  	default:
   300  		*e = ShortCallerEncoder
   301  	}
   302  	return nil
   303  }
   304  
   305  // A NameEncoder serializes a period-separated logger name to a primitive
   306  // type.
   307  //
   308  // This function must make exactly one call
   309  // to a PrimitiveArrayEncoder's Append* method.
   310  type NameEncoder func(string, PrimitiveArrayEncoder)
   311  
   312  // FullNameEncoder serializes the logger name as-is.
   313  func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
   314  	enc.AppendString(loggerName)
   315  }
   316  
   317  // UnmarshalText unmarshals text to a NameEncoder. Currently, everything is
   318  // unmarshaled to FullNameEncoder.
   319  func (e *NameEncoder) UnmarshalText(text []byte) error {
   320  	switch string(text) {
   321  	case "full":
   322  		*e = FullNameEncoder
   323  	default:
   324  		*e = FullNameEncoder
   325  	}
   326  	return nil
   327  }
   328  
   329  // An EncoderConfig allows users to configure the concrete encoders supplied by
   330  // zapcore.
   331  type EncoderConfig struct {
   332  	// Set the keys used for each log entry. If any key is empty, that portion
   333  	// of the entry is omitted.
   334  	MessageKey     string `json:"messageKey" yaml:"messageKey"`
   335  	LevelKey       string `json:"levelKey" yaml:"levelKey"`
   336  	TimeKey        string `json:"timeKey" yaml:"timeKey"`
   337  	NameKey        string `json:"nameKey" yaml:"nameKey"`
   338  	CallerKey      string `json:"callerKey" yaml:"callerKey"`
   339  	FunctionKey    string `json:"functionKey" yaml:"functionKey"`
   340  	StacktraceKey  string `json:"stacktraceKey" yaml:"stacktraceKey"`
   341  	SkipLineEnding bool   `json:"skipLineEnding" yaml:"skipLineEnding"`
   342  	LineEnding     string `json:"lineEnding" yaml:"lineEnding"`
   343  	// Configure the primitive representations of common complex types. For
   344  	// example, some users may want all time.Times serialized as floating-point
   345  	// seconds since epoch, while others may prefer ISO8601 strings.
   346  	EncodeLevel    LevelEncoder    `json:"levelEncoder" yaml:"levelEncoder"`
   347  	EncodeTime     TimeEncoder     `json:"timeEncoder" yaml:"timeEncoder"`
   348  	EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
   349  	EncodeCaller   CallerEncoder   `json:"callerEncoder" yaml:"callerEncoder"`
   350  	// Unlike the other primitive type encoders, EncodeName is optional. The
   351  	// zero value falls back to FullNameEncoder.
   352  	EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
   353  	// Configure the encoder for interface{} type objects.
   354  	// If not provided, objects are encoded using json.Encoder
   355  	NewReflectedEncoder func(io.Writer) ReflectedEncoder `json:"-" yaml:"-"`
   356  	// Configures the field separator used by the console encoder. Defaults
   357  	// to tab.
   358  	ConsoleSeparator string `json:"consoleSeparator" yaml:"consoleSeparator"`
   359  }
   360  
   361  // ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a
   362  // map- or struct-like object to the logging context. Like maps, ObjectEncoders
   363  // aren't safe for concurrent use (though typical use shouldn't require locks).
   364  type ObjectEncoder interface {
   365  	// Logging-specific marshalers.
   366  	AddArray(key string, marshaler ArrayMarshaler) error
   367  	AddObject(key string, marshaler ObjectMarshaler) error
   368  
   369  	// Built-in types.
   370  	AddBinary(key string, value []byte)     // for arbitrary bytes
   371  	AddByteString(key string, value []byte) // for UTF-8 encoded bytes
   372  	AddBool(key string, value bool)
   373  	AddComplex128(key string, value complex128)
   374  	AddComplex64(key string, value complex64)
   375  	AddDuration(key string, value time.Duration)
   376  	AddFloat64(key string, value float64)
   377  	AddFloat32(key string, value float32)
   378  	AddInt(key string, value int)
   379  	AddInt64(key string, value int64)
   380  	AddInt32(key string, value int32)
   381  	AddInt16(key string, value int16)
   382  	AddInt8(key string, value int8)
   383  	AddString(key, value string)
   384  	AddTime(key string, value time.Time)
   385  	AddUint(key string, value uint)
   386  	AddUint64(key string, value uint64)
   387  	AddUint32(key string, value uint32)
   388  	AddUint16(key string, value uint16)
   389  	AddUint8(key string, value uint8)
   390  	AddUintptr(key string, value uintptr)
   391  
   392  	// AddReflected uses reflection to serialize arbitrary objects, so it can be
   393  	// slow and allocation-heavy.
   394  	AddReflected(key string, value interface{}) error
   395  	// OpenNamespace opens an isolated namespace where all subsequent fields will
   396  	// be added. Applications can use namespaces to prevent key collisions when
   397  	// injecting loggers into sub-components or third-party libraries.
   398  	OpenNamespace(key string)
   399  }
   400  
   401  // ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding
   402  // array-like objects to the logging context. Of note, it supports mixed-type
   403  // arrays even though they aren't typical in Go. Like slices, ArrayEncoders
   404  // aren't safe for concurrent use (though typical use shouldn't require locks).
   405  type ArrayEncoder interface {
   406  	// Built-in types.
   407  	PrimitiveArrayEncoder
   408  
   409  	// Time-related types.
   410  	AppendDuration(time.Duration)
   411  	AppendTime(time.Time)
   412  
   413  	// Logging-specific marshalers.
   414  	AppendArray(ArrayMarshaler) error
   415  	AppendObject(ObjectMarshaler) error
   416  
   417  	// AppendReflected uses reflection to serialize arbitrary objects, so it's
   418  	// slow and allocation-heavy.
   419  	AppendReflected(value interface{}) error
   420  }
   421  
   422  // PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals
   423  // only in Go's built-in types. It's included only so that Duration- and
   424  // TimeEncoders cannot trigger infinite recursion.
   425  type PrimitiveArrayEncoder interface {
   426  	// Built-in types.
   427  	AppendBool(bool)
   428  	AppendByteString([]byte) // for UTF-8 encoded bytes
   429  	AppendComplex128(complex128)
   430  	AppendComplex64(complex64)
   431  	AppendFloat64(float64)
   432  	AppendFloat32(float32)
   433  	AppendInt(int)
   434  	AppendInt64(int64)
   435  	AppendInt32(int32)
   436  	AppendInt16(int16)
   437  	AppendInt8(int8)
   438  	AppendString(string)
   439  	AppendUint(uint)
   440  	AppendUint64(uint64)
   441  	AppendUint32(uint32)
   442  	AppendUint16(uint16)
   443  	AppendUint8(uint8)
   444  	AppendUintptr(uintptr)
   445  }
   446  
   447  // Encoder is a format-agnostic interface for all log entry marshalers. Since
   448  // log encoders don't need to support the same wide range of use cases as
   449  // general-purpose marshalers, it's possible to make them faster and
   450  // lower-allocation.
   451  //
   452  // Implementations of the ObjectEncoder interface's methods can, of course,
   453  // freely modify the receiver. However, the Clone and EncodeEntry methods will
   454  // be called concurrently and shouldn't modify the receiver.
   455  type Encoder interface {
   456  	ObjectEncoder
   457  
   458  	// Clone copies the encoder, ensuring that adding fields to the copy doesn't
   459  	// affect the original.
   460  	Clone() Encoder
   461  
   462  	// EncodeEntry encodes an entry and fields, along with any accumulated
   463  	// context, into a byte buffer and returns it. Any fields that are empty,
   464  	// including fields on the `Entry` type, should be omitted.
   465  	EncodeEntry(Entry, []Field) (*buffer.Buffer, error)
   466  }
   467  

View as plain text