• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-logfmt / logfmt / 18261386991

05 Oct 2025 04:30PM UTC coverage: 84.512%. Remained the same
18261386991

push

github

web-flow
Modernize code through Go 1.21 (#19)

* Go 1.18 updates

* Go 1.19 updates

* Stop using reflect.DeepEqual for testing error values

* go get github.com/google/go-cmp/cmp@latest

10 of 11 new or added lines in 2 files covered. (90.91%)

502 of 594 relevant lines covered (84.51%)

5.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.34
/encode.go
1
package logfmt
2

3
import (
4
        "bytes"
5
        "encoding"
6
        "errors"
7
        "fmt"
8
        "io"
9
        "reflect"
10
        "strings"
11
        "unicode/utf8"
12
)
13

14
// MarshalKeyvals returns the logfmt encoding of keyvals, a variadic sequence
15
// of alternating keys and values.
16
func MarshalKeyvals(keyvals ...any) ([]byte, error) {
6✔
17
        buf := &bytes.Buffer{}
6✔
18
        if err := NewEncoder(buf).EncodeKeyvals(keyvals...); err != nil {
12✔
19
                return nil, err
6✔
20
        }
6✔
21
        return buf.Bytes(), nil
6✔
22
}
23

24
// An Encoder writes logfmt data to an output stream.
25
type Encoder struct {
26
        w       io.Writer
27
        scratch bytes.Buffer
28
        needSep bool
29
}
30

31
// NewEncoder returns a new encoder that writes to w.
32
func NewEncoder(w io.Writer) *Encoder {
6✔
33
        return &Encoder{
6✔
34
                w: w,
6✔
35
        }
6✔
36
}
6✔
37

38
var (
39
        space   = []byte(" ")
40
        equals  = []byte("=")
41
        newline = []byte("\n")
42
        null    = []byte("null")
43
)
44

45
// EncodeKeyval writes the logfmt encoding of key and value to the stream. A
46
// single space is written before the second and subsequent keys in a record.
47
// Nothing is written if a non-nil error is returned.
48
func (enc *Encoder) EncodeKeyval(key, value any) error {
6✔
49
        enc.scratch.Reset()
6✔
50
        if enc.needSep {
12✔
51
                if _, err := enc.scratch.Write(space); err != nil {
6✔
52
                        return err
×
53
                }
×
54
        }
55
        if err := writeKey(&enc.scratch, key); err != nil {
12✔
56
                return err
6✔
57
        }
6✔
58
        if _, err := enc.scratch.Write(equals); err != nil {
6✔
59
                return err
×
60
        }
×
61
        if err := writeValue(&enc.scratch, value); err != nil {
12✔
62
                return err
6✔
63
        }
6✔
64
        _, err := enc.w.Write(enc.scratch.Bytes())
6✔
65
        enc.needSep = true
6✔
66
        return err
6✔
67
}
68

69
// EncodeKeyvals writes the logfmt encoding of keyvals to the stream. Keyvals
70
// is a variadic sequence of alternating keys and values. Keys of unsupported
71
// type are skipped along with their corresponding value. Values of
72
// unsupported type or that cause a MarshalerError are replaced by their error
73
// but do not cause EncodeKeyvals to return an error. If a non-nil error is
74
// returned some key/value pairs may not have be written.
75
func (enc *Encoder) EncodeKeyvals(keyvals ...any) error {
6✔
76
        if len(keyvals) == 0 {
12✔
77
                return nil
6✔
78
        }
6✔
79
        if len(keyvals)%2 == 1 {
12✔
80
                keyvals = append(keyvals, nil)
6✔
81
        }
6✔
82
        for i := 0; i < len(keyvals); i += 2 {
12✔
83
                k, v := keyvals[i], keyvals[i+1]
6✔
84
                err := enc.EncodeKeyval(k, v)
6✔
85
                if err == ErrUnsupportedKeyType {
12✔
86
                        continue
6✔
87
                }
88
                if _, ok := err.(*MarshalerError); ok || err == ErrUnsupportedValueType {
12✔
89
                        v = err
6✔
90
                        err = enc.EncodeKeyval(k, v)
6✔
91
                }
6✔
92
                if err != nil {
12✔
93
                        return err
6✔
94
                }
6✔
95
        }
96
        return nil
6✔
97
}
98

99
// MarshalerError represents an error encountered while marshaling a value.
100
type MarshalerError struct {
101
        Type reflect.Type
102
        Err  error
103
}
104

105
func (e *MarshalerError) Error() string {
6✔
106
        return "error marshaling value of type " + e.Type.String() + ": " + e.Err.Error()
6✔
107
}
6✔
108

109
// ErrNilKey is returned by Marshal functions and Encoder methods if a key is
110
// a nil interface or pointer value.
111
var ErrNilKey = errors.New("nil key")
112

113
// ErrInvalidKey is returned by Marshal functions and Encoder methods if, after
114
// dropping invalid runes, a key is empty.
115
var ErrInvalidKey = errors.New("invalid key")
116

117
// ErrUnsupportedKeyType is returned by Encoder methods if a key has an
118
// unsupported type.
119
var ErrUnsupportedKeyType = errors.New("unsupported key type")
120

121
// ErrUnsupportedValueType is returned by Encoder methods if a value has an
122
// unsupported type.
123
var ErrUnsupportedValueType = errors.New("unsupported value type")
124

125
func writeKey(w io.Writer, key any) error {
6✔
126
        if key == nil {
12✔
127
                return ErrNilKey
6✔
128
        }
6✔
129

130
        switch k := key.(type) {
6✔
131
        case string:
6✔
132
                return writeStringKey(w, k)
6✔
133
        case []byte:
6✔
134
                if k == nil {
6✔
135
                        return ErrNilKey
×
136
                }
×
137
                return writeBytesKey(w, k)
6✔
138
        case encoding.TextMarshaler:
6✔
139
                kb, err := safeMarshal(k)
6✔
140
                if err != nil {
12✔
141
                        return err
6✔
142
                }
6✔
143
                if kb == nil {
12✔
144
                        return ErrNilKey
6✔
145
                }
6✔
146
                return writeBytesKey(w, kb)
6✔
147
        case fmt.Stringer:
6✔
148
                ks, ok := safeString(k)
6✔
149
                if !ok {
12✔
150
                        return ErrNilKey
6✔
151
                }
6✔
152
                return writeStringKey(w, ks)
6✔
153
        default:
6✔
154
                rkey := reflect.ValueOf(key)
6✔
155
                switch rkey.Kind() {
6✔
156
                case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct:
6✔
157
                        return ErrUnsupportedKeyType
6✔
158
                case reflect.Pointer:
6✔
159
                        if rkey.IsNil() {
12✔
160
                                return ErrNilKey
6✔
161
                        }
6✔
162
                        return writeKey(w, rkey.Elem().Interface())
6✔
163
                }
164
                return writeStringKey(w, fmt.Sprint(k))
6✔
165
        }
166
}
167

168
// keyRuneFilter returns r for all valid key runes, and -1 for all invalid key
169
// runes. When used as the mapping function for strings.Map and bytes.Map
170
// functions it causes them to remove invalid key runes from strings or byte
171
// slices respectively.
172
func keyRuneFilter(r rune) rune {
6✔
173
        if r <= ' ' || r == '=' || r == '"' || r == 0x7f || r == utf8.RuneError {
12✔
174
                return -1
6✔
175
        }
6✔
176
        return r
6✔
177
}
178

179
func writeStringKey(w io.Writer, key string) error {
6✔
180
        k := strings.Map(keyRuneFilter, key)
6✔
181
        if k == "" {
12✔
182
                return ErrInvalidKey
6✔
183
        }
6✔
184
        _, err := io.WriteString(w, k)
6✔
185
        return err
6✔
186
}
187

188
func writeBytesKey(w io.Writer, key []byte) error {
6✔
189
        k := bytes.Map(keyRuneFilter, key)
6✔
190
        if len(k) == 0 {
12✔
191
                return ErrInvalidKey
6✔
192
        }
6✔
193
        _, err := w.Write(k)
6✔
194
        return err
6✔
195
}
196

197
func writeValue(w io.Writer, value any) error {
6✔
198
        switch v := value.(type) {
6✔
199
        case nil:
6✔
200
                return writeBytesValue(w, null)
6✔
201
        case string:
6✔
202
                return writeStringValue(w, v, true)
6✔
203
        case []byte:
6✔
204
                return writeBytesValue(w, v)
6✔
205
        case encoding.TextMarshaler:
6✔
206
                vb, err := safeMarshal(v)
6✔
207
                if err != nil {
12✔
208
                        return err
6✔
209
                }
6✔
210
                if vb == nil {
12✔
211
                        vb = null
6✔
212
                }
6✔
213
                return writeBytesValue(w, vb)
6✔
214
        case error:
6✔
215
                se, ok := safeError(v)
6✔
216
                return writeStringValue(w, se, ok)
6✔
217
        case fmt.Stringer:
6✔
218
                ss, ok := safeString(v)
6✔
219
                return writeStringValue(w, ss, ok)
6✔
220
        default:
6✔
221
                rvalue := reflect.ValueOf(value)
6✔
222
                switch rvalue.Kind() {
6✔
223
                case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct:
6✔
224
                        return ErrUnsupportedValueType
6✔
225
                case reflect.Pointer:
6✔
226
                        if rvalue.IsNil() {
12✔
227
                                return writeBytesValue(w, null)
6✔
228
                        }
6✔
229
                        return writeValue(w, rvalue.Elem().Interface())
6✔
230
                }
231
                return writeStringValue(w, fmt.Sprint(v), true)
6✔
232
        }
233
}
234

235
func needsQuotedValueRune(r rune) bool {
6✔
236
        return r <= ' ' || r == '=' || r == '"' || r == 0x7f || r == utf8.RuneError
6✔
237
}
6✔
238

239
func writeStringValue(w io.Writer, value string, ok bool) error {
6✔
240
        var err error
6✔
241
        if ok && value == "null" {
12✔
242
                _, err = io.WriteString(w, `"null"`)
6✔
243
        } else if strings.IndexFunc(value, needsQuotedValueRune) != -1 {
18✔
244
                _, err = writeQuotedString(w, value)
6✔
245
        } else {
12✔
246
                _, err = io.WriteString(w, value)
6✔
247
        }
6✔
248
        return err
6✔
249
}
250

251
func writeBytesValue(w io.Writer, value []byte) error {
6✔
252
        var err error
6✔
253
        if bytes.IndexFunc(value, needsQuotedValueRune) != -1 {
12✔
254
                _, err = writeQuotedBytes(w, value)
6✔
255
        } else {
12✔
256
                _, err = w.Write(value)
6✔
257
        }
6✔
258
        return err
6✔
259
}
260

261
// EndRecord writes a newline character to the stream and resets the encoder
262
// to the beginning of a new record.
263
func (enc *Encoder) EndRecord() error {
6✔
264
        _, err := enc.w.Write(newline)
6✔
265
        if err == nil {
12✔
266
                enc.needSep = false
6✔
267
        }
6✔
268
        return err
6✔
269
}
270

271
// Reset resets the encoder to the beginning of a new record.
272
func (enc *Encoder) Reset() {
×
273
        enc.needSep = false
×
274
}
×
275

276
func safeError(err error) (s string, ok bool) {
6✔
277
        defer func() {
12✔
278
                if panicVal := recover(); panicVal != nil {
6✔
NEW
279
                        if v := reflect.ValueOf(err); v.Kind() == reflect.Pointer && v.IsNil() {
×
280
                                s, ok = "null", false
×
281
                        } else {
×
282
                                s, ok = fmt.Sprintf("PANIC:%v", panicVal), false
×
283
                        }
×
284
                }
285
        }()
286
        s, ok = err.Error(), true
6✔
287
        return
6✔
288
}
289

290
func safeString(str fmt.Stringer) (s string, ok bool) {
6✔
291
        defer func() {
12✔
292
                if panicVal := recover(); panicVal != nil {
12✔
293
                        if v := reflect.ValueOf(str); v.Kind() == reflect.Pointer && v.IsNil() {
12✔
294
                                s, ok = "null", false
6✔
295
                        } else {
12✔
296
                                s, ok = fmt.Sprintf("PANIC:%v", panicVal), true
6✔
297
                        }
6✔
298
                }
299
        }()
300
        s, ok = str.String(), true
6✔
301
        return
6✔
302
}
303

304
func safeMarshal(tm encoding.TextMarshaler) (b []byte, err error) {
6✔
305
        defer func() {
12✔
306
                if panicVal := recover(); panicVal != nil {
12✔
307
                        if v := reflect.ValueOf(tm); v.Kind() == reflect.Pointer && v.IsNil() {
12✔
308
                                b, err = nil, nil
6✔
309
                        } else {
6✔
310
                                b, err = nil, fmt.Errorf("panic when marshalling: %s", panicVal)
×
311
                        }
×
312
                }
313
        }()
314
        b, err = tm.MarshalText()
6✔
315
        if err != nil {
12✔
316
                return nil, &MarshalerError{
6✔
317
                        Type: reflect.TypeOf(tm),
6✔
318
                        Err:  err,
6✔
319
                }
6✔
320
        }
6✔
321
        return
6✔
322
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc