github.com/lingyao2333/mo-zero@v1.4.1/core/logx/writer.go (about)

     1  package logx
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"path"
    10  	"sync"
    11  	"sync/atomic"
    12  
    13  	fatihcolor "github.com/fatih/color"
    14  	"github.com/lingyao2333/mo-zero/core/color"
    15  )
    16  
    17  type (
    18  	Writer interface {
    19  		Alert(v interface{})
    20  		Close() error
    21  		Debug(v interface{}, fields ...LogField)
    22  		Error(v interface{}, fields ...LogField)
    23  		Info(v interface{}, fields ...LogField)
    24  		Severe(v interface{})
    25  		Slow(v interface{}, fields ...LogField)
    26  		Stack(v interface{})
    27  		Stat(v interface{}, fields ...LogField)
    28  	}
    29  
    30  	atomicWriter struct {
    31  		writer Writer
    32  		lock   sync.RWMutex
    33  	}
    34  
    35  	concreteWriter struct {
    36  		infoLog   io.WriteCloser
    37  		errorLog  io.WriteCloser
    38  		severeLog io.WriteCloser
    39  		slowLog   io.WriteCloser
    40  		statLog   io.WriteCloser
    41  		stackLog  io.Writer
    42  	}
    43  )
    44  
    45  // NewWriter creates a new Writer with the given io.Writer.
    46  func NewWriter(w io.Writer) Writer {
    47  	lw := newLogWriter(log.New(w, "", flags))
    48  
    49  	return &concreteWriter{
    50  		infoLog:   lw,
    51  		errorLog:  lw,
    52  		severeLog: lw,
    53  		slowLog:   lw,
    54  		statLog:   lw,
    55  		stackLog:  lw,
    56  	}
    57  }
    58  
    59  func (w *atomicWriter) Load() Writer {
    60  	w.lock.RLock()
    61  	defer w.lock.RUnlock()
    62  	return w.writer
    63  }
    64  
    65  func (w *atomicWriter) Store(v Writer) {
    66  	w.lock.Lock()
    67  	defer w.lock.Unlock()
    68  	w.writer = v
    69  }
    70  
    71  func (w *atomicWriter) StoreIfNil(v Writer) Writer {
    72  	w.lock.Lock()
    73  	defer w.lock.Unlock()
    74  
    75  	if w.writer == nil {
    76  		w.writer = v
    77  	}
    78  
    79  	return w.writer
    80  }
    81  
    82  func (w *atomicWriter) Swap(v Writer) Writer {
    83  	w.lock.Lock()
    84  	defer w.lock.Unlock()
    85  	old := w.writer
    86  	w.writer = v
    87  	return old
    88  }
    89  
    90  func newConsoleWriter() Writer {
    91  	outLog := newLogWriter(log.New(fatihcolor.Output, "", flags))
    92  	errLog := newLogWriter(log.New(fatihcolor.Error, "", flags))
    93  	return &concreteWriter{
    94  		infoLog:   outLog,
    95  		errorLog:  errLog,
    96  		severeLog: errLog,
    97  		slowLog:   errLog,
    98  		stackLog:  newLessWriter(errLog, options.logStackCooldownMills),
    99  		statLog:   outLog,
   100  	}
   101  }
   102  
   103  func newFileWriter(c LogConf) (Writer, error) {
   104  	var err error
   105  	var opts []LogOption
   106  	var infoLog io.WriteCloser
   107  	var errorLog io.WriteCloser
   108  	var severeLog io.WriteCloser
   109  	var slowLog io.WriteCloser
   110  	var statLog io.WriteCloser
   111  	var stackLog io.Writer
   112  
   113  	if len(c.Path) == 0 {
   114  		return nil, ErrLogPathNotSet
   115  	}
   116  
   117  	opts = append(opts, WithCooldownMillis(c.StackCooldownMillis))
   118  	if c.Compress {
   119  		opts = append(opts, WithGzip())
   120  	}
   121  	if c.KeepDays > 0 {
   122  		opts = append(opts, WithKeepDays(c.KeepDays))
   123  	}
   124  	if c.MaxBackups > 0 {
   125  		opts = append(opts, WithMaxBackups(c.MaxBackups))
   126  	}
   127  	if c.MaxSize > 0 {
   128  		opts = append(opts, WithMaxSize(c.MaxSize))
   129  	}
   130  
   131  	opts = append(opts, WithRotation(c.Rotation))
   132  
   133  	accessFile := path.Join(c.Path, accessFilename)
   134  	errorFile := path.Join(c.Path, errorFilename)
   135  	severeFile := path.Join(c.Path, severeFilename)
   136  	slowFile := path.Join(c.Path, slowFilename)
   137  	statFile := path.Join(c.Path, statFilename)
   138  
   139  	handleOptions(opts)
   140  	setupLogLevel(c)
   141  
   142  	if infoLog, err = createOutput(accessFile); err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	if errorLog, err = createOutput(errorFile); err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	if severeLog, err = createOutput(severeFile); err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	if slowLog, err = createOutput(slowFile); err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	if statLog, err = createOutput(statFile); err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	stackLog = newLessWriter(errorLog, options.logStackCooldownMills)
   163  
   164  	return &concreteWriter{
   165  		infoLog:   infoLog,
   166  		errorLog:  errorLog,
   167  		severeLog: severeLog,
   168  		slowLog:   slowLog,
   169  		statLog:   statLog,
   170  		stackLog:  stackLog,
   171  	}, nil
   172  }
   173  
   174  func (w *concreteWriter) Alert(v interface{}) {
   175  	output(w.errorLog, levelAlert, v)
   176  }
   177  
   178  func (w *concreteWriter) Close() error {
   179  	if err := w.infoLog.Close(); err != nil {
   180  		return err
   181  	}
   182  
   183  	if err := w.errorLog.Close(); err != nil {
   184  		return err
   185  	}
   186  
   187  	if err := w.severeLog.Close(); err != nil {
   188  		return err
   189  	}
   190  
   191  	if err := w.slowLog.Close(); err != nil {
   192  		return err
   193  	}
   194  
   195  	return w.statLog.Close()
   196  }
   197  
   198  func (w *concreteWriter) Debug(v interface{}, fields ...LogField) {
   199  	output(w.infoLog, levelDebug, v, fields...)
   200  }
   201  
   202  func (w *concreteWriter) Error(v interface{}, fields ...LogField) {
   203  	output(w.errorLog, levelError, v, fields...)
   204  }
   205  
   206  func (w *concreteWriter) Info(v interface{}, fields ...LogField) {
   207  	output(w.infoLog, levelInfo, v, fields...)
   208  }
   209  
   210  func (w *concreteWriter) Severe(v interface{}) {
   211  	output(w.severeLog, levelFatal, v)
   212  }
   213  
   214  func (w *concreteWriter) Slow(v interface{}, fields ...LogField) {
   215  	output(w.slowLog, levelSlow, v, fields...)
   216  }
   217  
   218  func (w *concreteWriter) Stack(v interface{}) {
   219  	output(w.stackLog, levelError, v)
   220  }
   221  
   222  func (w *concreteWriter) Stat(v interface{}, fields ...LogField) {
   223  	output(w.statLog, levelStat, v, fields...)
   224  }
   225  
   226  type nopWriter struct{}
   227  
   228  func (n nopWriter) Alert(_ interface{}) {
   229  }
   230  
   231  func (n nopWriter) Close() error {
   232  	return nil
   233  }
   234  
   235  func (n nopWriter) Debug(_ interface{}, _ ...LogField) {
   236  }
   237  
   238  func (n nopWriter) Error(_ interface{}, _ ...LogField) {
   239  }
   240  
   241  func (n nopWriter) Info(_ interface{}, _ ...LogField) {
   242  }
   243  
   244  func (n nopWriter) Severe(_ interface{}) {
   245  }
   246  
   247  func (n nopWriter) Slow(_ interface{}, _ ...LogField) {
   248  }
   249  
   250  func (n nopWriter) Stack(_ interface{}) {
   251  }
   252  
   253  func (n nopWriter) Stat(_ interface{}, _ ...LogField) {
   254  }
   255  
   256  func buildFields(fields ...LogField) []string {
   257  	var items []string
   258  
   259  	for _, field := range fields {
   260  		items = append(items, fmt.Sprintf("%s=%v", field.Key, field.Value))
   261  	}
   262  
   263  	return items
   264  }
   265  
   266  func combineGlobalFields(fields []LogField) []LogField {
   267  	globals := globalFields.Load()
   268  	if globals == nil {
   269  		return fields
   270  	}
   271  
   272  	return append(globals.([]LogField), fields...)
   273  }
   274  
   275  func output(writer io.Writer, level string, val interface{}, fields ...LogField) {
   276  	fields = combineGlobalFields(fields)
   277  
   278  	switch atomic.LoadUint32(&encoding) {
   279  	case plainEncodingType:
   280  		writePlainAny(writer, level, val, buildFields(fields...)...)
   281  	default:
   282  		entry := make(logEntry)
   283  		for _, field := range fields {
   284  			entry[field.Key] = field.Value
   285  		}
   286  		entry[timestampKey] = getTimestamp()
   287  		entry[levelKey] = level
   288  		entry[contentKey] = val
   289  		writeJson(writer, entry)
   290  	}
   291  }
   292  
   293  func wrapLevelWithColor(level string) string {
   294  	var colour color.Color
   295  	switch level {
   296  	case levelAlert:
   297  		colour = color.FgRed
   298  	case levelError:
   299  		colour = color.FgRed
   300  	case levelFatal:
   301  		colour = color.FgRed
   302  	case levelInfo:
   303  		colour = color.FgBlue
   304  	case levelSlow:
   305  		colour = color.FgYellow
   306  	case levelDebug:
   307  		colour = color.FgYellow
   308  	case levelStat:
   309  		colour = color.FgGreen
   310  	}
   311  
   312  	if colour == color.NoColor {
   313  		return level
   314  	}
   315  
   316  	return color.WithColorPadding(level, colour)
   317  }
   318  
   319  func writeJson(writer io.Writer, info interface{}) {
   320  	if content, err := json.Marshal(info); err != nil {
   321  		log.Println(err.Error())
   322  	} else if writer == nil {
   323  		log.Println(string(content))
   324  	} else {
   325  		writer.Write(append(content, '\n'))
   326  	}
   327  }
   328  
   329  func writePlainAny(writer io.Writer, level string, val interface{}, fields ...string) {
   330  	level = wrapLevelWithColor(level)
   331  
   332  	switch v := val.(type) {
   333  	case string:
   334  		writePlainText(writer, level, v, fields...)
   335  	case error:
   336  		writePlainText(writer, level, v.Error(), fields...)
   337  	case fmt.Stringer:
   338  		writePlainText(writer, level, v.String(), fields...)
   339  	default:
   340  		writePlainValue(writer, level, v, fields...)
   341  	}
   342  }
   343  
   344  func writePlainText(writer io.Writer, level, msg string, fields ...string) {
   345  	var buf bytes.Buffer
   346  	buf.WriteString(getTimestamp())
   347  	buf.WriteByte(plainEncodingSep)
   348  	buf.WriteString(level)
   349  	buf.WriteByte(plainEncodingSep)
   350  	buf.WriteString(msg)
   351  	for _, item := range fields {
   352  		buf.WriteByte(plainEncodingSep)
   353  		buf.WriteString(item)
   354  	}
   355  	buf.WriteByte('\n')
   356  	if writer == nil {
   357  		log.Println(buf.String())
   358  		return
   359  	}
   360  
   361  	if _, err := writer.Write(buf.Bytes()); err != nil {
   362  		log.Println(err.Error())
   363  	}
   364  }
   365  
   366  func writePlainValue(writer io.Writer, level string, val interface{}, fields ...string) {
   367  	var buf bytes.Buffer
   368  	buf.WriteString(getTimestamp())
   369  	buf.WriteByte(plainEncodingSep)
   370  	buf.WriteString(level)
   371  	buf.WriteByte(plainEncodingSep)
   372  	if err := json.NewEncoder(&buf).Encode(val); err != nil {
   373  		log.Println(err.Error())
   374  		return
   375  	}
   376  
   377  	for _, item := range fields {
   378  		buf.WriteByte(plainEncodingSep)
   379  		buf.WriteString(item)
   380  	}
   381  	buf.WriteByte('\n')
   382  	if writer == nil {
   383  		log.Println(buf.String())
   384  		return
   385  	}
   386  
   387  	if _, err := writer.Write(buf.Bytes()); err != nil {
   388  		log.Println(err.Error())
   389  	}
   390  }