summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/exp/slog/handler.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/exp/slog/handler.go')
-rw-r--r--vendor/golang.org/x/exp/slog/handler.go577
1 files changed, 0 insertions, 577 deletions
diff --git a/vendor/golang.org/x/exp/slog/handler.go b/vendor/golang.org/x/exp/slog/handler.go
deleted file mode 100644
index bd635cb81..000000000
--- a/vendor/golang.org/x/exp/slog/handler.go
+++ /dev/null
@@ -1,577 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package slog
-
-import (
- "context"
- "fmt"
- "io"
- "reflect"
- "strconv"
- "sync"
- "time"
-
- "golang.org/x/exp/slices"
- "golang.org/x/exp/slog/internal/buffer"
-)
-
-// A Handler handles log records produced by a Logger..
-//
-// A typical handler may print log records to standard error,
-// or write them to a file or database, or perhaps augment them
-// with additional attributes and pass them on to another handler.
-//
-// Any of the Handler's methods may be called concurrently with itself
-// or with other methods. It is the responsibility of the Handler to
-// manage this concurrency.
-//
-// Users of the slog package should not invoke Handler methods directly.
-// They should use the methods of [Logger] instead.
-type Handler interface {
- // Enabled reports whether the handler handles records at the given level.
- // The handler ignores records whose level is lower.
- // It is called early, before any arguments are processed,
- // to save effort if the log event should be discarded.
- // If called from a Logger method, the first argument is the context
- // passed to that method, or context.Background() if nil was passed
- // or the method does not take a context.
- // The context is passed so Enabled can use its values
- // to make a decision.
- Enabled(context.Context, Level) bool
-
- // Handle handles the Record.
- // It will only be called when Enabled returns true.
- // The Context argument is as for Enabled.
- // It is present solely to provide Handlers access to the context's values.
- // Canceling the context should not affect record processing.
- // (Among other things, log messages may be necessary to debug a
- // cancellation-related problem.)
- //
- // Handle methods that produce output should observe the following rules:
- // - If r.Time is the zero time, ignore the time.
- // - If r.PC is zero, ignore it.
- // - Attr's values should be resolved.
- // - If an Attr's key and value are both the zero value, ignore the Attr.
- // This can be tested with attr.Equal(Attr{}).
- // - If a group's key is empty, inline the group's Attrs.
- // - If a group has no Attrs (even if it has a non-empty key),
- // ignore it.
- Handle(context.Context, Record) error
-
- // WithAttrs returns a new Handler whose attributes consist of
- // both the receiver's attributes and the arguments.
- // The Handler owns the slice: it may retain, modify or discard it.
- WithAttrs(attrs []Attr) Handler
-
- // WithGroup returns a new Handler with the given group appended to
- // the receiver's existing groups.
- // The keys of all subsequent attributes, whether added by With or in a
- // Record, should be qualified by the sequence of group names.
- //
- // How this qualification happens is up to the Handler, so long as
- // this Handler's attribute keys differ from those of another Handler
- // with a different sequence of group names.
- //
- // A Handler should treat WithGroup as starting a Group of Attrs that ends
- // at the end of the log event. That is,
- //
- // logger.WithGroup("s").LogAttrs(level, msg, slog.Int("a", 1), slog.Int("b", 2))
- //
- // should behave like
- //
- // logger.LogAttrs(level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2)))
- //
- // If the name is empty, WithGroup returns the receiver.
- WithGroup(name string) Handler
-}
-
-type defaultHandler struct {
- ch *commonHandler
- // log.Output, except for testing
- output func(calldepth int, message string) error
-}
-
-func newDefaultHandler(output func(int, string) error) *defaultHandler {
- return &defaultHandler{
- ch: &commonHandler{json: false},
- output: output,
- }
-}
-
-func (*defaultHandler) Enabled(_ context.Context, l Level) bool {
- return l >= LevelInfo
-}
-
-// Collect the level, attributes and message in a string and
-// write it with the default log.Logger.
-// Let the log.Logger handle time and file/line.
-func (h *defaultHandler) Handle(ctx context.Context, r Record) error {
- buf := buffer.New()
- buf.WriteString(r.Level.String())
- buf.WriteByte(' ')
- buf.WriteString(r.Message)
- state := h.ch.newHandleState(buf, true, " ", nil)
- defer state.free()
- state.appendNonBuiltIns(r)
-
- // skip [h.output, defaultHandler.Handle, handlerWriter.Write, log.Output]
- return h.output(4, buf.String())
-}
-
-func (h *defaultHandler) WithAttrs(as []Attr) Handler {
- return &defaultHandler{h.ch.withAttrs(as), h.output}
-}
-
-func (h *defaultHandler) WithGroup(name string) Handler {
- return &defaultHandler{h.ch.withGroup(name), h.output}
-}
-
-// HandlerOptions are options for a TextHandler or JSONHandler.
-// A zero HandlerOptions consists entirely of default values.
-type HandlerOptions struct {
- // AddSource causes the handler to compute the source code position
- // of the log statement and add a SourceKey attribute to the output.
- AddSource bool
-
- // Level reports the minimum record level that will be logged.
- // The handler discards records with lower levels.
- // If Level is nil, the handler assumes LevelInfo.
- // The handler calls Level.Level for each record processed;
- // to adjust the minimum level dynamically, use a LevelVar.
- Level Leveler
-
- // ReplaceAttr is called to rewrite each non-group attribute before it is logged.
- // The attribute's value has been resolved (see [Value.Resolve]).
- // If ReplaceAttr returns an Attr with Key == "", the attribute is discarded.
- //
- // The built-in attributes with keys "time", "level", "source", and "msg"
- // are passed to this function, except that time is omitted
- // if zero, and source is omitted if AddSource is false.
- //
- // The first argument is a list of currently open groups that contain the
- // Attr. It must not be retained or modified. ReplaceAttr is never called
- // for Group attributes, only their contents. For example, the attribute
- // list
- //
- // Int("a", 1), Group("g", Int("b", 2)), Int("c", 3)
- //
- // results in consecutive calls to ReplaceAttr with the following arguments:
- //
- // nil, Int("a", 1)
- // []string{"g"}, Int("b", 2)
- // nil, Int("c", 3)
- //
- // ReplaceAttr can be used to change the default keys of the built-in
- // attributes, convert types (for example, to replace a `time.Time` with the
- // integer seconds since the Unix epoch), sanitize personal information, or
- // remove attributes from the output.
- ReplaceAttr func(groups []string, a Attr) Attr
-}
-
-// Keys for "built-in" attributes.
-const (
- // TimeKey is the key used by the built-in handlers for the time
- // when the log method is called. The associated Value is a [time.Time].
- TimeKey = "time"
- // LevelKey is the key used by the built-in handlers for the level
- // of the log call. The associated value is a [Level].
- LevelKey = "level"
- // MessageKey is the key used by the built-in handlers for the
- // message of the log call. The associated value is a string.
- MessageKey = "msg"
- // SourceKey is the key used by the built-in handlers for the source file
- // and line of the log call. The associated value is a string.
- SourceKey = "source"
-)
-
-type commonHandler struct {
- json bool // true => output JSON; false => output text
- opts HandlerOptions
- preformattedAttrs []byte
- groupPrefix string // for text: prefix of groups opened in preformatting
- groups []string // all groups started from WithGroup
- nOpenGroups int // the number of groups opened in preformattedAttrs
- mu sync.Mutex
- w io.Writer
-}
-
-func (h *commonHandler) clone() *commonHandler {
- // We can't use assignment because we can't copy the mutex.
- return &commonHandler{
- json: h.json,
- opts: h.opts,
- preformattedAttrs: slices.Clip(h.preformattedAttrs),
- groupPrefix: h.groupPrefix,
- groups: slices.Clip(h.groups),
- nOpenGroups: h.nOpenGroups,
- w: h.w,
- }
-}
-
-// enabled reports whether l is greater than or equal to the
-// minimum level.
-func (h *commonHandler) enabled(l Level) bool {
- minLevel := LevelInfo
- if h.opts.Level != nil {
- minLevel = h.opts.Level.Level()
- }
- return l >= minLevel
-}
-
-func (h *commonHandler) withAttrs(as []Attr) *commonHandler {
- h2 := h.clone()
- // Pre-format the attributes as an optimization.
- prefix := buffer.New()
- defer prefix.Free()
- prefix.WriteString(h.groupPrefix)
- state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "", prefix)
- defer state.free()
- if len(h2.preformattedAttrs) > 0 {
- state.sep = h.attrSep()
- }
- state.openGroups()
- for _, a := range as {
- state.appendAttr(a)
- }
- // Remember the new prefix for later keys.
- h2.groupPrefix = state.prefix.String()
- // Remember how many opened groups are in preformattedAttrs,
- // so we don't open them again when we handle a Record.
- h2.nOpenGroups = len(h2.groups)
- return h2
-}
-
-func (h *commonHandler) withGroup(name string) *commonHandler {
- if name == "" {
- return h
- }
- h2 := h.clone()
- h2.groups = append(h2.groups, name)
- return h2
-}
-
-func (h *commonHandler) handle(r Record) error {
- state := h.newHandleState(buffer.New(), true, "", nil)
- defer state.free()
- if h.json {
- state.buf.WriteByte('{')
- }
- // Built-in attributes. They are not in a group.
- stateGroups := state.groups
- state.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups.
- rep := h.opts.ReplaceAttr
- // time
- if !r.Time.IsZero() {
- key := TimeKey
- val := r.Time.Round(0) // strip monotonic to match Attr behavior
- if rep == nil {
- state.appendKey(key)
- state.appendTime(val)
- } else {
- state.appendAttr(Time(key, val))
- }
- }
- // level
- key := LevelKey
- val := r.Level
- if rep == nil {
- state.appendKey(key)
- state.appendString(val.String())
- } else {
- state.appendAttr(Any(key, val))
- }
- // source
- if h.opts.AddSource {
- state.appendAttr(Any(SourceKey, r.source()))
- }
- key = MessageKey
- msg := r.Message
- if rep == nil {
- state.appendKey(key)
- state.appendString(msg)
- } else {
- state.appendAttr(String(key, msg))
- }
- state.groups = stateGroups // Restore groups passed to ReplaceAttrs.
- state.appendNonBuiltIns(r)
- state.buf.WriteByte('\n')
-
- h.mu.Lock()
- defer h.mu.Unlock()
- _, err := h.w.Write(*state.buf)
- return err
-}
-
-func (s *handleState) appendNonBuiltIns(r Record) {
- // preformatted Attrs
- if len(s.h.preformattedAttrs) > 0 {
- s.buf.WriteString(s.sep)
- s.buf.Write(s.h.preformattedAttrs)
- s.sep = s.h.attrSep()
- }
- // Attrs in Record -- unlike the built-in ones, they are in groups started
- // from WithGroup.
- s.prefix = buffer.New()
- defer s.prefix.Free()
- s.prefix.WriteString(s.h.groupPrefix)
- s.openGroups()
- r.Attrs(func(a Attr) bool {
- s.appendAttr(a)
- return true
- })
- if s.h.json {
- // Close all open groups.
- for range s.h.groups {
- s.buf.WriteByte('}')
- }
- // Close the top-level object.
- s.buf.WriteByte('}')
- }
-}
-
-// attrSep returns the separator between attributes.
-func (h *commonHandler) attrSep() string {
- if h.json {
- return ","
- }
- return " "
-}
-
-// handleState holds state for a single call to commonHandler.handle.
-// The initial value of sep determines whether to emit a separator
-// before the next key, after which it stays true.
-type handleState struct {
- h *commonHandler
- buf *buffer.Buffer
- freeBuf bool // should buf be freed?
- sep string // separator to write before next key
- prefix *buffer.Buffer // for text: key prefix
- groups *[]string // pool-allocated slice of active groups, for ReplaceAttr
-}
-
-var groupPool = sync.Pool{New: func() any {
- s := make([]string, 0, 10)
- return &s
-}}
-
-func (h *commonHandler) newHandleState(buf *buffer.Buffer, freeBuf bool, sep string, prefix *buffer.Buffer) handleState {
- s := handleState{
- h: h,
- buf: buf,
- freeBuf: freeBuf,
- sep: sep,
- prefix: prefix,
- }
- if h.opts.ReplaceAttr != nil {
- s.groups = groupPool.Get().(*[]string)
- *s.groups = append(*s.groups, h.groups[:h.nOpenGroups]...)
- }
- return s
-}
-
-func (s *handleState) free() {
- if s.freeBuf {
- s.buf.Free()
- }
- if gs := s.groups; gs != nil {
- *gs = (*gs)[:0]
- groupPool.Put(gs)
- }
-}
-
-func (s *handleState) openGroups() {
- for _, n := range s.h.groups[s.h.nOpenGroups:] {
- s.openGroup(n)
- }
-}
-
-// Separator for group names and keys.
-const keyComponentSep = '.'
-
-// openGroup starts a new group of attributes
-// with the given name.
-func (s *handleState) openGroup(name string) {
- if s.h.json {
- s.appendKey(name)
- s.buf.WriteByte('{')
- s.sep = ""
- } else {
- s.prefix.WriteString(name)
- s.prefix.WriteByte(keyComponentSep)
- }
- // Collect group names for ReplaceAttr.
- if s.groups != nil {
- *s.groups = append(*s.groups, name)
- }
-}
-
-// closeGroup ends the group with the given name.
-func (s *handleState) closeGroup(name string) {
- if s.h.json {
- s.buf.WriteByte('}')
- } else {
- (*s.prefix) = (*s.prefix)[:len(*s.prefix)-len(name)-1 /* for keyComponentSep */]
- }
- s.sep = s.h.attrSep()
- if s.groups != nil {
- *s.groups = (*s.groups)[:len(*s.groups)-1]
- }
-}
-
-// appendAttr appends the Attr's key and value using app.
-// It handles replacement and checking for an empty key.
-// after replacement).
-func (s *handleState) appendAttr(a Attr) {
- if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup {
- var gs []string
- if s.groups != nil {
- gs = *s.groups
- }
- // Resolve before calling ReplaceAttr, so the user doesn't have to.
- a.Value = a.Value.Resolve()
- a = rep(gs, a)
- }
- a.Value = a.Value.Resolve()
- // Elide empty Attrs.
- if a.isEmpty() {
- return
- }
- // Special case: Source.
- if v := a.Value; v.Kind() == KindAny {
- if src, ok := v.Any().(*Source); ok {
- if s.h.json {
- a.Value = src.group()
- } else {
- a.Value = StringValue(fmt.Sprintf("%s:%d", src.File, src.Line))
- }
- }
- }
- if a.Value.Kind() == KindGroup {
- attrs := a.Value.Group()
- // Output only non-empty groups.
- if len(attrs) > 0 {
- // Inline a group with an empty key.
- if a.Key != "" {
- s.openGroup(a.Key)
- }
- for _, aa := range attrs {
- s.appendAttr(aa)
- }
- if a.Key != "" {
- s.closeGroup(a.Key)
- }
- }
- } else {
- s.appendKey(a.Key)
- s.appendValue(a.Value)
- }
-}
-
-func (s *handleState) appendError(err error) {
- s.appendString(fmt.Sprintf("!ERROR:%v", err))
-}
-
-func (s *handleState) appendKey(key string) {
- s.buf.WriteString(s.sep)
- if s.prefix != nil {
- // TODO: optimize by avoiding allocation.
- s.appendString(string(*s.prefix) + key)
- } else {
- s.appendString(key)
- }
- if s.h.json {
- s.buf.WriteByte(':')
- } else {
- s.buf.WriteByte('=')
- }
- s.sep = s.h.attrSep()
-}
-
-func (s *handleState) appendString(str string) {
- if s.h.json {
- s.buf.WriteByte('"')
- *s.buf = appendEscapedJSONString(*s.buf, str)
- s.buf.WriteByte('"')
- } else {
- // text
- if needsQuoting(str) {
- *s.buf = strconv.AppendQuote(*s.buf, str)
- } else {
- s.buf.WriteString(str)
- }
- }
-}
-
-func (s *handleState) appendValue(v Value) {
- defer func() {
- if r := recover(); r != nil {
- // If it panics with a nil pointer, the most likely cases are
- // an encoding.TextMarshaler or error fails to guard against nil,
- // in which case "<nil>" seems to be the feasible choice.
- //
- // Adapted from the code in fmt/print.go.
- if v := reflect.ValueOf(v.any); v.Kind() == reflect.Pointer && v.IsNil() {
- s.appendString("<nil>")
- return
- }
-
- // Otherwise just print the original panic message.
- s.appendString(fmt.Sprintf("!PANIC: %v", r))
- }
- }()
-
- var err error
- if s.h.json {
- err = appendJSONValue(s, v)
- } else {
- err = appendTextValue(s, v)
- }
- if err != nil {
- s.appendError(err)
- }
-}
-
-func (s *handleState) appendTime(t time.Time) {
- if s.h.json {
- appendJSONTime(s, t)
- } else {
- writeTimeRFC3339Millis(s.buf, t)
- }
-}
-
-// This takes half the time of Time.AppendFormat.
-func writeTimeRFC3339Millis(buf *buffer.Buffer, t time.Time) {
- year, month, day := t.Date()
- buf.WritePosIntWidth(year, 4)
- buf.WriteByte('-')
- buf.WritePosIntWidth(int(month), 2)
- buf.WriteByte('-')
- buf.WritePosIntWidth(day, 2)
- buf.WriteByte('T')
- hour, min, sec := t.Clock()
- buf.WritePosIntWidth(hour, 2)
- buf.WriteByte(':')
- buf.WritePosIntWidth(min, 2)
- buf.WriteByte(':')
- buf.WritePosIntWidth(sec, 2)
- ns := t.Nanosecond()
- buf.WriteByte('.')
- buf.WritePosIntWidth(ns/1e6, 3)
- _, offsetSeconds := t.Zone()
- if offsetSeconds == 0 {
- buf.WriteByte('Z')
- } else {
- offsetMinutes := offsetSeconds / 60
- if offsetMinutes < 0 {
- buf.WriteByte('-')
- offsetMinutes = -offsetMinutes
- } else {
- buf.WriteByte('+')
- }
- buf.WritePosIntWidth(offsetMinutes/60, 2)
- buf.WriteByte(':')
- buf.WritePosIntWidth(offsetMinutes%60, 2)
- }
-}