2017-05-02 16:49:29 -07:00
|
|
|
package log
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
)
|
|
|
|
|
|
|
|
type fieldType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
stringType fieldType = iota
|
|
|
|
boolType
|
|
|
|
intType
|
|
|
|
int32Type
|
|
|
|
uint32Type
|
|
|
|
int64Type
|
|
|
|
uint64Type
|
|
|
|
float32Type
|
|
|
|
float64Type
|
|
|
|
errorType
|
|
|
|
objectType
|
|
|
|
lazyLoggerType
|
2019-08-14 02:00:39 -07:00
|
|
|
noopType
|
2017-05-02 16:49:29 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-10 15:41:20 -07:00
|
|
|
// Error adds an error with the key "error.object" to a Span.LogFields() record
|
2017-05-02 16:49:29 -07:00
|
|
|
func Error(err error) Field {
|
|
|
|
return Field{
|
2020-07-10 15:41:20 -07:00
|
|
|
key: "error.object",
|
2017-05-02 16:49:29 -07:00
|
|
|
fieldType: errorType,
|
|
|
|
interfaceVal: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Object adds an object-valued key:value pair to a Span.LogFields() record
|
2020-07-10 15:41:20 -07:00
|
|
|
// 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.
|
2017-05-02 16:49:29 -07:00
|
|
|
func Object(key string, obj interface{}) Field {
|
|
|
|
return Field{
|
|
|
|
key: key,
|
|
|
|
fieldType: objectType,
|
|
|
|
interfaceVal: obj,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-10 15:41:20 -07:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2017-05-02 16:49:29 -07:00
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-14 02:00:39 -07:00
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 16:49:29 -07:00
|
|
|
// 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))
|
2019-08-14 02:00:39 -07:00
|
|
|
case noopType:
|
|
|
|
// intentionally left blank
|
2017-05-02 16:49:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2019-08-14 02:00:39 -07:00
|
|
|
case noopType:
|
|
|
|
return nil
|
2017-05-02 16:49:29 -07:00
|
|
|
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())
|
|
|
|
}
|