Test / test (push) Canceled after 0s
Test / fuzz (./compat, FuzzParseFormat) (push) Canceled after 0s
Test / fuzz (./formatter, FuzzFormatJSON) (push) Canceled after 0s
Test / fuzz (./formatter, FuzzFormatTextPolicy) (push) Canceled after 0s
Test / fuzz (./sanitizer, FuzzSanitize) (push) Canceled after 0s
Test / fuzz (./sanitizer, FuzzSerializerJSON) (push) Canceled after 0s
114 lines
3.0 KiB
Go
114 lines
3.0 KiB
Go
package compat
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/lixenwraith/log"
|
|
)
|
|
|
|
// Only unindexed, single-argument verbs are eligible for extraction. Mixed
|
|
// printf expressions fall back intact, rather than attributing an argument to
|
|
// the wrong key (width, precision and explicit indexes may consume arguments).
|
|
var keyValuePattern = regexp.MustCompile(`(\w+)\s*[:=]\s*%[vsdqxXeEfFgGpbcU]`)
|
|
|
|
func parseFormat(format string, args []any) []any {
|
|
fallback := func() []any { return []any{"msg", fmt.Sprintf(format, args...)} }
|
|
matches := keyValuePattern.FindAllStringSubmatchIndex(format, -1)
|
|
if len(matches) == 0 || len(matches) != len(args) {
|
|
return fallback()
|
|
}
|
|
fields := make([]any, 0, len(matches)*2+2)
|
|
var text []string
|
|
end := 0
|
|
for i, m := range matches {
|
|
gap := format[end:m[0]]
|
|
if strings.Contains(gap, "%") {
|
|
return fallback()
|
|
}
|
|
if part := strings.TrimSpace(gap); part != "" {
|
|
text = append(text, part)
|
|
}
|
|
fields = append(fields, format[m[2]:m[3]], args[i])
|
|
end = m[1]
|
|
}
|
|
tail := format[end:]
|
|
if strings.Contains(tail, "%") {
|
|
return fallback()
|
|
}
|
|
if part := strings.TrimSpace(tail); part != "" {
|
|
text = append(text, part)
|
|
}
|
|
if len(text) > 0 {
|
|
fields = append([]any{"msg", strings.Join(text, " ")}, fields...)
|
|
}
|
|
return fields
|
|
}
|
|
|
|
// StructuredGnetAdapter provides enhanced structured logging for gnet
|
|
type StructuredGnetAdapter struct {
|
|
*GnetAdapter
|
|
extractFields bool
|
|
}
|
|
|
|
// NewStructuredGnetAdapter creates a gnet adapter with structured field extraction
|
|
func NewStructuredGnetAdapter(logger *log.Logger, opts ...GnetOption) *StructuredGnetAdapter {
|
|
return &StructuredGnetAdapter{
|
|
GnetAdapter: NewGnetAdapter(logger, opts...),
|
|
extractFields: true,
|
|
}
|
|
}
|
|
|
|
// Debugf logs with structured field extraction
|
|
func (a *StructuredGnetAdapter) Debugf(format string, args ...any) {
|
|
if !a.logger.Enabled(log.LevelDebug) {
|
|
return
|
|
}
|
|
if a.extractFields {
|
|
fields := parseFormat(format, args)
|
|
a.logger.Debug(append(fields, "source", "gnet")...)
|
|
} else {
|
|
a.GnetAdapter.Debugf(format, args...)
|
|
}
|
|
}
|
|
|
|
// Infof logs with structured field extraction
|
|
func (a *StructuredGnetAdapter) Infof(format string, args ...any) {
|
|
if !a.logger.Enabled(log.LevelInfo) {
|
|
return
|
|
}
|
|
if a.extractFields {
|
|
fields := parseFormat(format, args)
|
|
a.logger.Info(append(fields, "source", "gnet")...)
|
|
} else {
|
|
a.GnetAdapter.Infof(format, args...)
|
|
}
|
|
}
|
|
|
|
// Warnf logs with structured field extraction
|
|
func (a *StructuredGnetAdapter) Warnf(format string, args ...any) {
|
|
if !a.logger.Enabled(log.LevelWarn) {
|
|
return
|
|
}
|
|
if a.extractFields {
|
|
fields := parseFormat(format, args)
|
|
a.logger.Warn(append(fields, "source", "gnet")...)
|
|
} else {
|
|
a.GnetAdapter.Warnf(format, args...)
|
|
}
|
|
}
|
|
|
|
// Errorf logs with structured field extraction
|
|
func (a *StructuredGnetAdapter) Errorf(format string, args ...any) {
|
|
if !a.logger.Enabled(log.LevelError) {
|
|
return
|
|
}
|
|
if a.extractFields {
|
|
fields := parseFormat(format, args)
|
|
a.logger.Error(append(fields, "source", "gnet")...)
|
|
} else {
|
|
a.GnetAdapter.Errorf(format, args...)
|
|
}
|
|
}
|