1 package log 2 3 import ( 4 "runtime" 5 "strconv" 6 "strings" 7 "time" 8 ) 9 10 // A Valuer generates a log value. When passed to With, WithPrefix, or 11 // WithSuffix in a value element (odd indexes), it represents a dynamic 12 // value which is re-evaluated with each log event. 13 type Valuer func() interface{} 14 15 // bindValues replaces all value elements (odd indexes) containing a Valuer 16 // with their generated value. 17 func bindValues(keyvals []interface{}) { 18 for i := 1; i < len(keyvals); i += 2 { 19 if v, ok := keyvals[i].(Valuer); ok { 20 keyvals[i] = v() 21 } 22 } 23 } 24 25 // containsValuer returns true if any of the value elements (odd indexes) 26 // contain a Valuer. 27 func containsValuer(keyvals []interface{}) bool { 28 for i := 1; i < len(keyvals); i += 2 { 29 if _, ok := keyvals[i].(Valuer); ok { 30 return true 31 } 32 } 33 return false 34 } 35 36 // Timestamp returns a timestamp Valuer. It invokes the t function to get the 37 // time; unless you are doing something tricky, pass time.Now. 38 // 39 // Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which 40 // are TimestampFormats that use the RFC3339Nano format. 41 func Timestamp(t func() time.Time) Valuer { 42 return func() interface{} { return t() } 43 } 44 45 // TimestampFormat returns a timestamp Valuer with a custom time format. It 46 // invokes the t function to get the time to format; unless you are doing 47 // something tricky, pass time.Now. The layout string is passed to 48 // Time.Format. 49 // 50 // Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which 51 // are TimestampFormats that use the RFC3339Nano format. 52 func TimestampFormat(t func() time.Time, layout string) Valuer { 53 return func() interface{} { 54 return timeFormat{ 55 time: t(), 56 layout: layout, 57 } 58 } 59 } 60 61 // A timeFormat represents an instant in time and a layout used when 62 // marshaling to a text format. 63 type timeFormat struct { 64 time time.Time 65 layout string 66 } 67 68 func (tf timeFormat) String() string { 69 return tf.time.Format(tf.layout) 70 } 71 72 // MarshalText implements encoding.TextMarshaller. 73 func (tf timeFormat) MarshalText() (text []byte, err error) { 74 // The following code adapted from the standard library time.Time.Format 75 // method. Using the same undocumented magic constant to extend the size 76 // of the buffer as seen there. 77 b := make([]byte, 0, len(tf.layout)+10) 78 b = tf.time.AppendFormat(b, tf.layout) 79 return b, nil 80 } 81 82 // Caller returns a Valuer that returns a file and line from a specified depth 83 // in the callstack. Users will probably want to use DefaultCaller. 84 func Caller(depth int) Valuer { 85 return func() interface{} { 86 _, file, line, _ := runtime.Caller(depth) 87 idx := strings.LastIndexByte(file, '/') 88 // using idx+1 below handles both of following cases: 89 // idx == -1 because no "/" was found, or 90 // idx >= 0 and we want to start at the character after the found "/". 91 return file[idx+1:] + ":" + strconv.Itoa(line) 92 } 93 } 94 95 var ( 96 // DefaultTimestamp is a Valuer that returns the current wallclock time, 97 // respecting time zones, when bound. 98 DefaultTimestamp = TimestampFormat(time.Now, time.RFC3339Nano) 99 100 // DefaultTimestampUTC is a Valuer that returns the current time in UTC 101 // when bound. 102 DefaultTimestampUTC = TimestampFormat( 103 func() time.Time { return time.Now().UTC() }, 104 time.RFC3339Nano, 105 ) 106 107 // DefaultCaller is a Valuer that returns the file and line where the Log 108 // method was invoked. It can only be used with log.With. 109 DefaultCaller = Caller(3) 110 ) 111