Package list golang-github-go-kit-kit / 584a942
log: Add TimestampFormat. - TimestampFormat is a function to produce time Valuers with arbitrary time formats. - The Valuer generates values of the unexported type timeFormat, a value that defers formating timestamps until serialization, which improves performance when a log event is not serialized. - DefaultTimestamp and DefaultTimestampUTC are now created using TimestampFormat. Chris Hines 4 years ago
2 changed file(s) with 88 addition(s) and 11 deletion(s). Raw diff Collapse all Expand all
3131 return false
3232 }
3333
34 // Timestamp returns a Valuer that invokes the underlying function when bound,
35 // returning a time.Time. Users will probably want to use DefaultTimestamp or
36 // DefaultTimestampUTC.
34 // Timestamp returns a timestamp Valuer. It invokes the t function to get the
35 // time; unless you are doing something tricky, pass time.Now.
36 //
37 // Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which
38 // are TimestampFormats that use the RFC3339Nano format.
3739 func Timestamp(t func() time.Time) Valuer {
3840 return func() interface{} { return t() }
41 }
42
43 // TimestampFormat returns a timestamp Valuer with a custom time format. It
44 // invokes the t function to get the time to format; unless you are doing
45 // something tricky, pass time.Now. The layout string is passed to
46 // Time.Format.
47 //
48 // Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which
49 // are TimestampFormats that use the RFC3339Nano format.
50 func TimestampFormat(t func() time.Time, layout string) Valuer {
51 return func() interface{} {
52 return timeFormat{
53 time: t(),
54 layout: layout,
55 }
56 }
57 }
58
59 // A timeFormat represents an instant in time and a layout used when
60 // marshaling to a text format.
61 type timeFormat struct {
62 time time.Time
63 layout string
64 }
65
66 func (tf timeFormat) String() string {
67 return tf.time.Format(tf.layout)
68 }
69
70 // MarshalText implements encoding.TextMarshaller.
71 func (tf timeFormat) MarshalText() (text []byte, err error) {
72 // The following code adapted from the standard library time.Time.Format
73 // method. Using the same undocumented magic constant to extend the size
74 // of the buffer as seen there.
75 b := make([]byte, 0, len(tf.layout)+10)
76 b = tf.time.AppendFormat(b, tf.layout)
77 return b, nil
3978 }
4079
4180 // Caller returns a Valuer that returns a file and line from a specified depth
4786 var (
4887 // DefaultTimestamp is a Valuer that returns the current wallclock time,
4988 // respecting time zones, when bound.
50 DefaultTimestamp = Valuer(func() interface{} {
51 return time.Now().Format(time.RFC3339Nano)
52 })
89 DefaultTimestamp = TimestampFormat(time.Now, time.RFC3339Nano)
5390
5491 // DefaultTimestampUTC is a Valuer that returns the current time in UTC
5592 // when bound.
56 DefaultTimestampUTC = Valuer(func() interface{} {
57 return time.Now().UTC().Format(time.RFC3339Nano)
58 })
93 DefaultTimestampUTC = TimestampFormat(
94 func() time.Time { return time.Now().UTC() },
95 time.RFC3339Nano,
96 )
5997
6098 // DefaultCaller is a Valuer that returns the file and line where the Log
6199 // method was invoked. It can only be used with log.With.
00 package log_test
11
22 import (
3 "encoding"
34 "fmt"
5 "reflect"
46 "testing"
57 "time"
68
3335 if want, have := start.Add(time.Second), timestamp; want != have {
3436 t.Errorf("output[1]: want %v, have %v", want, have)
3537 }
36 if want, have := "value_test.go:29", fmt.Sprint(output[3]); want != have {
38 if want, have := "value_test.go:31", fmt.Sprint(output[3]); want != have {
3739 t.Errorf("output[3]: want %s, have %s", want, have)
3840 }
3941
4648 if want, have := start.Add(2*time.Second), timestamp; want != have {
4749 t.Errorf("output[1]: want %v, have %v", want, have)
4850 }
49 if want, have := "value_test.go:42", fmt.Sprint(output[3]); want != have {
51 if want, have := "value_test.go:44", fmt.Sprint(output[3]); want != have {
5052 t.Errorf("output[3]: want %s, have %s", want, have)
5153 }
5254 }
8991 }
9092 }
9193
94 func TestTimestampFormat(t *testing.T) {
95 t.Parallel()
96
97 start := time.Date(2015, time.April, 25, 0, 0, 0, 0, time.UTC)
98 now := start
99 mocktime := func() time.Time {
100 now = now.Add(time.Second)
101 return now
102 }
103
104 tv := log.TimestampFormat(mocktime, time.RFC822)
105
106 if want, have := now.Add(time.Second).Format(time.RFC822), fmt.Sprint(tv()); want != have {
107 t.Errorf("wrong time format: want %v, have %v", want, have)
108 }
109
110 if want, have := now.Add(2*time.Second).Format(time.RFC822), fmt.Sprint(tv()); want != have {
111 t.Errorf("wrong time format: want %v, have %v", want, have)
112 }
113
114 mustMarshal := func(v interface{}) []byte {
115 b, err := v.(encoding.TextMarshaler).MarshalText()
116 if err != nil {
117 t.Fatal("error marshaling text:", err)
118 }
119 return b
120 }
121
122 if want, have := now.Add(3*time.Second).AppendFormat(nil, time.RFC822), mustMarshal(tv()); !reflect.DeepEqual(want, have) {
123 t.Errorf("wrong time format: want %s, have %s", want, have)
124 }
125
126 if want, have := now.Add(4*time.Second).AppendFormat(nil, time.RFC822), mustMarshal(tv()); !reflect.DeepEqual(want, have) {
127 t.Errorf("wrong time format: want %s, have %s", want, have)
128 }
129 }
130
92131 func BenchmarkValueBindingTimestamp(b *testing.B) {
93132 logger := log.NewNopLogger()
94133 lc := log.With(logger, "ts", log.DefaultTimestamp)