1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-12-23 22:11:10 +02:00
Files
imgproxy/logger/formatter_structured.go
2025-09-21 20:13:27 +06:00

134 lines
3.1 KiB
Go

package logger
import (
"log/slog"
)
// formatterStructured is a flat structured log formatter.
type formatterStructured struct {
formatterCommon
// Current group prefix
prefix *buffer
}
// newFormatterStructured creates a new formatterStructured instance.
func newFormatterStructured(groups []attrGroup, buf *buffer) *formatterStructured {
return &formatterStructured{
formatterCommon: newFormatterCommon(groups, buf),
}
}
// format formats a log record.
func (s *formatterStructured) format(r slog.Record) {
s.prefix = newBuffer()
defer func() {
s.prefix.free()
}()
// Append timestamp
s.appendKey(slog.TimeKey)
s.appendTime(r.Time)
// Append log level
s.appendKey(slog.LevelKey)
s.appendString(s.levelName(r.Level), true)
// Append message
s.appendKey(slog.MessageKey)
s.appendString(r.Message, true)
// Append groups added with [Handler.WithAttrs] and [Handler.WithGroup]
for _, g := range s.groups {
if g.name != "" {
s.openGroup(g.name)
}
s.appendAttributes(g.attrs)
}
// Append attributes from the record
r.Attrs(func(attr slog.Attr) bool {
s.appendAttribute(attr)
return true
})
// Append error, source, and stack if present
if s.error.Key != "" {
s.appendKey(s.error.Key)
s.appendValue(s.error.Value, false)
}
if s.source.Key != "" {
s.appendKey(s.source.Key)
s.appendValue(s.source.Value, false)
}
if s.stack.Key != "" {
s.appendKey(s.stack.Key)
s.appendValue(s.stack.Value, false)
}
}
// appendAttributes appends a list of attributes to the buffer.
func (s *formatterStructured) appendAttributes(attrs []slog.Attr) {
for _, attr := range attrs {
s.appendAttribute(attr)
}
}
// appendAttribute appends a single attribute to the buffer.
func (s *formatterStructured) appendAttribute(attr slog.Attr) {
// Resolve [slog.LogValuer] values
attr.Value = attr.Value.Resolve()
// If there are no groups opened, save special attributes for later
if s.prefix.len() == 0 && s.saveSpecialAttr(attr) {
return
}
// Groups need special handling
if attr.Value.Kind() == slog.KindGroup {
s.appendGroup(attr.Key, attr.Value.Group())
return
}
s.appendKey(attr.Key)
s.appendValue(attr.Value, true)
}
// appendKey appends an attribute key to the buffer.
func (s *formatterStructured) appendKey(key string) {
if len(*s.buf) > 0 {
s.buf.append(' ')
}
s.buf.appendString(s.prefix.String() + key)
s.buf.append('=')
}
// appendGroup appends a group of attributes to the buffer.
func (s *formatterStructured) appendGroup(name string, attrs []slog.Attr) {
if len(attrs) == 0 {
return
}
if len(name) > 0 {
// If the group has a name, open it and defer closing it.
// Unnamed groups should be treated as sets of regular attributes.
s.openGroup(name)
defer s.closeGroup(name)
}
s.appendAttributes(attrs)
}
// openGroup opens a new group of attributes.
func (s *formatterStructured) openGroup(name string) {
s.prefix.appendStringRaw(name)
s.prefix.append('.')
}
// closeGroup closes the most recently opened group of attributes.
func (s *formatterStructured) closeGroup(name string) {
s.prefix.remove(len(name) + 1) // +1 for the dot
}