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

     1  package logx
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"path"
     9  	"runtime/debug"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/lingyao2333/mo-zero/core/sysx"
    15  )
    16  
    17  const callerDepth = 4
    18  
    19  var (
    20  	timeFormat = "2006-01-02T15:04:05.000Z07:00"
    21  	logLevel   uint32
    22  	encoding   uint32 = jsonEncodingType
    23  	// use uint32 for atomic operations
    24  	disableLog  uint32
    25  	disableStat uint32
    26  	options     logOptions
    27  	writer      = new(atomicWriter)
    28  	setupOnce   sync.Once
    29  )
    30  
    31  type (
    32  	// LogField is a key-value pair that will be added to the log entry.
    33  	LogField struct {
    34  		Key   string
    35  		Value interface{}
    36  	}
    37  
    38  	// LogOption defines the method to customize the logging.
    39  	LogOption func(options *logOptions)
    40  
    41  	logEntry map[string]interface{}
    42  
    43  	logOptions struct {
    44  		gzipEnabled           bool
    45  		logStackCooldownMills int
    46  		keepDays              int
    47  		maxBackups            int
    48  		maxSize               int
    49  		rotationRule          string
    50  	}
    51  )
    52  
    53  // Alert alerts v in alert level, and the message is written to error log.
    54  func Alert(v string) {
    55  	getWriter().Alert(v)
    56  }
    57  
    58  // Close closes the logging.
    59  func Close() error {
    60  	if w := writer.Swap(nil); w != nil {
    61  		return w.(io.Closer).Close()
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  // Debug writes v into access log.
    68  func Debug(v ...interface{}) {
    69  	writeDebug(fmt.Sprint(v...))
    70  }
    71  
    72  // Debugf writes v with format into access log.
    73  func Debugf(format string, v ...interface{}) {
    74  	writeDebug(fmt.Sprintf(format, v...))
    75  }
    76  
    77  // Debugv writes v into access log with json content.
    78  func Debugv(v interface{}) {
    79  	writeDebug(v)
    80  }
    81  
    82  // Debugw writes msg along with fields into access log.
    83  func Debugw(msg string, fields ...LogField) {
    84  	writeDebug(msg, fields...)
    85  }
    86  
    87  // Disable disables the logging.
    88  func Disable() {
    89  	atomic.StoreUint32(&disableLog, 1)
    90  	writer.Store(nopWriter{})
    91  }
    92  
    93  // DisableStat disables the stat logs.
    94  func DisableStat() {
    95  	atomic.StoreUint32(&disableStat, 1)
    96  }
    97  
    98  // Error writes v into error log.
    99  func Error(v ...interface{}) {
   100  	writeError(fmt.Sprint(v...))
   101  }
   102  
   103  // Errorf writes v with format into error log.
   104  func Errorf(format string, v ...interface{}) {
   105  	writeError(fmt.Errorf(format, v...).Error())
   106  }
   107  
   108  // ErrorStack writes v along with call stack into error log.
   109  func ErrorStack(v ...interface{}) {
   110  	// there is newline in stack string
   111  	writeStack(fmt.Sprint(v...))
   112  }
   113  
   114  // ErrorStackf writes v along with call stack in format into error log.
   115  func ErrorStackf(format string, v ...interface{}) {
   116  	// there is newline in stack string
   117  	writeStack(fmt.Sprintf(format, v...))
   118  }
   119  
   120  // Errorv writes v into error log with json content.
   121  // No call stack attached, because not elegant to pack the messages.
   122  func Errorv(v interface{}) {
   123  	writeError(v)
   124  }
   125  
   126  // Errorw writes msg along with fields into error log.
   127  func Errorw(msg string, fields ...LogField) {
   128  	writeError(msg, fields...)
   129  }
   130  
   131  // Field returns a LogField for the given key and value.
   132  func Field(key string, value interface{}) LogField {
   133  	switch val := value.(type) {
   134  	case error:
   135  		return LogField{Key: key, Value: val.Error()}
   136  	case []error:
   137  		var errs []string
   138  		for _, err := range val {
   139  			errs = append(errs, err.Error())
   140  		}
   141  		return LogField{Key: key, Value: errs}
   142  	case time.Duration:
   143  		return LogField{Key: key, Value: fmt.Sprint(val)}
   144  	case []time.Duration:
   145  		var durs []string
   146  		for _, dur := range val {
   147  			durs = append(durs, fmt.Sprint(dur))
   148  		}
   149  		return LogField{Key: key, Value: durs}
   150  	case []time.Time:
   151  		var times []string
   152  		for _, t := range val {
   153  			times = append(times, fmt.Sprint(t))
   154  		}
   155  		return LogField{Key: key, Value: times}
   156  	case fmt.Stringer:
   157  		return LogField{Key: key, Value: val.String()}
   158  	case []fmt.Stringer:
   159  		var strs []string
   160  		for _, str := range val {
   161  			strs = append(strs, str.String())
   162  		}
   163  		return LogField{Key: key, Value: strs}
   164  	default:
   165  		return LogField{Key: key, Value: val}
   166  	}
   167  }
   168  
   169  // Info writes v into access log.
   170  func Info(v ...interface{}) {
   171  	writeInfo(fmt.Sprint(v...))
   172  }
   173  
   174  // Infof writes v with format into access log.
   175  func Infof(format string, v ...interface{}) {
   176  	writeInfo(fmt.Sprintf(format, v...))
   177  }
   178  
   179  // Infov writes v into access log with json content.
   180  func Infov(v interface{}) {
   181  	writeInfo(v)
   182  }
   183  
   184  // Infow writes msg along with fields into access log.
   185  func Infow(msg string, fields ...LogField) {
   186  	writeInfo(msg, fields...)
   187  }
   188  
   189  // Must checks if err is nil, otherwise logs the error and exits.
   190  func Must(err error) {
   191  	if err == nil {
   192  		return
   193  	}
   194  
   195  	msg := err.Error()
   196  	log.Print(msg)
   197  	getWriter().Severe(msg)
   198  	os.Exit(1)
   199  }
   200  
   201  // MustSetup sets up logging with given config c. It exits on error.
   202  func MustSetup(c LogConf) {
   203  	Must(SetUp(c))
   204  }
   205  
   206  // Reset clears the writer and resets the log level.
   207  func Reset() Writer {
   208  	return writer.Swap(nil)
   209  }
   210  
   211  // SetLevel sets the logging level. It can be used to suppress some logs.
   212  func SetLevel(level uint32) {
   213  	atomic.StoreUint32(&logLevel, level)
   214  }
   215  
   216  // SetWriter sets the logging writer. It can be used to customize the logging.
   217  func SetWriter(w Writer) {
   218  	if atomic.LoadUint32(&disableLog) == 0 {
   219  		writer.Store(w)
   220  	}
   221  }
   222  
   223  // SetUp sets up the logx. If already set up, just return nil.
   224  // we allow SetUp to be called multiple times, because for example
   225  // we need to allow different service frameworks to initialize logx respectively.
   226  func SetUp(c LogConf) (err error) {
   227  	// Just ignore the subsequent SetUp calls.
   228  	// Because multiple services in one process might call SetUp respectively.
   229  	// Need to wait for the first caller to complete the execution.
   230  	setupOnce.Do(func() {
   231  		setupLogLevel(c)
   232  
   233  		if len(c.TimeFormat) > 0 {
   234  			timeFormat = c.TimeFormat
   235  		}
   236  
   237  		switch c.Encoding {
   238  		case plainEncoding:
   239  			atomic.StoreUint32(&encoding, plainEncodingType)
   240  		default:
   241  			atomic.StoreUint32(&encoding, jsonEncodingType)
   242  		}
   243  
   244  		switch c.Mode {
   245  		case fileMode:
   246  			err = setupWithFiles(c)
   247  		case volumeMode:
   248  			err = setupWithVolume(c)
   249  		default:
   250  			setupWithConsole()
   251  		}
   252  	})
   253  
   254  	return
   255  }
   256  
   257  // Severe writes v into severe log.
   258  func Severe(v ...interface{}) {
   259  	writeSevere(fmt.Sprint(v...))
   260  }
   261  
   262  // Severef writes v with format into severe log.
   263  func Severef(format string, v ...interface{}) {
   264  	writeSevere(fmt.Sprintf(format, v...))
   265  }
   266  
   267  // Slow writes v into slow log.
   268  func Slow(v ...interface{}) {
   269  	writeSlow(fmt.Sprint(v...))
   270  }
   271  
   272  // Slowf writes v with format into slow log.
   273  func Slowf(format string, v ...interface{}) {
   274  	writeSlow(fmt.Sprintf(format, v...))
   275  }
   276  
   277  // Slowv writes v into slow log with json content.
   278  func Slowv(v interface{}) {
   279  	writeSlow(v)
   280  }
   281  
   282  // Sloww writes msg along with fields into slow log.
   283  func Sloww(msg string, fields ...LogField) {
   284  	writeSlow(msg, fields...)
   285  }
   286  
   287  // Stat writes v into stat log.
   288  func Stat(v ...interface{}) {
   289  	writeStat(fmt.Sprint(v...))
   290  }
   291  
   292  // Statf writes v with format into stat log.
   293  func Statf(format string, v ...interface{}) {
   294  	writeStat(fmt.Sprintf(format, v...))
   295  }
   296  
   297  // WithCooldownMillis customizes logging on writing call stack interval.
   298  func WithCooldownMillis(millis int) LogOption {
   299  	return func(opts *logOptions) {
   300  		opts.logStackCooldownMills = millis
   301  	}
   302  }
   303  
   304  // WithKeepDays customizes logging to keep logs with days.
   305  func WithKeepDays(days int) LogOption {
   306  	return func(opts *logOptions) {
   307  		opts.keepDays = days
   308  	}
   309  }
   310  
   311  // WithGzip customizes logging to automatically gzip the log files.
   312  func WithGzip() LogOption {
   313  	return func(opts *logOptions) {
   314  		opts.gzipEnabled = true
   315  	}
   316  }
   317  
   318  // WithMaxBackups customizes how many log files backups will be kept.
   319  func WithMaxBackups(count int) LogOption {
   320  	return func(opts *logOptions) {
   321  		opts.maxBackups = count
   322  	}
   323  }
   324  
   325  // WithMaxSize customizes how much space the writing log file can take up.
   326  func WithMaxSize(size int) LogOption {
   327  	return func(opts *logOptions) {
   328  		opts.maxSize = size
   329  	}
   330  }
   331  
   332  // WithRotation customizes which log rotation rule to use.
   333  func WithRotation(r string) LogOption {
   334  	return func(opts *logOptions) {
   335  		opts.rotationRule = r
   336  	}
   337  }
   338  
   339  func addCaller(fields ...LogField) []LogField {
   340  	return append(fields, Field(callerKey, getCaller(callerDepth)))
   341  }
   342  
   343  func createOutput(path string) (io.WriteCloser, error) {
   344  	if len(path) == 0 {
   345  		return nil, ErrLogPathNotSet
   346  	}
   347  
   348  	switch options.rotationRule {
   349  	case sizeRotationRule:
   350  		return NewLogger(path, NewSizeLimitRotateRule(path, backupFileDelimiter, options.keepDays,
   351  			options.maxSize, options.maxBackups, options.gzipEnabled), options.gzipEnabled)
   352  	default:
   353  		return NewLogger(path, DefaultRotateRule(path, backupFileDelimiter, options.keepDays,
   354  			options.gzipEnabled), options.gzipEnabled)
   355  	}
   356  }
   357  
   358  func getWriter() Writer {
   359  	w := writer.Load()
   360  	if w == nil {
   361  		w = writer.StoreIfNil(newConsoleWriter())
   362  	}
   363  
   364  	return w
   365  }
   366  
   367  func handleOptions(opts []LogOption) {
   368  	for _, opt := range opts {
   369  		opt(&options)
   370  	}
   371  }
   372  
   373  func setupLogLevel(c LogConf) {
   374  	switch c.Level {
   375  	case levelDebug:
   376  		SetLevel(DebugLevel)
   377  	case levelInfo:
   378  		SetLevel(InfoLevel)
   379  	case levelError:
   380  		SetLevel(ErrorLevel)
   381  	case levelSevere:
   382  		SetLevel(SevereLevel)
   383  	}
   384  }
   385  
   386  func setupWithConsole() {
   387  	SetWriter(newConsoleWriter())
   388  }
   389  
   390  func setupWithFiles(c LogConf) error {
   391  	w, err := newFileWriter(c)
   392  	if err != nil {
   393  		return err
   394  	}
   395  
   396  	SetWriter(w)
   397  	return nil
   398  }
   399  
   400  func setupWithVolume(c LogConf) error {
   401  	if len(c.ServiceName) == 0 {
   402  		return ErrLogServiceNameNotSet
   403  	}
   404  
   405  	c.Path = path.Join(c.Path, c.ServiceName, sysx.Hostname())
   406  	return setupWithFiles(c)
   407  }
   408  
   409  func shallLog(level uint32) bool {
   410  	return atomic.LoadUint32(&logLevel) <= level
   411  }
   412  
   413  func shallLogStat() bool {
   414  	return atomic.LoadUint32(&disableStat) == 0
   415  }
   416  
   417  func writeDebug(val interface{}, fields ...LogField) {
   418  	if shallLog(DebugLevel) {
   419  		getWriter().Debug(val, addCaller(fields...)...)
   420  	}
   421  }
   422  
   423  func writeError(val interface{}, fields ...LogField) {
   424  	if shallLog(ErrorLevel) {
   425  		getWriter().Error(val, addCaller(fields...)...)
   426  	}
   427  }
   428  
   429  func writeInfo(val interface{}, fields ...LogField) {
   430  	if shallLog(InfoLevel) {
   431  		getWriter().Info(val, addCaller(fields...)...)
   432  	}
   433  }
   434  
   435  func writeSevere(msg string) {
   436  	if shallLog(SevereLevel) {
   437  		getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
   438  	}
   439  }
   440  
   441  func writeSlow(val interface{}, fields ...LogField) {
   442  	if shallLog(ErrorLevel) {
   443  		getWriter().Slow(val, addCaller(fields...)...)
   444  	}
   445  }
   446  
   447  func writeStack(msg string) {
   448  	if shallLog(ErrorLevel) {
   449  		getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
   450  	}
   451  }
   452  
   453  func writeStat(msg string) {
   454  	if shallLogStat() && shallLog(InfoLevel) {
   455  		getWriter().Stat(msg, addCaller()...)
   456  	}
   457  }