1 // Copyright 2022 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
13 const nAttrsInline = 5
15 // A Record holds information about a log event.
16 // Copies of a Record share state.
17 // Do not modify a Record after handing out a copy to it.
18 // Use [Record.Clone] to create a copy with no shared state.
20 // The time at which the output method (Log, Info, etc.) was called.
26 // The level of the event.
29 // The program counter at the time the record was constructed, as determined
30 // by runtime.Callers. If zero, no program counter is available.
32 // The only valid use for this value is as an argument to
33 // [runtime.CallersFrames]. In particular, it must not be passed to
34 // [runtime.FuncForPC].
37 // Allocation optimization: an inline array sized to hold
38 // the majority of log calls (based on examination of open-source
39 // code). It holds the start of the list of Attrs.
40 front [nAttrsInline]Attr
42 // The number of Attrs in front.
45 // The list of Attrs except for those in front.
47 // - len(back) > 0 iff nFront == len(front)
48 // - Unused array elements are zero. Used to detect mistakes.
52 // NewRecord creates a Record from the given arguments.
53 // Use [Record.AddAttrs] to add attributes to the Record.
55 // NewRecord is intended for logging APIs that want to support a [Handler] as
57 func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record {
66 // frame returns the runtime.Frame of the log event.
67 // If the Record was created without the necessary information,
68 // or if the location is unavailable, it returns a zero Frame.
69 func (r Record) frame() runtime.Frame {
70 fs := runtime.CallersFrames([]uintptr{r.PC})
75 // Clone returns a copy of the record with no shared state.
76 // The original record and the clone can both be modified
77 // without interfering with each other.
78 func (r Record) Clone() Record {
79 r.back = slices.Clip(r.back) // prevent append from mutating shared array
83 // NumAttrs returns the number of attributes in the Record.
84 func (r Record) NumAttrs() int {
85 return r.nFront + len(r.back)
88 // Attrs calls f on each Attr in the Record.
89 // The Attrs are already resolved.
90 func (r Record) Attrs(f func(Attr)) {
91 for i := 0; i < r.nFront; i++ {
94 for _, a := range r.back {
99 // AddAttrs appends the given Attrs to the Record's list of Attrs.
100 // It resolves the Attrs before doing so.
101 func (r *Record) AddAttrs(attrs ...Attr) {
103 n := copy(r.front[r.nFront:], attrs)
105 // Check if a copy was modified by slicing past the end
106 // and seeing if the Attr there is non-zero.
107 if cap(r.back) > len(r.back) {
108 end := r.back[:len(r.back)+1][len(r.back)]
110 panic("copies of a slog.Record were both modified")
113 r.back = append(r.back, attrs[n:]...)
116 // Add converts the args to Attrs as described in [Logger.Log],
117 // then appends the Attrs to the Record's list of Attrs.
118 // It resolves the Attrs before doing so.
119 func (r *Record) Add(args ...any) {
122 a, args = argsToAttr(args)
123 if r.nFront < len(r.front) {
124 r.front[r.nFront] = a
128 r.back = make([]Attr, 0, countAttrs(args))
130 r.back = append(r.back, a)
136 // countAttrs returns the number of Attrs that would be created from args.
137 func countAttrs(args []any) int {
139 for i := 0; i < len(args); i++ {
141 if _, ok := args[i].(string); ok {
148 const badKey = "!BADKEY"
150 // argsToAttr turns a prefix of the nonempty args slice into an Attr
151 // and returns the unconsumed portion of the slice.
152 // If args[0] is an Attr, it returns it, resolved.
153 // If args[0] is a string, it treats the first two elements as
155 // Otherwise, it treats args[0] as a value with a missing key.
156 func argsToAttr(args []any) (Attr, []any) {
157 switch x := args[0].(type) {
160 return String(badKey, x), nil
163 a.Value = a.Value.Resolve()
167 x.Value = x.Value.Resolve()
171 return Any(badKey, x), args[1:]