@@ -12,6 +12,9 @@ import (
1212const nAttrsInline = 5
1313
1414// A Record holds information about a log event.
15+ // Copies of a Record share state.
16+ // Do not modify a Record after handing out a copy to it.
17+ // Use [Record.Clone] to create a copy with no shared state.
1518type Record struct {
1619 // The time at which the output method (Log, Info, etc.) was called.
1720 time time.Time
@@ -28,26 +31,28 @@ type Record struct {
2831
2932 // Allocation optimization: an inline array sized to hold
3033 // the majority of log calls (based on examination of open-source
31- // code). The array holds the end of the sequence of Attrs.
32- tail [nAttrsInline ]Attr
34+ // code). It holds the start of the list of Attrs.
35+ front [nAttrsInline ]Attr
3336
34- // The number of Attrs in tail .
35- nTail int
37+ // The number of Attrs in front .
38+ nFront int
3639
37- // The sequence of Attrs except for the tail, represented as a functional
38- // list of arrays.
39- attrs list [[nAttrsInline ]Attr ]
40+ // The list of Attrs except for those in front.
41+ // Invariants:
42+ // - len(back) > 0 iff nFront == len(front)
43+ // - Unused array elements are zero. Used to detect mistakes.
44+ back []Attr
4045}
4146
42- // MakeRecord creates a new Record from the given arguments.
43- // Use [Record.AddAttr ] to add attributes to the Record.
47+ // NewRecord creates a Record from the given arguments.
48+ // Use [Record.AddAttrs ] to add attributes to the Record.
4449// If calldepth is greater than zero, [Record.SourceLine] will
4550// return the file and line number at that depth,
46- // where 1 means the caller of MakeRecord .
51+ // where 1 means the caller of NewRecord .
4752//
48- // MakeRecord is intended for logging APIs that want to support a [Handler] as
53+ // NewRecord is intended for logging APIs that want to support a [Handler] as
4954// a backend.
50- func MakeRecord (t time.Time , level Level , msg string , calldepth int ) Record {
55+ func NewRecord (t time.Time , level Level , msg string , calldepth int ) Record {
5156 var p uintptr
5257 if calldepth > 0 {
5358 p = pc (calldepth + 1 )
@@ -85,50 +90,97 @@ func (r *Record) SourceLine() (file string, line int) {
8590 return f .File , f .Line
8691}
8792
88- // Attrs returns a copy of the sequence of Attrs in r.
89- func (r * Record ) Attrs () []Attr {
90- res := make ([]Attr , 0 , r .attrs .len ()* nAttrsInline + r .nTail )
91- r .attrs = r .attrs .normalize ()
92- for _ , f := range r .attrs .front {
93- res = append (res , f [:]... )
93+ // Clone returns a copy of the record with no shared state.
94+ // The original record and the clone can both be modified
95+ // without interfering with each other.
96+ func (r * Record ) Clone () Record {
97+ c := * r
98+ if len (c .back ) > 0 {
99+ c .back = make ([]Attr , len (c .back ))
100+ copy (c .back , r .back )
94101 }
95- for _ , a := range r .tail [:r .nTail ] {
96- res = append (res , a )
97- }
98- return res
102+ return c
99103}
100104
101- // NumAttrs returns the number of Attrs in r .
105+ // NumAttrs returns the number of attributes in the Record .
102106func (r * Record ) NumAttrs () int {
103- return r .attrs .len ()* nAttrsInline + r .nTail
107+ return r .nFront + len (r .back )
108+ }
109+
110+ // Attrs calls f on each Attr in the Record.
111+ func (r * Record ) Attrs (f func (Attr )) {
112+ for i := 0 ; i < r .nFront ; i ++ {
113+ f (r .front [i ])
114+ }
115+ for _ , a := range r .back {
116+ f (a )
117+ }
104118}
105119
106- // Attr returns the i'th Attr in r.
107- func (r * Record ) Attr (i int ) Attr {
108- if r .attrs .back != nil {
109- r .attrs = r .attrs .normalize ()
120+ // AddAttrs appends the given attrs to the Record's list of Attrs.
121+ func (r * Record ) AddAttrs (attrs ... Attr ) {
122+ n := copy (r .front [r .nFront :], attrs )
123+ r .nFront += n
124+ // Check if a copy was modified by slicing past the end
125+ // and seeing if the Attr there is non-zero.
126+ if cap (r .back ) > len (r .back ) {
127+ end := r .back [:len (r .back )+ 1 ][len (r .back )]
128+ if end != (Attr {}) {
129+ panic ("copies of a slog.Record were both modified" )
130+ }
110131 }
111- alen := r .attrs .len () * nAttrsInline
112- if i < alen {
113- return r .attrs .at (i / nAttrsInline )[i % nAttrsInline ]
132+ r .back = append (r .back , attrs [n :]... )
133+ }
134+
135+ func (r * Record ) setAttrsFromArgs (args []any ) {
136+ var a Attr
137+ for len (args ) > 0 {
138+ a , args = argsToAttr (args )
139+ if r .nFront < len (r .front ) {
140+ r .front [r .nFront ] = a
141+ r .nFront ++
142+ } else {
143+ if r .back == nil {
144+ r .back = make ([]Attr , 0 , countAttrs (args ))
145+ }
146+ r .back = append (r .back , a )
147+ }
114148 }
115- return r . tail [ i - alen ]
149+
116150}
117151
118- // AddAttr appends an attributes to the record's list of attributes.
119- // It does not check for duplicate keys.
120- func (r * Record ) AddAttr (a Attr ) {
121- if r .nTail == len (r .tail ) {
122- r .attrs = r .attrs .append (r .tail )
123- r .nTail = 0
152+ // countAttrs returns the number of Attrs that would be created from args.
153+ func countAttrs (args []any ) int {
154+ n := 0
155+ for i := 0 ; i < len (args ); i ++ {
156+ n ++
157+ if _ , ok := args [i ].(string ); ok {
158+ i ++
159+ }
124160 }
125- r .tail [r .nTail ] = a
126- r .nTail ++
161+ return n
127162}
128163
129- func (r * Record ) addAttrs (attrs []Attr ) {
130- // TODO: be cleverer.
131- for _ , a := range attrs {
132- r .AddAttr (a )
164+ const badKey = "!BADKEY"
165+
166+ // argsToAttr turns a prefix of the nonempty args slice into an Attr
167+ // and returns the unconsumed portion of the slice.
168+ // If args[0] is an Attr, it returns it.
169+ // If args[0] is a string, it treats the first two elements as
170+ // a key-value pair.
171+ // Otherwise, it treats args[0] as a value with a missing key.
172+ func argsToAttr (args []any ) (Attr , []any ) {
173+ switch x := args [0 ].(type ) {
174+ case string :
175+ if len (args ) == 1 {
176+ return String (badKey , x ), nil
177+ }
178+ return Any (x , args [1 ]), args [2 :]
179+
180+ case Attr :
181+ return x , args [1 :]
182+
183+ default :
184+ return Any (badKey , x ), args [1 :]
133185 }
134186}
0 commit comments