github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zlog/zlogger.go (about)

     1  package zlog
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"reflect"
     9  	"runtime"
    10  	"sync"
    11  	"text/tabwriter"
    12  	"time"
    13  
    14  	"github.com/sohaha/zlsgo/zfile"
    15  	"github.com/sohaha/zlsgo/zreflect"
    16  	"github.com/sohaha/zlsgo/zstring"
    17  	"github.com/sohaha/zlsgo/ztime"
    18  	"github.com/sohaha/zlsgo/zutil"
    19  )
    20  
    21  // log header information tag bit, using bitmap mode
    22  const (
    23  	BitDate         int                                 = 1 << iota // Date marker  2019/01/23
    24  	BitTime                                                         // Time Label Bit  01:23:12
    25  	BitMicroSeconds                                                 // Microsecond label bit 01:23:12.111222
    26  	BitLongFile                                                     // Full file name, example: /home/go/src/github.com/sohaha/zlsgo/doc.go
    27  	BitShortFile                                                    // Final File name   doc.go
    28  	BitLevel                                                        // Current log level
    29  	BitStdFlag      = BitDate | BitTime                             // Standard header log format
    30  	BitDefault      = BitLevel | BitShortFile | BitTime             // Default log header format
    31  	// LogMaxBuf LogMaxBuf
    32  	LogMaxBuf = 1024 * 1024
    33  )
    34  
    35  // log level
    36  const (
    37  	LogFatal = iota
    38  	LogPanic
    39  	LogTrack
    40  	LogError
    41  	LogWarn
    42  	LogTips
    43  	LogSuccess
    44  	LogInfo
    45  	LogDebug
    46  	LogDump
    47  	LogNot = -1
    48  )
    49  
    50  var Levels = []string{
    51  	"[FATAL]",
    52  	"[PANIC]",
    53  	"[TRACK]",
    54  	"[ERROR]",
    55  	"[WARN] ",
    56  	"[TIPS] ",
    57  	"[SUCCE]",
    58  	"[INFO] ",
    59  	"[DEBUG]",
    60  	"[DUMP] ",
    61  }
    62  
    63  var LevelColous = []Color{
    64  	ColorRed,
    65  	ColorLightRed,
    66  	ColorLightYellow,
    67  	ColorRed,
    68  	ColorYellow,
    69  	ColorWhite,
    70  	ColorGreen,
    71  	ColorBlue,
    72  	ColorLightCyan,
    73  	ColorCyan,
    74  }
    75  
    76  type (
    77  	// Logger logger struct
    78  	Logger struct {
    79  		out         io.Writer
    80  		file        *zfile.MemoryFile
    81  		prefix      string
    82  		fileDir     string
    83  		fileName    string
    84  		writeBefore []func(level int, log string) bool
    85  		// buf           bytes.Buffer
    86  		calldDepth    int
    87  		level         int
    88  		flag          int
    89  		mu            sync.RWMutex
    90  		color         bool
    91  		fileAndStdout bool
    92  	}
    93  	formatter struct {
    94  		v     reflect.Value
    95  		force bool
    96  		quote bool
    97  	}
    98  	visit struct {
    99  		typ reflect.Type
   100  		v   uintptr
   101  	}
   102  	zprinter struct {
   103  		io.Writer
   104  		tw      *tabwriter.Writer
   105  		visited map[visit]int
   106  		depth   int
   107  	}
   108  )
   109  
   110  // New Initialize a log object
   111  func New(moduleName ...string) *Logger {
   112  	name := ""
   113  	if len(moduleName) > 0 {
   114  		name = moduleName[0]
   115  	}
   116  	return NewZLog(os.Stderr, name, BitDefault, LogDump, true, 3)
   117  }
   118  
   119  // NewZLog Create log
   120  func NewZLog(out io.Writer, prefix string, flag int, level int, color bool, calldDepth int) *Logger {
   121  	zlog := &Logger{out: out, prefix: prefix, flag: flag, file: nil, calldDepth: calldDepth, level: level, color: color}
   122  	runtime.SetFinalizer(zlog, CleanLog)
   123  	return zlog
   124  }
   125  
   126  // CleanLog CleanLog
   127  func CleanLog(log *Logger) {
   128  	log.CloseFile()
   129  }
   130  
   131  // DisableConsoleColor DisableConsoleColor
   132  func (log *Logger) DisableConsoleColor() {
   133  	log.color = false
   134  }
   135  
   136  // ForceConsoleColor ForceConsoleColor
   137  func (log *Logger) ForceConsoleColor() {
   138  	log.color = true
   139  }
   140  
   141  // ColorTextWrap ColorTextWrap
   142  func (log *Logger) ColorTextWrap(color Color, text string) string {
   143  	if log.color {
   144  		return ColorTextWrap(color, text)
   145  	}
   146  	return text
   147  }
   148  
   149  // ColorBackgroundWrap ColorBackgroundWrap
   150  func (log *Logger) ColorBackgroundWrap(color Color, backgroundColor Color, text string) string {
   151  	if log.color {
   152  		return ColorBackgroundWrap(color, backgroundColor, text)
   153  	}
   154  	return text
   155  }
   156  
   157  // OpTextWrap OpTextWrap
   158  func (log *Logger) OpTextWrap(color Op, text string) string {
   159  	if log.color {
   160  		return OpTextWrap(color, text)
   161  	}
   162  	return text
   163  }
   164  
   165  func (log *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int, level int) {
   166  	if log.flag&(BitDate|BitTime|BitMicroSeconds|BitLevel) != 0 {
   167  
   168  		if log.flag&BitDate != 0 {
   169  			buf.WriteString(ztime.FormatTime(t, "Y/m/d "))
   170  		}
   171  
   172  		if log.flag&(BitTime|BitMicroSeconds) != 0 {
   173  			buf.WriteString(ztime.FormatTime(t, "H:i:s"))
   174  			if log.flag&BitMicroSeconds != 0 {
   175  				buf.WriteByte('.')
   176  				itoa(buf, t.Nanosecond()/1e3, 6) // "12:12:59.123456
   177  			}
   178  			buf.WriteByte(' ')
   179  		}
   180  
   181  		if log.flag&BitLevel != 0 {
   182  			buf.WriteString(log.ColorTextWrap(LevelColous[level], Levels[level]+" "))
   183  		}
   184  
   185  		if log.flag&(BitShortFile|BitLongFile) != 0 {
   186  			if log.flag&BitShortFile != 0 {
   187  				short := file
   188  				for i := len(file) - 1; i > 0; i-- {
   189  					if file[i] == '/' {
   190  						short = file[i+1:]
   191  						break
   192  					}
   193  				}
   194  				file = short
   195  			}
   196  			buf.WriteString(file)
   197  			buf.WriteByte(':')
   198  			itoa(buf, line, -1)
   199  			buf.WriteString(": ")
   200  		}
   201  	}
   202  }
   203  
   204  // outPut Output log
   205  func (log *Logger) outPut(level int, s string, isWrap bool, calldDepth int, prefixText ...string) error {
   206  	if log.writeBefore != nil && len(s) > 0 {
   207  		p := s
   208  		if isWrap {
   209  			p = p[:len(p)-1]
   210  		}
   211  		for i := range log.writeBefore {
   212  			if log.writeBefore[i](level, p) {
   213  				return nil
   214  			}
   215  		}
   216  	}
   217  
   218  	if len(prefixText) > 0 {
   219  		s = prefixText[0] + s
   220  	}
   221  
   222  	buf := zutil.GetBuff(len(s) + 34)
   223  	defer zutil.PutBuff(buf)
   224  
   225  	now := ztime.Time()
   226  	if level != LogNot {
   227  		file, line := log.fileLocation(calldDepth)
   228  		log.formatHeader(buf, now, file, line, level)
   229  	}
   230  
   231  	if log.prefix != "" {
   232  		buf.WriteString(log.prefix)
   233  	}
   234  
   235  	buf.WriteString(s)
   236  	if isWrap && len(s) > 0 && s[len(s)-1] != '\n' {
   237  		buf.WriteByte('\n')
   238  	}
   239  	_, err := log.out.Write(buf.Bytes())
   240  	return err
   241  }
   242  
   243  // Printf formats according to a format specifier and writes to standard output
   244  func (log *Logger) Printf(format string, v ...interface{}) {
   245  	_ = log.outPut(LogNot, fmt.Sprintf(format, v...), false, log.calldDepth)
   246  }
   247  
   248  // Println Println
   249  func (log *Logger) Println(v ...interface{}) {
   250  	_ = log.outPut(LogNot, fmt.Sprintln(v...), true, log.calldDepth)
   251  }
   252  
   253  // Debugf Debugf
   254  func (log *Logger) Debugf(format string, v ...interface{}) {
   255  	if log.level < LogDebug {
   256  		return
   257  	}
   258  	_ = log.outPut(LogDebug, fmt.Sprintf(format, v...), false, log.calldDepth)
   259  }
   260  
   261  // Debug Debug
   262  func (log *Logger) Debug(v ...interface{}) {
   263  	if log.level < LogDebug {
   264  		return
   265  	}
   266  	_ = log.outPut(LogDebug, fmt.Sprintln(v...), true, log.calldDepth)
   267  }
   268  
   269  // Dump pretty print format
   270  func (log *Logger) Dump(v ...interface{}) {
   271  	if log.level < LogDump {
   272  		return
   273  	}
   274  	args := formatArgs(v...)
   275  	_, file, line, ok := callerName(1)
   276  	if ok {
   277  		names, err := argNames(file, line)
   278  		if err == nil {
   279  			args = prependArgName(names, args)
   280  		}
   281  	}
   282  
   283  	_ = log.outPut(LogDump, fmt.Sprintln(args...), true, log.calldDepth)
   284  }
   285  
   286  // Successf output Success
   287  func (log *Logger) Successf(format string, v ...interface{}) {
   288  	if log.level < LogSuccess {
   289  		return
   290  	}
   291  	_ = log.outPut(LogSuccess, fmt.Sprintf(format, v...), false, log.calldDepth)
   292  }
   293  
   294  // Success output Success
   295  func (log *Logger) Success(v ...interface{}) {
   296  	if log.level < LogSuccess {
   297  		return
   298  	}
   299  	_ = log.outPut(LogSuccess, fmt.Sprintln(v...), true, log.calldDepth)
   300  }
   301  
   302  // Infof output Info
   303  func (log *Logger) Infof(format string, v ...interface{}) {
   304  	if log.level < LogInfo {
   305  		return
   306  	}
   307  	_ = log.outPut(LogInfo, fmt.Sprintf(format, v...), false, log.calldDepth)
   308  }
   309  
   310  // Info output Info
   311  func (log *Logger) Info(v ...interface{}) {
   312  	if log.level < LogInfo {
   313  		return
   314  	}
   315  	_ = log.outPut(LogInfo, fmt.Sprintln(v...), true, log.calldDepth)
   316  }
   317  
   318  // Tipsf output Tips
   319  func (log *Logger) Tipsf(format string, v ...interface{}) {
   320  	if log.level < LogTips {
   321  		return
   322  	}
   323  	_ = log.outPut(LogTips, fmt.Sprintf(format, v...), false, log.calldDepth)
   324  }
   325  
   326  // Tips output Tips
   327  func (log *Logger) Tips(v ...interface{}) {
   328  	if log.level < LogTips {
   329  		return
   330  	}
   331  	_ = log.outPut(LogTips, fmt.Sprintln(v...), true, log.calldDepth)
   332  }
   333  
   334  // Warnf output Warn
   335  func (log *Logger) Warnf(format string, v ...interface{}) {
   336  	if log.level < LogWarn {
   337  		return
   338  	}
   339  	_ = log.outPut(LogWarn, fmt.Sprintf(format, v...), false, log.calldDepth)
   340  }
   341  
   342  // Warn output Warn
   343  func (log *Logger) Warn(v ...interface{}) {
   344  	if log.level < LogWarn {
   345  		return
   346  	}
   347  	_ = log.outPut(LogWarn, fmt.Sprintln(v...), true, log.calldDepth)
   348  }
   349  
   350  // Errorf output Error
   351  func (log *Logger) Errorf(format string, v ...interface{}) {
   352  	if log.level < LogError {
   353  		return
   354  	}
   355  	_ = log.outPut(LogError, fmt.Sprintf(format, v...), false, log.calldDepth)
   356  }
   357  
   358  // Error output Error
   359  func (log *Logger) Error(v ...interface{}) {
   360  	if log.level < LogError {
   361  		return
   362  	}
   363  	_ = log.outPut(LogError, fmt.Sprintln(v...), true, log.calldDepth)
   364  }
   365  
   366  // Fatalf output Fatal
   367  func (log *Logger) Fatalf(format string, v ...interface{}) {
   368  	if log.level < LogFatal {
   369  		return
   370  	}
   371  	_ = log.outPut(LogFatal, fmt.Sprintf(format, v...), false, log.calldDepth)
   372  	osExit(1)
   373  }
   374  
   375  // Fatal output Fatal
   376  func (log *Logger) Fatal(v ...interface{}) {
   377  	if log.level < LogFatal {
   378  		return
   379  	}
   380  	_ = log.outPut(LogFatal, fmt.Sprintln(v...), true, log.calldDepth)
   381  	osExit(1)
   382  }
   383  
   384  // Panicf output Panic
   385  func (log *Logger) Panicf(format string, v ...interface{}) {
   386  	if log.level < LogPanic {
   387  		return
   388  	}
   389  	s := fmt.Sprintf(format, v...)
   390  	_ = log.outPut(LogPanic, fmt.Sprintf(format, s), false, log.calldDepth)
   391  	panic(s)
   392  }
   393  
   394  // Panic output panic
   395  func (log *Logger) Panic(v ...interface{}) {
   396  	if log.level < LogPanic {
   397  		return
   398  	}
   399  	s := fmt.Sprintln(v...)
   400  	_ = log.outPut(LogPanic, s, true, log.calldDepth)
   401  	panic(s)
   402  }
   403  
   404  // Stack output Stack
   405  func (log *Logger) Stack(v interface{}) {
   406  	if log.level < LogTrack {
   407  		return
   408  	}
   409  	var s string
   410  	switch e := v.(type) {
   411  	case error:
   412  		s = fmt.Sprintf("%+v", e)
   413  	case string:
   414  		s = e
   415  	default:
   416  		s = fmt.Sprintf("%v", e)
   417  	}
   418  	_ = log.outPut(LogTrack, s, true, log.calldDepth)
   419  }
   420  
   421  // Track output Track
   422  func (log *Logger) Track(v string, i ...int) {
   423  	if log.level < LogTrack {
   424  		return
   425  	}
   426  	b, skip, max, index := zutil.GetBuff(), 4, 1, 1
   427  	il := len(i)
   428  	if il > 0 {
   429  		max = i[0]
   430  		if il == 2 {
   431  			skip = skip + i[1]
   432  		}
   433  	}
   434  	s := zutil.Callers(skip)
   435  	l := len(s)
   436  	if max >= l {
   437  		max = l
   438  	}
   439  	s = s[:max]
   440  	space := "  "
   441  	b.WriteString(v + "\n")
   442  	s.Format(func(fn *runtime.Func, file string, line int) bool {
   443  		if index > 9 {
   444  			space = " "
   445  		}
   446  		b.WriteString(fmt.Sprintf(
   447  			"   %d).%s%s\n    \t%s:%d\n",
   448  			index, space, fn.Name(), file, line,
   449  		))
   450  		index++
   451  		return true
   452  	})
   453  	text := b.String()
   454  	zutil.PutBuff(b)
   455  	_ = log.outPut(LogTrack, text, true, log.calldDepth)
   456  }
   457  
   458  func callerName(skip int) (name, file string, line int, ok bool) {
   459  	var pc uintptr
   460  	if pc, file, line, ok = runtime.Caller(skip + 1); !ok {
   461  		return
   462  	}
   463  	name = runtime.FuncForPC(pc).Name()
   464  	return
   465  }
   466  
   467  // GetFlags Get the current log bitmap tag
   468  func (log *Logger) GetFlags() int {
   469  	log.mu.Lock()
   470  	defer log.mu.Unlock()
   471  	return log.flag
   472  }
   473  
   474  // ResetFlags Reset the GetFlags bitMap tag bit in the log
   475  func (log *Logger) ResetFlags(flag int) {
   476  	log.mu.Lock()
   477  	defer log.mu.Unlock()
   478  	log.flag = flag
   479  }
   480  
   481  // AddFlag Set flag Tags
   482  func (log *Logger) AddFlag(flag int) {
   483  	log.mu.Lock()
   484  	defer log.mu.Unlock()
   485  	log.flag |= flag
   486  }
   487  
   488  // SetPrefix Setting log prefix
   489  func (log *Logger) SetPrefix(prefix string) {
   490  	log.mu.Lock()
   491  	defer log.mu.Unlock()
   492  	log.prefix = prefix
   493  }
   494  
   495  func (log *Logger) GetPrefix() string {
   496  	return log.prefix
   497  }
   498  
   499  // SetLogLevel Setting log display level
   500  func (log *Logger) SetLogLevel(level int) {
   501  	log.level = level
   502  }
   503  
   504  // GetLogLevel Get log display level
   505  func (log *Logger) GetLogLevel() int {
   506  	return log.level
   507  }
   508  
   509  func (log *Logger) Write(b []byte) (n int, err error) {
   510  	_ = log.outPut(LogWarn, zstring.Bytes2String(b), false, log.calldDepth)
   511  	return len(b), nil
   512  }
   513  
   514  func (log *Logger) SetIgnoreLog(logs ...string) {
   515  	log.WriteBefore(func(level int, log string) bool {
   516  		for _, v := range logs {
   517  			if zstring.Match(log, v) {
   518  				return true
   519  			}
   520  		}
   521  		return false
   522  	})
   523  }
   524  
   525  func (log *Logger) WriteBefore(fn ...func(level int, log string) bool) {
   526  	log.writeBefore = append(log.writeBefore, fn...)
   527  }
   528  
   529  func itoa(buf *bytes.Buffer, i int, wid int) {
   530  	u := uint(i)
   531  	if u == 0 && wid <= 1 {
   532  		buf.WriteByte('0')
   533  		return
   534  	}
   535  
   536  	var b [32]byte
   537  	bp := len(b)
   538  	for ; u > 0 || wid > 0; u /= 10 {
   539  		bp--
   540  		wid--
   541  		b[bp] = byte(u%10) + '0'
   542  	}
   543  
   544  	for bp < len(b) {
   545  		buf.WriteByte(b[bp])
   546  		bp++
   547  	}
   548  }
   549  
   550  func (log *Logger) ResetWriter(w io.Writer) {
   551  	log.out = w
   552  }
   553  
   554  func formatArgs(args ...interface{}) []interface{} {
   555  	formatted := make([]interface{}, 0, len(args))
   556  	for _, a := range args {
   557  		s := ColorTextWrap(ColorCyan, sprint(a))
   558  		formatted = append(formatted, s)
   559  	}
   560  
   561  	return formatted
   562  }
   563  
   564  func sprint(a ...interface{}) string {
   565  	return fmt.Sprint(wrap(a, true)...)
   566  }
   567  
   568  func wrap(a []interface{}, force bool) []interface{} {
   569  	w := make([]interface{}, len(a))
   570  	for i, x := range a {
   571  		w[i] = formatter{v: zreflect.ValueOf(x), force: force}
   572  	}
   573  	return w
   574  }
   575  
   576  func writeByte(w io.Writer, b byte) {
   577  	_, _ = w.Write([]byte{b})
   578  }
   579  
   580  func prependArgName(names []string, values []interface{}) []interface{} {
   581  	vLen := len(values)
   582  	nLen := len(names)
   583  	prepended := make([]interface{}, vLen)
   584  	for i, value := range values {
   585  		name := ""
   586  		if i < nLen {
   587  			name = names[i]
   588  		}
   589  		if name == "" {
   590  			prepended[i] = OpTextWrap(OpBold, value.(string))
   591  			continue
   592  		}
   593  		name = ColorTextWrap(ColorBlue, OpTextWrap(OpBold, name))
   594  		prepended[i] = fmt.Sprintf("%s=%s", name, value)
   595  	}
   596  	return prepended
   597  }
   598  
   599  func (log *Logger) fileLocation(calldDepth int) (file string, line int) {
   600  	if log.flag&(BitShortFile|BitLongFile) != 0 {
   601  		var ok bool
   602  		_, file, line, ok = runtime.Caller(calldDepth)
   603  		if !ok {
   604  			file = "unknown-file"
   605  			line = 0
   606  		}
   607  	}
   608  	return
   609  }