131 lines
3.5 KiB
Go
131 lines
3.5 KiB
Go
// FILE: lixenwraith/log/compat/structured.go
|
|
package compat
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/lixenwraith/log"
|
|
)
|
|
|
|
// parseFormat attempts to extract structured fields from printf-style format strings
|
|
// Useful for preserving structured logging semantics
|
|
func parseFormat(format string, args []any) []any {
|
|
// Pattern to detect common structured patterns like "key=%v" or "key: %v"
|
|
keyValuePattern := regexp.MustCompile(`(\w+)\s*[:=]\s*%[vsdqxXeEfFgGpbcU]`)
|
|
|
|
matches := keyValuePattern.FindAllStringSubmatchIndex(format, -1)
|
|
if len(matches) == 0 || len(matches) > len(args) {
|
|
// Fallback to simple message if pattern doesn't match
|
|
return []any{"msg", fmt.Sprintf(format, args...)}
|
|
}
|
|
|
|
// Build structured fields
|
|
fields := make([]any, 0, len(matches)*2+2)
|
|
lastEnd := 0
|
|
argIndex := 0
|
|
|
|
for _, match := range matches {
|
|
// Add any text before this match as part of the message
|
|
if match[0] > lastEnd {
|
|
prefix := format[lastEnd:match[0]]
|
|
if strings.TrimSpace(prefix) != "" {
|
|
if len(fields) == 0 {
|
|
fields = append(fields, "msg", strings.TrimSpace(prefix))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extract key name
|
|
keyStart := match[2]
|
|
keyEnd := match[3]
|
|
key := format[keyStart:keyEnd]
|
|
|
|
// Get corresponding value
|
|
if argIndex < len(args) {
|
|
fields = append(fields, key, args[argIndex])
|
|
argIndex++
|
|
}
|
|
|
|
lastEnd = match[1]
|
|
}
|
|
|
|
// Handle remaining format string and args
|
|
if lastEnd < len(format) {
|
|
remainingFormat := format[lastEnd:]
|
|
remainingArgs := args[argIndex:]
|
|
if len(remainingArgs) > 0 {
|
|
remaining := fmt.Sprintf(remainingFormat, remainingArgs...)
|
|
if strings.TrimSpace(remaining) != "" {
|
|
if len(fields) == 0 {
|
|
fields = append(fields, "msg", strings.TrimSpace(remaining))
|
|
} else {
|
|
// Append to existing message
|
|
for i := 0; i < len(fields); i += 2 {
|
|
if fields[i] == "msg" {
|
|
fields[i+1] = fmt.Sprintf("%v %s", fields[i+1], strings.TrimSpace(remaining))
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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.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.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.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.extractFields {
|
|
fields := parseFormat(format, args)
|
|
a.logger.Error(append(fields, "source", "gnet")...)
|
|
} else {
|
|
a.GnetAdapter.Errorf(format, args...)
|
|
}
|
|
} |