123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- package logx
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "log"
- "path"
- "runtime/debug"
- "sync"
- "sync/atomic"
- fatihcolor "github.com/fatih/color"
- "github.com/wuntsong-org/go-zero-plus/core/color"
- )
- type (
- Writer interface {
- Alert(v any)
- Close() error
- Debug(v any, fields ...LogField)
- Error(v any, fields ...LogField)
- Info(v any, fields ...LogField)
- Severe(v any)
- Slow(v any, fields ...LogField)
- Stack(v any)
- Stat(v any, fields ...LogField)
- }
- atomicWriter struct {
- writer Writer
- lock sync.RWMutex
- }
- concreteWriter struct {
- infoLog io.WriteCloser
- errorLog io.WriteCloser
- severeLog io.WriteCloser
- slowLog io.WriteCloser
- statLog io.WriteCloser
- stackLog io.Writer
- }
- )
- // NewWriter creates a new Writer with the given io.Writer.
- func NewWriter(w io.Writer) Writer {
- lw := newLogWriter(log.New(w, "", flags))
- return &concreteWriter{
- infoLog: lw,
- errorLog: lw,
- severeLog: lw,
- slowLog: lw,
- statLog: lw,
- stackLog: lw,
- }
- }
- func (w *atomicWriter) Load() Writer {
- w.lock.RLock()
- defer w.lock.RUnlock()
- return w.writer
- }
- func (w *atomicWriter) Store(v Writer) {
- w.lock.Lock()
- defer w.lock.Unlock()
- w.writer = v
- }
- func (w *atomicWriter) StoreIfNil(v Writer) Writer {
- w.lock.Lock()
- defer w.lock.Unlock()
- if w.writer == nil {
- w.writer = v
- }
- return w.writer
- }
- func (w *atomicWriter) Swap(v Writer) Writer {
- w.lock.Lock()
- defer w.lock.Unlock()
- old := w.writer
- w.writer = v
- return old
- }
- func newConsoleWriter() Writer {
- outLog := newLogWriter(log.New(fatihcolor.Output, "", flags))
- errLog := newLogWriter(log.New(fatihcolor.Error, "", flags))
- return &concreteWriter{
- infoLog: outLog,
- errorLog: errLog,
- severeLog: errLog,
- slowLog: errLog,
- stackLog: newLessWriter(errLog, options.logStackCooldownMills),
- statLog: outLog,
- }
- }
- func newFileWriter(c LogConf) (Writer, error) {
- var err error
- var opts []LogOption
- var infoLog io.WriteCloser
- var errorLog io.WriteCloser
- var severeLog io.WriteCloser
- var slowLog io.WriteCloser
- var statLog io.WriteCloser
- var stackLog io.Writer
- if len(c.Path) == 0 {
- return nil, ErrLogPathNotSet
- }
- opts = append(opts, WithCooldownMillis(c.StackCooldownMillis))
- if c.Compress {
- opts = append(opts, WithGzip())
- }
- if c.KeepDays > 0 {
- opts = append(opts, WithKeepDays(c.KeepDays))
- }
- if c.MaxBackups > 0 {
- opts = append(opts, WithMaxBackups(c.MaxBackups))
- }
- if c.MaxSize > 0 {
- opts = append(opts, WithMaxSize(c.MaxSize))
- }
- opts = append(opts, WithRotation(c.Rotation))
- accessFile := path.Join(c.Path, accessFilename)
- errorFile := path.Join(c.Path, errorFilename)
- severeFile := path.Join(c.Path, severeFilename)
- slowFile := path.Join(c.Path, slowFilename)
- statFile := path.Join(c.Path, statFilename)
- handleOptions(opts)
- setupLogLevel(c)
- if infoLog, err = createOutput(accessFile); err != nil {
- return nil, err
- }
- if errorLog, err = createOutput(errorFile); err != nil {
- return nil, err
- }
- if severeLog, err = createOutput(severeFile); err != nil {
- return nil, err
- }
- if slowLog, err = createOutput(slowFile); err != nil {
- return nil, err
- }
- if statLog, err = createOutput(statFile); err != nil {
- return nil, err
- }
- stackLog = newLessWriter(errorLog, options.logStackCooldownMills)
- return &concreteWriter{
- infoLog: infoLog,
- errorLog: errorLog,
- severeLog: severeLog,
- slowLog: slowLog,
- statLog: statLog,
- stackLog: stackLog,
- }, nil
- }
- func (w *concreteWriter) Alert(v any) {
- output(w.errorLog, levelAlert, v)
- }
- func (w *concreteWriter) Close() error {
- if err := w.infoLog.Close(); err != nil {
- return err
- }
- if err := w.errorLog.Close(); err != nil {
- return err
- }
- if err := w.severeLog.Close(); err != nil {
- return err
- }
- if err := w.slowLog.Close(); err != nil {
- return err
- }
- return w.statLog.Close()
- }
- func (w *concreteWriter) Debug(v any, fields ...LogField) {
- output(w.infoLog, levelDebug, v, fields...)
- }
- func (w *concreteWriter) Error(v any, fields ...LogField) {
- output(w.errorLog, levelError, v, fields...)
- }
- func (w *concreteWriter) Info(v any, fields ...LogField) {
- output(w.infoLog, levelInfo, v, fields...)
- }
- func (w *concreteWriter) Severe(v any) {
- output(w.severeLog, levelFatal, v)
- }
- func (w *concreteWriter) Slow(v any, fields ...LogField) {
- output(w.slowLog, levelSlow, v, fields...)
- }
- func (w *concreteWriter) Stack(v any) {
- output(w.stackLog, levelError, v)
- }
- func (w *concreteWriter) Stat(v any, fields ...LogField) {
- output(w.statLog, levelStat, v, fields...)
- }
- type nopWriter struct{}
- func (n nopWriter) Alert(_ any) {
- }
- func (n nopWriter) Close() error {
- return nil
- }
- func (n nopWriter) Debug(_ any, _ ...LogField) {
- }
- func (n nopWriter) Error(_ any, _ ...LogField) {
- }
- func (n nopWriter) Info(_ any, _ ...LogField) {
- }
- func (n nopWriter) Severe(_ any) {
- }
- func (n nopWriter) Slow(_ any, _ ...LogField) {
- }
- func (n nopWriter) Stack(_ any) {
- }
- func (n nopWriter) Stat(_ any, _ ...LogField) {
- }
- func buildPlainFields(fields ...LogField) []string {
- var items []string
- for _, field := range fields {
- items = append(items, fmt.Sprintf("%s=%+v", field.Key, field.Value))
- }
- return items
- }
- func combineGlobalFields(fields []LogField) []LogField {
- globals := globalFields.Load()
- if globals == nil {
- return fields
- }
- gf := globals.([]LogField)
- ret := make([]LogField, 0, len(gf)+len(fields))
- ret = append(ret, gf...)
- ret = append(ret, fields...)
- return ret
- }
- func output(writer io.Writer, level string, val any, fields ...LogField) {
- // only truncate string content, don't know how to truncate the values of other types.
- if v, ok := val.(string); ok {
- maxLen := atomic.LoadUint32(&maxContentLength)
- if maxLen > 0 && len(v) > int(maxLen) {
- val = v[:maxLen]
- fields = append(fields, truncatedField)
- }
- }
- fields = combineGlobalFields(fields)
- switch atomic.LoadUint32(&encoding) {
- case plainEncodingType:
- writePlainAny(writer, level, val, buildPlainFields(fields...)...)
- default:
- entry := make(logEntry)
- for _, field := range fields {
- entry[field.Key] = field.Value
- }
- entry[timestampKey] = getTimestamp()
- entry[levelKey] = level
- entry[contentKey] = val
- writeJson(writer, entry)
- }
- }
- func wrapLevelWithColor(level string) string {
- var colour color.Color
- switch level {
- case levelAlert:
- colour = color.FgRed
- case levelError:
- colour = color.FgRed
- case levelFatal:
- colour = color.FgRed
- case levelInfo:
- colour = color.FgBlue
- case levelSlow:
- colour = color.FgYellow
- case levelDebug:
- colour = color.FgYellow
- case levelStat:
- colour = color.FgGreen
- }
- if colour == color.NoColor {
- return level
- }
- return color.WithColorPadding(level, colour)
- }
- func writeJson(writer io.Writer, info any) {
- if content, err := json.Marshal(info); err != nil {
- log.Printf("err: %s\n\n%s", err.Error(), debug.Stack())
- } else if writer == nil {
- log.Println(string(content))
- } else {
- if _, err := writer.Write(append(content, '\n')); err != nil {
- log.Println(err.Error())
- }
- }
- }
- func writePlainAny(writer io.Writer, level string, val any, fields ...string) {
- level = wrapLevelWithColor(level)
- switch v := val.(type) {
- case string:
- writePlainText(writer, level, v, fields...)
- case error:
- writePlainText(writer, level, v.Error(), fields...)
- case fmt.Stringer:
- writePlainText(writer, level, v.String(), fields...)
- default:
- writePlainValue(writer, level, v, fields...)
- }
- }
- func writePlainText(writer io.Writer, level, msg string, fields ...string) {
- var buf bytes.Buffer
- buf.WriteString(getTimestamp())
- buf.WriteByte(plainEncodingSep)
- buf.WriteString(level)
- buf.WriteByte(plainEncodingSep)
- buf.WriteString(msg)
- for _, item := range fields {
- buf.WriteByte(plainEncodingSep)
- buf.WriteString(item)
- }
- buf.WriteByte('\n')
- if writer == nil {
- log.Println(buf.String())
- return
- }
- if _, err := writer.Write(buf.Bytes()); err != nil {
- log.Println(err.Error())
- }
- }
- func writePlainValue(writer io.Writer, level string, val any, fields ...string) {
- var buf bytes.Buffer
- buf.WriteString(getTimestamp())
- buf.WriteByte(plainEncodingSep)
- buf.WriteString(level)
- buf.WriteByte(plainEncodingSep)
- if err := json.NewEncoder(&buf).Encode(val); err != nil {
- log.Printf("err: %s\n\n%s", err.Error(), debug.Stack())
- return
- }
- for _, item := range fields {
- buf.WriteByte(plainEncodingSep)
- buf.WriteString(item)
- }
- buf.WriteByte('\n')
- if writer == nil {
- log.Println(buf.String())
- return
- }
- if _, err := writer.Write(buf.Bytes()); err != nil {
- log.Println(err.Error())
- }
- }
|