mirror of
https://github.com/yusing/godoxy.git
synced 2026-02-19 08:57:42 +01:00
This is a large-scale refactoring across the codebase that replaces the custom `gperr.Error` type with Go's standard `error` interface. The changes include: - Replacing `gperr.Error` return types with `error` in function signatures - Using `errors.New()` and `fmt.Errorf()` instead of `gperr.New()` and `gperr.Errorf()` - Using `%w` format verb for error wrapping instead of `.With()` method - Replacing `gperr.Subject()` calls with `gperr.PrependSubject()` - Converting error logging from `gperr.Log*()` functions to zerolog's `.Err().Msg()` pattern - Update NewLogger to handle multiline error message - Updating `goutils` submodule to latest commit This refactoring aligns with Go idioms and removes the dependency on custom error handling abstractions in favor of standard library patterns.
135 lines
3.0 KiB
Go
135 lines
3.0 KiB
Go
package logging
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/rs/zerolog"
|
|
"github.com/yusing/godoxy/internal/common"
|
|
|
|
"github.com/rs/zerolog/diode"
|
|
zerologlog "github.com/rs/zerolog/log"
|
|
)
|
|
|
|
func InitLogger(out ...io.Writer) {
|
|
logger = NewLogger(out...)
|
|
log.SetOutput(logger)
|
|
log.SetPrefix("")
|
|
log.SetFlags(0)
|
|
zerolog.TimeFieldFormat = timeFmt
|
|
zerologlog.Logger = logger
|
|
}
|
|
|
|
var (
|
|
logger zerolog.Logger
|
|
timeFmt string
|
|
level zerolog.Level
|
|
prefix string
|
|
)
|
|
|
|
func init() {
|
|
switch {
|
|
case common.IsTrace:
|
|
timeFmt = "04:05"
|
|
level = zerolog.TraceLevel
|
|
case common.IsDebug:
|
|
timeFmt = "01-02 15:04"
|
|
level = zerolog.DebugLevel
|
|
default:
|
|
timeFmt = "01-02 15:04"
|
|
level = zerolog.InfoLevel
|
|
}
|
|
prefixLength := len(timeFmt) + 5 // level takes 3 + 2 spaces
|
|
prefix = strings.Repeat(" ", prefixLength)
|
|
InitLogger(os.Stdout)
|
|
}
|
|
|
|
func fmtMessage(msg string) string {
|
|
nLines := strings.Count(msg, "\n")
|
|
if nLines == 0 {
|
|
return msg
|
|
}
|
|
|
|
var sb strings.Builder
|
|
sb.Grow(len(msg) + nLines*len(prefix))
|
|
|
|
// write first line unindented
|
|
idx := strings.IndexByte(msg, '\n')
|
|
sb.WriteString(msg[:idx])
|
|
sb.WriteByte('\n')
|
|
msg = msg[idx+1:]
|
|
|
|
// write remaining lines indented
|
|
for line := range strings.Lines(msg) {
|
|
sb.WriteString(prefix)
|
|
sb.WriteString(line)
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func diodeMultiWriter(out ...io.Writer) io.Writer {
|
|
return diode.NewWriter(multiWriter(out...), 1024, 0, func(missed int) {
|
|
zerologlog.Warn().Int("missed", missed).Msg("missed log messages")
|
|
})
|
|
}
|
|
|
|
func multiWriter(out ...io.Writer) io.Writer {
|
|
if len(out) == 0 {
|
|
return os.Stdout
|
|
}
|
|
if len(out) == 1 {
|
|
return out[0]
|
|
}
|
|
return io.MultiWriter(out...)
|
|
}
|
|
|
|
func NewLogger(out ...io.Writer) zerolog.Logger {
|
|
writer := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
|
if !common.IsTest {
|
|
w.Out = diodeMultiWriter(out...)
|
|
} else {
|
|
w.Out = multiWriter(out...)
|
|
}
|
|
w.TimeFormat = timeFmt
|
|
w.FormatPrepare = func(evt map[string]any) error {
|
|
// move error field to join message if it's multiline
|
|
if err, ok := evt[zerolog.ErrorFieldName].(string); ok {
|
|
if strings.Count(err, "\n") == 0 {
|
|
return nil
|
|
}
|
|
msg, ok := evt[zerolog.MessageFieldName].(string)
|
|
if ok && msg != "" {
|
|
evt[zerolog.MessageFieldName] = msg + "\n" + err
|
|
} else {
|
|
evt[zerolog.MessageFieldName] = err
|
|
}
|
|
delete(evt, zerolog.ErrorFieldName)
|
|
}
|
|
return nil
|
|
}
|
|
w.FormatMessage = func(msgI any) string { // pad spaces for each line
|
|
if msgI == nil {
|
|
return ""
|
|
}
|
|
return fmtMessage(msgI.(string))
|
|
}
|
|
})
|
|
return zerolog.New(writer).Level(level).With().Timestamp().Logger()
|
|
}
|
|
|
|
func NewLoggerWithFixedLevel(lvl zerolog.Level, out ...io.Writer) zerolog.Logger {
|
|
writer := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
|
w.Out = diodeMultiWriter(out...)
|
|
w.TimeFormat = timeFmt
|
|
w.FormatMessage = func(msgI any) string { // pad spaces for each line
|
|
if msgI == nil {
|
|
return ""
|
|
}
|
|
return fmtMessage(msgI.(string))
|
|
}
|
|
})
|
|
return zerolog.New(writer).Level(level).With().Str("level", lvl.String()).Timestamp().Logger()
|
|
}
|