feat: add fields with logx methods, support using third party logging libs. (#1847)
* backup * simplify * chore: remove unused pool * chore: fix lint errors * chore: use strings.Builder instead of bytes.Buffer * test: add more tests * chore: fix reviewdog * test: fix data race * feat: make logger customizable * chore: fix reviewdog * test: fix fails * chore: fix set writer twice * chore: use context instead of golang.org context * chore: specify uint32 for level types
This commit is contained in:
320
core/logx/writer.go
Normal file
320
core/logx/writer.go
Normal file
@@ -0,0 +1,320 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type (
|
||||
Writer interface {
|
||||
Alert(v interface{})
|
||||
Close() error
|
||||
Error(v interface{}, fields ...LogField)
|
||||
Info(v interface{}, fields ...LogField)
|
||||
Severe(v interface{})
|
||||
Slow(v interface{}, fields ...LogField)
|
||||
Stack(v interface{})
|
||||
Stat(v interface{}, 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()
|
||||
w.writer = v
|
||||
w.lock.Unlock()
|
||||
}
|
||||
|
||||
func (w *atomicWriter) Swap(v Writer) Writer {
|
||||
w.lock.Lock()
|
||||
old := w.writer
|
||||
w.writer = v
|
||||
w.lock.Unlock()
|
||||
return old
|
||||
}
|
||||
|
||||
func newConsoleWriter() Writer {
|
||||
outLog := newLogWriter(log.New(os.Stdout, "", flags))
|
||||
errLog := newLogWriter(log.New(os.Stderr, "", 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))
|
||||
}
|
||||
|
||||
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 interface{}) {
|
||||
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) Error(v interface{}, fields ...LogField) {
|
||||
output(w.errorLog, levelError, v, fields...)
|
||||
}
|
||||
|
||||
func (w *concreteWriter) Info(v interface{}, fields ...LogField) {
|
||||
output(w.infoLog, levelInfo, v, fields...)
|
||||
}
|
||||
|
||||
func (w *concreteWriter) Severe(v interface{}) {
|
||||
output(w.severeLog, levelFatal, v)
|
||||
}
|
||||
|
||||
func (w *concreteWriter) Slow(v interface{}, fields ...LogField) {
|
||||
output(w.slowLog, levelSlow, v, fields...)
|
||||
}
|
||||
|
||||
func (w *concreteWriter) Stack(v interface{}) {
|
||||
output(w.stackLog, levelError, v)
|
||||
}
|
||||
|
||||
func (w *concreteWriter) Stat(v interface{}, fields ...LogField) {
|
||||
output(w.statLog, levelStat, v, fields...)
|
||||
}
|
||||
|
||||
type nopWriter struct{}
|
||||
|
||||
func (n nopWriter) Alert(_ interface{}) {
|
||||
}
|
||||
|
||||
func (n nopWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n nopWriter) Error(_ interface{}, _ ...LogField) {
|
||||
}
|
||||
|
||||
func (n nopWriter) Info(_ interface{}, _ ...LogField) {
|
||||
}
|
||||
|
||||
func (n nopWriter) Severe(_ interface{}) {
|
||||
}
|
||||
|
||||
func (n nopWriter) Slow(_ interface{}, _ ...LogField) {
|
||||
}
|
||||
|
||||
func (n nopWriter) Stack(_ interface{}) {
|
||||
}
|
||||
|
||||
func (n nopWriter) Stat(_ interface{}, _ ...LogField) {
|
||||
}
|
||||
|
||||
func buildFields(fields ...LogField) []string {
|
||||
var items []string
|
||||
|
||||
for _, field := range fields {
|
||||
items = append(items, fmt.Sprintf("%s=%v", field.Key, field.Value))
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
func output(writer io.Writer, level string, val interface{}, fields ...LogField) {
|
||||
fields = append(fields, Field(callerKey, getCaller(callerDepth)))
|
||||
|
||||
switch atomic.LoadUint32(&encoding) {
|
||||
case plainEncodingType:
|
||||
writePlainAny(writer, level, val, buildFields(fields...)...)
|
||||
default:
|
||||
entry := make(logEntryWithFields)
|
||||
for _, field := range fields {
|
||||
entry[field.Key] = field.Value
|
||||
}
|
||||
entry[timestampKey] = getTimestamp()
|
||||
entry[levelKey] = level
|
||||
entry[contentKey] = val
|
||||
writeJson(writer, entry)
|
||||
}
|
||||
}
|
||||
|
||||
func writeJson(writer io.Writer, info interface{}) {
|
||||
if content, err := json.Marshal(info); err != nil {
|
||||
log.Println(err.Error())
|
||||
} else if writer == nil {
|
||||
log.Println(string(content))
|
||||
} else {
|
||||
writer.Write(append(content, '\n'))
|
||||
}
|
||||
}
|
||||
|
||||
func writePlainAny(writer io.Writer, level string, val interface{}, fields ...string) {
|
||||
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:
|
||||
var buf strings.Builder
|
||||
buf.WriteString(getTimestamp())
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
buf.WriteString(level)
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
if err := json.NewEncoder(&buf).Encode(val); err != nil {
|
||||
log.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range fields {
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
buf.WriteString(item)
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
if writer == nil {
|
||||
log.Println(buf.String())
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(writer, buf.String()); err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writePlainText(writer io.Writer, level, msg string, fields ...string) {
|
||||
var buf strings.Builder
|
||||
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 := fmt.Fprint(writer, buf.String()); err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user