123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- package log
- import (
- "fmt"
- "math"
- )
- type fieldType int
- const (
- stringType fieldType = iota
- boolType
- intType
- int32Type
- uint32Type
- int64Type
- uint64Type
- float32Type
- float64Type
- errorType
- objectType
- lazyLoggerType
- noopType
- )
- // Field instances are constructed via LogBool, LogString, and so on.
- // Tracing implementations may then handle them via the Field.Marshal
- // method.
- //
- // "heavily influenced by" (i.e., partially stolen from)
- // https://github.com/uber-go/zap
- type Field struct {
- key string
- fieldType fieldType
- numericVal int64
- stringVal string
- interfaceVal interface{}
- }
- // String adds a string-valued key:value pair to a Span.LogFields() record
- func String(key, val string) Field {
- return Field{
- key: key,
- fieldType: stringType,
- stringVal: val,
- }
- }
- // Bool adds a bool-valued key:value pair to a Span.LogFields() record
- func Bool(key string, val bool) Field {
- var numericVal int64
- if val {
- numericVal = 1
- }
- return Field{
- key: key,
- fieldType: boolType,
- numericVal: numericVal,
- }
- }
- // Int adds an int-valued key:value pair to a Span.LogFields() record
- func Int(key string, val int) Field {
- return Field{
- key: key,
- fieldType: intType,
- numericVal: int64(val),
- }
- }
- // Int32 adds an int32-valued key:value pair to a Span.LogFields() record
- func Int32(key string, val int32) Field {
- return Field{
- key: key,
- fieldType: int32Type,
- numericVal: int64(val),
- }
- }
- // Int64 adds an int64-valued key:value pair to a Span.LogFields() record
- func Int64(key string, val int64) Field {
- return Field{
- key: key,
- fieldType: int64Type,
- numericVal: val,
- }
- }
- // Uint32 adds a uint32-valued key:value pair to a Span.LogFields() record
- func Uint32(key string, val uint32) Field {
- return Field{
- key: key,
- fieldType: uint32Type,
- numericVal: int64(val),
- }
- }
- // Uint64 adds a uint64-valued key:value pair to a Span.LogFields() record
- func Uint64(key string, val uint64) Field {
- return Field{
- key: key,
- fieldType: uint64Type,
- numericVal: int64(val),
- }
- }
- // Float32 adds a float32-valued key:value pair to a Span.LogFields() record
- func Float32(key string, val float32) Field {
- return Field{
- key: key,
- fieldType: float32Type,
- numericVal: int64(math.Float32bits(val)),
- }
- }
- // Float64 adds a float64-valued key:value pair to a Span.LogFields() record
- func Float64(key string, val float64) Field {
- return Field{
- key: key,
- fieldType: float64Type,
- numericVal: int64(math.Float64bits(val)),
- }
- }
- // Error adds an error with the key "error.object" to a Span.LogFields() record
- func Error(err error) Field {
- return Field{
- key: "error.object",
- fieldType: errorType,
- interfaceVal: err,
- }
- }
- // Object adds an object-valued key:value pair to a Span.LogFields() record
- // Please pass in an immutable object, otherwise there may be concurrency issues.
- // Such as passing in the map, log.Object may result in "fatal error: concurrent map iteration and map write".
- // Because span is sent asynchronously, it is possible that this map will also be modified.
- func Object(key string, obj interface{}) Field {
- return Field{
- key: key,
- fieldType: objectType,
- interfaceVal: obj,
- }
- }
- // Event creates a string-valued Field for span logs with key="event" and value=val.
- func Event(val string) Field {
- return String("event", val)
- }
- // Message creates a string-valued Field for span logs with key="message" and value=val.
- func Message(val string) Field {
- return String("message", val)
- }
- // LazyLogger allows for user-defined, late-bound logging of arbitrary data
- type LazyLogger func(fv Encoder)
- // Lazy adds a LazyLogger to a Span.LogFields() record; the tracing
- // implementation will call the LazyLogger function at an indefinite time in
- // the future (after Lazy() returns).
- func Lazy(ll LazyLogger) Field {
- return Field{
- fieldType: lazyLoggerType,
- interfaceVal: ll,
- }
- }
- // Noop creates a no-op log field that should be ignored by the tracer.
- // It can be used to capture optional fields, for example those that should
- // only be logged in non-production environment:
- //
- // func customerField(order *Order) log.Field {
- // if os.Getenv("ENVIRONMENT") == "dev" {
- // return log.String("customer", order.Customer.ID)
- // }
- // return log.Noop()
- // }
- //
- // span.LogFields(log.String("event", "purchase"), customerField(order))
- //
- func Noop() Field {
- return Field{
- fieldType: noopType,
- }
- }
- // Encoder allows access to the contents of a Field (via a call to
- // Field.Marshal).
- //
- // Tracer implementations typically provide an implementation of Encoder;
- // OpenTracing callers typically do not need to concern themselves with it.
- type Encoder interface {
- EmitString(key, value string)
- EmitBool(key string, value bool)
- EmitInt(key string, value int)
- EmitInt32(key string, value int32)
- EmitInt64(key string, value int64)
- EmitUint32(key string, value uint32)
- EmitUint64(key string, value uint64)
- EmitFloat32(key string, value float32)
- EmitFloat64(key string, value float64)
- EmitObject(key string, value interface{})
- EmitLazyLogger(value LazyLogger)
- }
- // Marshal passes a Field instance through to the appropriate
- // field-type-specific method of an Encoder.
- func (lf Field) Marshal(visitor Encoder) {
- switch lf.fieldType {
- case stringType:
- visitor.EmitString(lf.key, lf.stringVal)
- case boolType:
- visitor.EmitBool(lf.key, lf.numericVal != 0)
- case intType:
- visitor.EmitInt(lf.key, int(lf.numericVal))
- case int32Type:
- visitor.EmitInt32(lf.key, int32(lf.numericVal))
- case int64Type:
- visitor.EmitInt64(lf.key, int64(lf.numericVal))
- case uint32Type:
- visitor.EmitUint32(lf.key, uint32(lf.numericVal))
- case uint64Type:
- visitor.EmitUint64(lf.key, uint64(lf.numericVal))
- case float32Type:
- visitor.EmitFloat32(lf.key, math.Float32frombits(uint32(lf.numericVal)))
- case float64Type:
- visitor.EmitFloat64(lf.key, math.Float64frombits(uint64(lf.numericVal)))
- case errorType:
- if err, ok := lf.interfaceVal.(error); ok {
- visitor.EmitString(lf.key, err.Error())
- } else {
- visitor.EmitString(lf.key, "<nil>")
- }
- case objectType:
- visitor.EmitObject(lf.key, lf.interfaceVal)
- case lazyLoggerType:
- visitor.EmitLazyLogger(lf.interfaceVal.(LazyLogger))
- case noopType:
- // intentionally left blank
- }
- }
- // Key returns the field's key.
- func (lf Field) Key() string {
- return lf.key
- }
- // Value returns the field's value as interface{}.
- func (lf Field) Value() interface{} {
- switch lf.fieldType {
- case stringType:
- return lf.stringVal
- case boolType:
- return lf.numericVal != 0
- case intType:
- return int(lf.numericVal)
- case int32Type:
- return int32(lf.numericVal)
- case int64Type:
- return int64(lf.numericVal)
- case uint32Type:
- return uint32(lf.numericVal)
- case uint64Type:
- return uint64(lf.numericVal)
- case float32Type:
- return math.Float32frombits(uint32(lf.numericVal))
- case float64Type:
- return math.Float64frombits(uint64(lf.numericVal))
- case errorType, objectType, lazyLoggerType:
- return lf.interfaceVal
- case noopType:
- return nil
- default:
- return nil
- }
- }
- // String returns a string representation of the key and value.
- func (lf Field) String() string {
- return fmt.Sprint(lf.key, ":", lf.Value())
- }
|