// 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 (
	
	
	
	
	
	
	
	
	
)

// 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(ctx, level, msg, slog.Int("a", 1), slog.Int("b", 2))
	//
	// should behave like
	//
	//     logger.LogAttrs(ctx, 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
	// internal.DefaultOutput, except for testing
	output func(pc uintptr, data []byte) error
}

func newDefaultHandler( func(uintptr, []byte) error) *defaultHandler {
	return &defaultHandler{
		ch:     &commonHandler{json: false},
		output: ,
	}
}

func (*defaultHandler) ( context.Context,  Level) bool {
	return  >= logLoggerLevel.Level()
}

// 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 ( *defaultHandler) ( context.Context,  Record) error {
	 := buffer.New()
	.WriteString(.Level.String())
	.WriteByte(' ')
	.WriteString(.Message)
	 := .ch.newHandleState(, true, " ")
	defer .free()
	.appendNonBuiltIns()
	return .output(.PC, *)
}

func ( *defaultHandler) ( []Attr) Handler {
	return &defaultHandler{.ch.withAttrs(), .output}
}

func ( *defaultHandler) ( string) Handler {
	return &defaultHandler{.ch.withGroup(), .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 a zero Attr, 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 *[Source].
	SourceKey = "source"
)

type commonHandler struct {
	json              bool // true => output JSON; false => output text
	opts              HandlerOptions
	preformattedAttrs []byte
	// groupPrefix is for the text handler only.
	// It holds the prefix for groups that were already pre-formatted.
	// A group will appear here when a call to WithGroup is followed by
	// a call to WithAttrs.
	groupPrefix string
	groups      []string // all groups started from WithGroup
	nOpenGroups int      // the number of groups opened in preformattedAttrs
	mu          *sync.Mutex
	w           io.Writer
}

func ( *commonHandler) () *commonHandler {
	// We can't use assignment because we can't copy the mutex.
	return &commonHandler{
		json:              .json,
		opts:              .opts,
		preformattedAttrs: slices.Clip(.preformattedAttrs),
		groupPrefix:       .groupPrefix,
		groups:            slices.Clip(.groups),
		nOpenGroups:       .nOpenGroups,
		w:                 .w,
		mu:                .mu, // mutex shared among all clones of this handler
	}
}

// enabled reports whether l is greater than or equal to the
// minimum level.
func ( *commonHandler) ( Level) bool {
	 := LevelInfo
	if .opts.Level != nil {
		 = .opts.Level.Level()
	}
	return  >= 
}

func ( *commonHandler) ( []Attr) *commonHandler {
	// We are going to ignore empty groups, so if the entire slice consists of
	// them, there is nothing to do.
	if countEmptyGroups() == len() {
		return 
	}
	 := .clone()
	// Pre-format the attributes as an optimization.
	 := .newHandleState((*buffer.Buffer)(&.preformattedAttrs), false, "")
	defer .free()
	.prefix.WriteString(.groupPrefix)
	if  := .preformattedAttrs; len() > 0 {
		.sep = .attrSep()
		if .json && [len()-1] == '{' {
			.sep = ""
		}
	}
	// Remember the position in the buffer, in case all attrs are empty.
	 := .buf.Len()
	.openGroups()
	if !.appendAttrs() {
		.buf.SetLen()
	} else {
		// Remember the new prefix for later keys.
		.groupPrefix = .prefix.String()
		// Remember how many opened groups are in preformattedAttrs,
		// so we don't open them again when we handle a Record.
		.nOpenGroups = len(.groups)
	}
	return 
}

func ( *commonHandler) ( string) *commonHandler {
	 := .clone()
	.groups = append(.groups, )
	return 
}

// handle is the internal implementation of Handler.Handle
// used by TextHandler and JSONHandler.
func ( *commonHandler) ( Record) error {
	 := .newHandleState(buffer.New(), true, "")
	defer .free()
	if .json {
		.buf.WriteByte('{')
	}
	// Built-in attributes. They are not in a group.
	 := .groups
	.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups.
	 := .opts.ReplaceAttr
	// time
	if !.Time.IsZero() {
		 := TimeKey
		 := .Time.Round(0) // strip monotonic to match Attr behavior
		if  == nil {
			.appendKey()
			.appendTime()
		} else {
			.appendAttr(Time(, ))
		}
	}
	// level
	 := LevelKey
	 := .Level
	if  == nil {
		.appendKey()
		.appendString(.String())
	} else {
		.appendAttr(Any(, ))
	}
	// source
	if .opts.AddSource {
		.appendAttr(Any(SourceKey, .source()))
	}
	 = MessageKey
	 := .Message
	if  == nil {
		.appendKey()
		.appendString()
	} else {
		.appendAttr(String(, ))
	}
	.groups =  // Restore groups passed to ReplaceAttrs.
	.appendNonBuiltIns()
	.buf.WriteByte('\n')

	.mu.Lock()
	defer .mu.Unlock()
	,  := .w.Write(*.buf)
	return 
}

func ( *handleState) ( Record) {
	// preformatted Attrs
	if  := .h.preformattedAttrs; len() > 0 {
		.buf.WriteString(.sep)
		.buf.Write()
		.sep = .h.attrSep()
		if .h.json && [len()-1] == '{' {
			.sep = ""
		}
	}
	// Attrs in Record -- unlike the built-in ones, they are in groups started
	// from WithGroup.
	// If the record has no Attrs, don't output any groups.
	 := .h.nOpenGroups
	if .NumAttrs() > 0 {
		.prefix.WriteString(.h.groupPrefix)
		// The group may turn out to be empty even though it has attrs (for
		// example, ReplaceAttr may delete all the attrs).
		// So remember where we are in the buffer, to restore the position
		// later if necessary.
		 := .buf.Len()
		.openGroups()
		 = len(.h.groups)
		 := true
		.Attrs(func( Attr) bool {
			if .appendAttr() {
				 = false
			}
			return true
		})
		if  {
			.buf.SetLen()
			 = .h.nOpenGroups
		}
	}
	if .h.json {
		// Close all open groups.
		for range .h.groups[:] {
			.buf.WriteByte('}')
		}
		// Close the top-level object.
		.buf.WriteByte('}')
	}
}

// attrSep returns the separator between attributes.
func ( *commonHandler) () string {
	if .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 {
	 := make([]string, 0, 10)
	return &
}}

func ( *commonHandler) ( *buffer.Buffer,  bool,  string) handleState {
	 := handleState{
		h:       ,
		buf:     ,
		freeBuf: ,
		sep:     ,
		prefix:  buffer.New(),
	}
	if .opts.ReplaceAttr != nil {
		.groups = groupPool.Get().(*[]string)
		*.groups = append(*.groups, .groups[:.nOpenGroups]...)
	}
	return 
}

func ( *handleState) () {
	if .freeBuf {
		.buf.Free()
	}
	if  := .groups;  != nil {
		* = (*)[:0]
		groupPool.Put()
	}
	.prefix.Free()
}

func ( *handleState) () {
	for ,  := range .h.groups[.h.nOpenGroups:] {
		.openGroup()
	}
}

// Separator for group names and keys.
const keyComponentSep = '.'

// openGroup starts a new group of attributes
// with the given name.
func ( *handleState) ( string) {
	if .h.json {
		.appendKey()
		.buf.WriteByte('{')
		.sep = ""
	} else {
		.prefix.WriteString()
		.prefix.WriteByte(keyComponentSep)
	}
	// Collect group names for ReplaceAttr.
	if .groups != nil {
		*.groups = append(*.groups, )
	}
}

// closeGroup ends the group with the given name.
func ( *handleState) ( string) {
	if .h.json {
		.buf.WriteByte('}')
	} else {
		(*.prefix) = (*.prefix)[:len(*.prefix)-len()-1 /* for keyComponentSep */]
	}
	.sep = .h.attrSep()
	if .groups != nil {
		*.groups = (*.groups)[:len(*.groups)-1]
	}
}

// appendAttrs appends the slice of Attrs.
// It reports whether something was appended.
func ( *handleState) ( []Attr) bool {
	 := false
	for ,  := range  {
		if .appendAttr() {
			 = true
		}
	}
	return 
}

// appendAttr appends the Attr's key and value.
// It handles replacement and checking for an empty key.
// It reports whether something was appended.
func ( *handleState) ( Attr) bool {
	.Value = .Value.Resolve()
	if  := .h.opts.ReplaceAttr;  != nil && .Value.Kind() != KindGroup {
		var  []string
		if .groups != nil {
			 = *.groups
		}
		// a.Value is resolved before calling ReplaceAttr, so the user doesn't have to.
		 = (, )
		// The ReplaceAttr function may return an unresolved Attr.
		.Value = .Value.Resolve()
	}
	// Elide empty Attrs.
	if .isEmpty() {
		return false
	}
	// Special case: Source.
	if  := .Value; .Kind() == KindAny {
		if ,  := .Any().(*Source);  {
			if .h.json {
				.Value = .group()
			} else {
				.Value = StringValue(fmt.Sprintf("%s:%d", .File, .Line))
			}
		}
	}
	if .Value.Kind() == KindGroup {
		 := .Value.Group()
		// Output only non-empty groups.
		if len() > 0 {
			// The group may turn out to be empty even though it has attrs (for
			// example, ReplaceAttr may delete all the attrs).
			// So remember where we are in the buffer, to restore the position
			// later if necessary.
			 := .buf.Len()
			// Inline a group with an empty key.
			if .Key != "" {
				.openGroup(.Key)
			}
			if !.appendAttrs() {
				.buf.SetLen()
				return false
			}
			if .Key != "" {
				.closeGroup(.Key)
			}
		}
	} else {
		.appendKey(.Key)
		.appendValue(.Value)
	}
	return true
}

func ( *handleState) ( error) {
	.appendString(fmt.Sprintf("!ERROR:%v", ))
}

func ( *handleState) ( string) {
	.buf.WriteString(.sep)
	if .prefix != nil && len(*.prefix) > 0 {
		// TODO: optimize by avoiding allocation.
		.appendString(string(*.prefix) + )
	} else {
		.appendString()
	}
	if .h.json {
		.buf.WriteByte(':')
	} else {
		.buf.WriteByte('=')
	}
	.sep = .h.attrSep()
}

func ( *handleState) ( string) {
	if .h.json {
		.buf.WriteByte('"')
		*.buf = appendEscapedJSONString(*.buf, )
		.buf.WriteByte('"')
	} else {
		// text
		if needsQuoting() {
			*.buf = strconv.AppendQuote(*.buf, )
		} else {
			.buf.WriteString()
		}
	}
}

func ( *handleState) ( Value) {
	defer func() {
		if  := recover();  != 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  := reflect.ValueOf(.any); .Kind() == reflect.Pointer && .IsNil() {
				.appendString("<nil>")
				return
			}

			// Otherwise just print the original panic message.
			.appendString(fmt.Sprintf("!PANIC: %v", ))
		}
	}()

	var  error
	if .h.json {
		 = appendJSONValue(, )
	} else {
		 = appendTextValue(, )
	}
	if  != nil {
		.appendError()
	}
}

func ( *handleState) ( time.Time) {
	if .h.json {
		appendJSONTime(, )
	} else {
		*.buf = appendRFC3339Millis(*.buf, )
	}
}

func appendRFC3339Millis( []byte,  time.Time) []byte {
	// Format according to time.RFC3339Nano since it is highly optimized,
	// but truncate it to use millisecond resolution.
	// Unfortunately, that format trims trailing 0s, so add 1/10 millisecond
	// to guarantee that there are exactly 4 digits after the period.
	const  = len("2006-01-02T15:04:05.000")
	 := len()
	 = .Truncate(time.Millisecond).Add(time.Millisecond / 10)
	 = .AppendFormat(, time.RFC3339Nano)
	 = append([:+], [++1:]...) // drop the 4th digit
	return 
}