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.
11 "log/slog/internal/buffer"
18 // A Handler handles log records produced by a Logger..
20 // A typical handler may print log records to standard error,
21 // or write them to a file or database, or perhaps augment them
22 // with additional attributes and pass them on to another handler.
24 // Any of the Handler's methods may be called concurrently with itself
25 // or with other methods. It is the responsibility of the Handler to
26 // manage this concurrency.
28 // Users of the slog package should not invoke Handler methods directly.
29 // They should use the methods of [Logger] instead.
30 type Handler interface {
31 // Enabled reports whether the handler handles records at the given level.
32 // The handler ignores records whose level is lower.
33 // It is called early, before any arguments are processed,
34 // to save effort if the log event should be discarded.
35 // If called from a Logger method, the first argument is the context
36 // passed to that method, or context.Background() if nil was passed
37 // or the method does not take a context.
38 // The context is passed so Enabled can use its values
39 // to make a decision.
40 Enabled(context.Context, Level) bool
42 // Handle handles the Record.
43 // It will only be called when Enabled returns true.
44 // The Context argument is as for Enabled.
45 // It is present solely to provide Handlers access to the context's values.
46 // Canceling the context should not affect record processing.
47 // (Among other things, log messages may be necessary to debug a
48 // cancellation-related problem.)
50 // Handle methods that produce output should observe the following rules:
51 // - If r.Time is the zero time, ignore the time.
52 // - If r.PC is zero, ignore it.
53 // - Attr's values should be resolved.
54 // - If an Attr's key and value are both the zero value, ignore the Attr.
55 // This can be tested with attr.Equal(Attr{}).
56 // - If a group's key is empty, inline the group's Attrs.
57 // - If a group has no Attrs (even if it has a non-empty key),
59 Handle(context.Context, Record) error
61 // WithAttrs returns a new Handler whose attributes consist of
62 // both the receiver's attributes and the arguments.
63 // The Handler owns the slice: it may retain, modify or discard it.
64 WithAttrs(attrs []Attr) Handler
66 // WithGroup returns a new Handler with the given group appended to
67 // the receiver's existing groups.
68 // The keys of all subsequent attributes, whether added by With or in a
69 // Record, should be qualified by the sequence of group names.
71 // How this qualification happens is up to the Handler, so long as
72 // this Handler's attribute keys differ from those of another Handler
73 // with a different sequence of group names.
75 // A Handler should treat WithGroup as starting a Group of Attrs that ends
76 // at the end of the log event. That is,
78 // logger.WithGroup("s").LogAttrs(level, msg, slog.Int("a", 1), slog.Int("b", 2))
82 // logger.LogAttrs(level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2)))
84 // If the name is empty, WithGroup returns the receiver.
85 WithGroup(name string) Handler
88 type defaultHandler struct {
90 // internal.DefaultOutput, except for testing
91 output func(pc uintptr, data []byte) error
94 func newDefaultHandler(output func(uintptr, []byte) error) *defaultHandler {
95 return &defaultHandler{
96 ch: &commonHandler{json: false},
101 func (*defaultHandler) Enabled(_ context.Context, l Level) bool {
102 return l >= LevelInfo
105 // Collect the level, attributes and message in a string and
106 // write it with the default log.Logger.
107 // Let the log.Logger handle time and file/line.
108 func (h *defaultHandler) Handle(ctx context.Context, r Record) error {
110 buf.WriteString(r.Level.String())
112 buf.WriteString(r.Message)
113 state := h.ch.newHandleState(buf, true, " ")
115 state.appendNonBuiltIns(r)
116 return h.output(r.PC, *buf)
119 func (h *defaultHandler) WithAttrs(as []Attr) Handler {
120 return &defaultHandler{h.ch.withAttrs(as), h.output}
123 func (h *defaultHandler) WithGroup(name string) Handler {
124 return &defaultHandler{h.ch.withGroup(name), h.output}
127 // HandlerOptions are options for a TextHandler or JSONHandler.
128 // A zero HandlerOptions consists entirely of default values.
129 type HandlerOptions struct {
130 // AddSource causes the handler to compute the source code position
131 // of the log statement and add a SourceKey attribute to the output.
134 // Level reports the minimum record level that will be logged.
135 // The handler discards records with lower levels.
136 // If Level is nil, the handler assumes LevelInfo.
137 // The handler calls Level.Level for each record processed;
138 // to adjust the minimum level dynamically, use a LevelVar.
141 // ReplaceAttr is called to rewrite each non-group attribute before it is logged.
142 // The attribute's value has been resolved (see [Value.Resolve]).
143 // If ReplaceAttr returns a zero Attr, the attribute is discarded.
145 // The built-in attributes with keys "time", "level", "source", and "msg"
146 // are passed to this function, except that time is omitted
147 // if zero, and source is omitted if AddSource is false.
149 // The first argument is a list of currently open groups that contain the
150 // Attr. It must not be retained or modified. ReplaceAttr is never called
151 // for Group attributes, only their contents. For example, the attribute
154 // Int("a", 1), Group("g", Int("b", 2)), Int("c", 3)
156 // results in consecutive calls to ReplaceAttr with the following arguments:
159 // []string{"g"}, Int("b", 2)
162 // ReplaceAttr can be used to change the default keys of the built-in
163 // attributes, convert types (for example, to replace a `time.Time` with the
164 // integer seconds since the Unix epoch), sanitize personal information, or
165 // remove attributes from the output.
166 ReplaceAttr func(groups []string, a Attr) Attr
169 // Keys for "built-in" attributes.
171 // TimeKey is the key used by the built-in handlers for the time
172 // when the log method is called. The associated Value is a [time.Time].
174 // LevelKey is the key used by the built-in handlers for the level
175 // of the log call. The associated value is a [Level].
177 // MessageKey is the key used by the built-in handlers for the
178 // message of the log call. The associated value is a string.
180 // SourceKey is the key used by the built-in handlers for the source file
181 // and line of the log call. The associated value is a string.
185 type commonHandler struct {
186 json bool // true => output JSON; false => output text
188 preformattedAttrs []byte
189 // groupPrefix is for the text handler only.
190 // It holds the prefix for groups that were already pre-formatted.
191 // A group will appear here when a call to WithGroup is followed by
192 // a call to WithAttrs.
194 groups []string // all groups started from WithGroup
195 nOpenGroups int // the number of groups opened in preformattedAttrs
200 func (h *commonHandler) clone() *commonHandler {
201 // We can't use assignment because we can't copy the mutex.
202 return &commonHandler{
205 preformattedAttrs: slices.Clip(h.preformattedAttrs),
206 groupPrefix: h.groupPrefix,
207 groups: slices.Clip(h.groups),
208 nOpenGroups: h.nOpenGroups,
210 mu: h.mu, // mutex shared among all clones of this handler
214 // enabled reports whether l is greater than or equal to the
216 func (h *commonHandler) enabled(l Level) bool {
217 minLevel := LevelInfo
218 if h.opts.Level != nil {
219 minLevel = h.opts.Level.Level()
224 func (h *commonHandler) withAttrs(as []Attr) *commonHandler {
225 // We are going to ignore empty groups, so if the entire slice consists of
226 // them, there is nothing to do.
227 if countEmptyGroups(as) == len(as) {
231 // Pre-format the attributes as an optimization.
232 state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "")
234 state.prefix.WriteString(h.groupPrefix)
235 if len(h2.preformattedAttrs) > 0 {
236 state.sep = h.attrSep()
239 for _, a := range as {
242 // Remember the new prefix for later keys.
243 h2.groupPrefix = state.prefix.String()
244 // Remember how many opened groups are in preformattedAttrs,
245 // so we don't open them again when we handle a Record.
246 h2.nOpenGroups = len(h2.groups)
250 func (h *commonHandler) withGroup(name string) *commonHandler {
252 h2.groups = append(h2.groups, name)
256 // handle is the internal implementation of Handler.Handle
257 // used by TextHandler and JSONHandler.
258 func (h *commonHandler) handle(r Record) error {
259 state := h.newHandleState(buffer.New(), true, "")
262 state.buf.WriteByte('{')
264 // Built-in attributes. They are not in a group.
265 stateGroups := state.groups
266 state.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups.
267 rep := h.opts.ReplaceAttr
269 if !r.Time.IsZero() {
271 val := r.Time.Round(0) // strip monotonic to match Attr behavior
274 state.appendTime(val)
276 state.appendAttr(Time(key, val))
284 state.appendString(val.String())
286 state.appendAttr(Any(key, val))
289 if h.opts.AddSource {
290 state.appendAttr(Any(SourceKey, r.source()))
296 state.appendString(msg)
298 state.appendAttr(String(key, msg))
300 state.groups = stateGroups // Restore groups passed to ReplaceAttrs.
301 state.appendNonBuiltIns(r)
302 state.buf.WriteByte('\n')
306 _, err := h.w.Write(*state.buf)
310 func (s *handleState) appendNonBuiltIns(r Record) {
311 // preformatted Attrs
312 if len(s.h.preformattedAttrs) > 0 {
313 s.buf.WriteString(s.sep)
314 s.buf.Write(s.h.preformattedAttrs)
315 s.sep = s.h.attrSep()
317 // Attrs in Record -- unlike the built-in ones, they are in groups started
319 // If the record has no Attrs, don't output any groups.
320 nOpenGroups := s.h.nOpenGroups
321 if r.NumAttrs() > 0 {
322 s.prefix.WriteString(s.h.groupPrefix)
324 nOpenGroups = len(s.h.groups)
325 r.Attrs(func(a Attr) bool {
331 // Close all open groups.
332 for range s.h.groups[:nOpenGroups] {
335 // Close the top-level object.
340 // attrSep returns the separator between attributes.
341 func (h *commonHandler) attrSep() string {
348 // handleState holds state for a single call to commonHandler.handle.
349 // The initial value of sep determines whether to emit a separator
350 // before the next key, after which it stays true.
351 type handleState struct {
354 freeBuf bool // should buf be freed?
355 sep string // separator to write before next key
356 prefix *buffer.Buffer // for text: key prefix
357 groups *[]string // pool-allocated slice of active groups, for ReplaceAttr
360 var groupPool = sync.Pool{New: func() any {
361 s := make([]string, 0, 10)
365 func (h *commonHandler) newHandleState(buf *buffer.Buffer, freeBuf bool, sep string) handleState {
371 prefix: buffer.New(),
373 if h.opts.ReplaceAttr != nil {
374 s.groups = groupPool.Get().(*[]string)
375 *s.groups = append(*s.groups, h.groups[:h.nOpenGroups]...)
380 func (s *handleState) free() {
384 if gs := s.groups; gs != nil {
391 func (s *handleState) openGroups() {
392 for _, n := range s.h.groups[s.h.nOpenGroups:] {
397 // Separator for group names and keys.
398 const keyComponentSep = '.'
400 // openGroup starts a new group of attributes
401 // with the given name.
402 func (s *handleState) openGroup(name string) {
408 s.prefix.WriteString(name)
409 s.prefix.WriteByte(keyComponentSep)
411 // Collect group names for ReplaceAttr.
413 *s.groups = append(*s.groups, name)
417 // closeGroup ends the group with the given name.
418 func (s *handleState) closeGroup(name string) {
422 (*s.prefix) = (*s.prefix)[:len(*s.prefix)-len(name)-1 /* for keyComponentSep */]
424 s.sep = s.h.attrSep()
426 *s.groups = (*s.groups)[:len(*s.groups)-1]
430 // appendAttr appends the Attr's key and value using app.
431 // It handles replacement and checking for an empty key.
432 // after replacement).
433 func (s *handleState) appendAttr(a Attr) {
434 if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup {
439 // Resolve before calling ReplaceAttr, so the user doesn't have to.
440 a.Value = a.Value.Resolve()
443 a.Value = a.Value.Resolve()
444 // Elide empty Attrs.
448 // Special case: Source.
449 if v := a.Value; v.Kind() == KindAny {
450 if src, ok := v.Any().(*Source); ok {
452 a.Value = src.group()
454 a.Value = StringValue(fmt.Sprintf("%s:%d", src.File, src.Line))
458 if a.Value.Kind() == KindGroup {
459 attrs := a.Value.Group()
460 // Output only non-empty groups.
462 // Inline a group with an empty key.
466 for _, aa := range attrs {
475 s.appendValue(a.Value)
479 func (s *handleState) appendError(err error) {
480 s.appendString(fmt.Sprintf("!ERROR:%v", err))
483 func (s *handleState) appendKey(key string) {
484 s.buf.WriteString(s.sep)
485 if s.prefix != nil && len(*s.prefix) > 0 {
486 // TODO: optimize by avoiding allocation.
487 s.appendString(string(*s.prefix) + key)
496 s.sep = s.h.attrSep()
499 func (s *handleState) appendString(str string) {
502 *s.buf = appendEscapedJSONString(*s.buf, str)
506 if needsQuoting(str) {
507 *s.buf = strconv.AppendQuote(*s.buf, str)
509 s.buf.WriteString(str)
514 func (s *handleState) appendValue(v Value) {
517 err = appendJSONValue(s, v)
519 err = appendTextValue(s, v)
526 func (s *handleState) appendTime(t time.Time) {
530 writeTimeRFC3339Millis(s.buf, t)
534 // This takes half the time of Time.AppendFormat.
535 func writeTimeRFC3339Millis(buf *buffer.Buffer, t time.Time) {
536 year, month, day := t.Date()
537 buf.WritePosIntWidth(year, 4)
539 buf.WritePosIntWidth(int(month), 2)
541 buf.WritePosIntWidth(day, 2)
543 hour, min, sec := t.Clock()
544 buf.WritePosIntWidth(hour, 2)
546 buf.WritePosIntWidth(min, 2)
548 buf.WritePosIntWidth(sec, 2)
551 buf.WritePosIntWidth(ns/1e6, 3)
552 _, offsetSeconds := t.Zone()
553 if offsetSeconds == 0 {
556 offsetMinutes := offsetSeconds / 60
557 if offsetMinutes < 0 {
559 offsetMinutes = -offsetMinutes
563 buf.WritePosIntWidth(offsetMinutes/60, 2)
565 buf.WritePosIntWidth(offsetMinutes%60, 2)