-
Notifications
You must be signed in to change notification settings - Fork 0
/
fields.go
198 lines (171 loc) · 5.47 KB
/
fields.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logging
import (
"bytes"
"context"
"fmt"
"sort"
)
const (
// ErrorKey is a logging field key to use for errors.
ErrorKey = "error"
)
// Fields maps string keys to arbitrary values.
//
// Fields can be added to a Context. Fields added to a Context augment those
// in the Context's parent Context, overriding duplicate keys. When Fields are
// added to a Context, they are copied internally for retention.
//
// Fields can also be added directly to a log message by calling its
// logging passthrough methods. This immediate usage avoids the overhead of
// duplicating the fields for retention.
type Fields map[string]interface{}
// NewFields instantiates a new Fields instance by duplicating the supplied map.
func NewFields(v map[string]interface{}) Fields {
fields := make(Fields)
for k, v := range v {
fields[k] = v
}
return fields
}
// WithError returns a Fields instance containing an error.
func WithError(err error) Fields {
return Fields{
ErrorKey: err,
}
}
// Copy returns a copy of this Fields with the keys from other overlaid on top
// of this one's.
func (f Fields) Copy(other Fields) Fields {
if len(f) == 0 && len(other) == 0 {
return nil
}
ret := make(Fields, len(f)+len(other))
for k, v := range f {
ret[k] = v
}
for k, v := range other {
ret[k] = v
}
return ret
}
// SortedEntries processes a Fields object, pruning invisible fields, placing
// the ErrorKey field first, and then sorting the remaining fields by key.
func (f Fields) SortedEntries() (s []*FieldEntry) {
if len(f) == 0 {
return nil
}
s = make([]*FieldEntry, 0, len(f))
for k, v := range f {
s = append(s, &FieldEntry{k, v})
}
sort.Sort(fieldEntrySlice(s))
return
}
// String returns a string describing the contents of f in a sorted,
// dictionary-like format.
func (f Fields) String() string {
b := bytes.Buffer{}
b.WriteRune('{')
for idx, e := range f.SortedEntries() {
if idx > 0 {
b.WriteString(", ")
}
b.WriteString(e.String())
}
b.WriteRune('}')
return b.String()
}
// Debugf is a shorthand method to call the current logger's Errorf method.
func (f Fields) Debugf(c context.Context, fmt string, args ...interface{}) {
Get(SetFields(c, f)).LogCall(Debug, 1, fmt, args)
}
// Infof is a shorthand method to call the current logger's Errorf method.
func (f Fields) Infof(c context.Context, fmt string, args ...interface{}) {
Get(SetFields(c, f)).LogCall(Info, 1, fmt, args)
}
// Warningf is a shorthand method to call the current logger's Errorf method.
func (f Fields) Warningf(c context.Context, fmt string, args ...interface{}) {
Get(SetFields(c, f)).LogCall(Warning, 1, fmt, args)
}
// Errorf is a shorthand method to call the current logger's Errorf method.
func (f Fields) Errorf(c context.Context, fmt string, args ...interface{}) {
Get(SetFields(c, f)).LogCall(Error, 1, fmt, args)
}
// FieldEntry is a static representation of a single key/value entry in a
// Fields.
type FieldEntry struct {
Key string // The field's key.
Value interface{} // The field's value.
}
// String returns the string representation of the field entry:
// "<key>":"<value>".
func (e *FieldEntry) String() string {
value := e.Value
if s, ok := value.(fmt.Stringer); ok {
value = s.String()
}
switch v := value.(type) {
case string:
return fmt.Sprintf("%q:%q", e.Key, v)
case error:
return fmt.Sprintf("%q:%q", e.Key, v.Error())
default:
return fmt.Sprintf("%q:%#v", e.Key, v)
}
}
// fieldEntrySlice is a slice of FieldEntry which implements sort.Interface.
// The error field is placed before any other field; the remaining fields are
// sorted alphabetically.
type fieldEntrySlice []*FieldEntry
var _ sort.Interface = fieldEntrySlice(nil)
func (s fieldEntrySlice) Less(i, j int) bool {
if s[i].Key == ErrorKey {
return s[j].Key != ErrorKey
}
if s[j].Key == ErrorKey {
return false
}
return s[i].Key < s[j].Key
}
func (s fieldEntrySlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s fieldEntrySlice) Len() int {
return len(s)
}
// SetFields adds the additional fields as context for the current Logger. The
// display of these fields depends on the implementation of the Logger. The
// new context will contain the combination of its current Fields, updated with
// the new ones (see Fields.Copy). Specifying the new fields as nil will
// clear the currently set fields.
func SetFields(c context.Context, fields Fields) context.Context {
return context.WithValue(c, fieldsKey, GetFields(c).Copy(fields))
}
// SetField is a convenience method for SetFields for a single key/value
// pair.
func SetField(c context.Context, key string, value interface{}) context.Context {
return SetFields(c, Fields{key: value})
}
// GetFields returns the current Fields.
//
// This method is used for logger implementations with the understanding that
// the returned fields must not be mutated.
func GetFields(c context.Context) Fields {
if ret, ok := c.Value(fieldsKey).(Fields); ok {
return ret
}
return nil
}