github.com/coyove/common@v0.0.0-20240403014525-f70e643f9de8/logg/logg.go (about)

     1  package logg
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/csv"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"os"
    10  	"runtime"
    11  	"strconv"
    12  	"strings"
    13  	"sync"
    14  	"syscall"
    15  	"time"
    16  )
    17  
    18  type _Level int
    19  
    20  const (
    21  	LvDebug0 _Level = iota - 2
    22  	LvDebug
    23  	LvLog
    24  	LvInfo
    25  	LvWarning
    26  	LvError
    27  	LvFatal
    28  	LvOff
    29  )
    30  
    31  type _Format byte
    32  
    33  const (
    34  	FmtLongTime _Format = 1 + iota
    35  	FmtLongTimeUTC
    36  	FmtShortTime
    37  	FmtShortTimeSec
    38  	FmtElapsedTime
    39  	FmtElapsedTimeSec
    40  	FmtLongFile
    41  	FmtShortFile
    42  	FmtLevel
    43  	FmtGoroutine
    44  	FmtVoid
    45  )
    46  
    47  type Logger struct {
    48  	Writer       io.Writer
    49  	logLevel     _Level
    50  	ignoreLevels []_Level
    51  	logPath      string
    52  	formats      []_Format
    53  	logFile      *os.File
    54  	logFileTmp   bytes.Buffer
    55  	logFileSize  int64
    56  	lastFlush    int64
    57  	start        int64
    58  	sync.Mutex
    59  }
    60  
    61  var (
    62  	LvTexts  = []string{" FATAL ", " ERROR ", " WARNING ", " INFO ", " LOG ", " DEBUG ", " DEBUG0 "}
    63  	lvlookup = map[string]_Level{"dbg0": LvDebug0, "dbg": LvDebug, "info": LvInfo, "log": LvLog, "warn": LvWarning, "err": LvError, "fatal": LvFatal, "off": LvOff}
    64  )
    65  
    66  func (l *Logger) SetLevel(lv string) _Level {
    67  	l.ignoreLevels = nil
    68  	for i, lv := range strings.Split(lv, "^") {
    69  		n, ok := lvlookup[lv]
    70  		if !ok {
    71  			panic("unexpected log level: " + lv)
    72  		}
    73  
    74  		if i == 0 {
    75  			l.logLevel = n
    76  		} else {
    77  			if l.ignoreLevels == nil {
    78  				l.ignoreLevels = []_Level{n}
    79  			} else {
    80  				l.ignoreLevels = append(l.ignoreLevels, n)
    81  			}
    82  		}
    83  	}
    84  
    85  	return l.logLevel
    86  }
    87  
    88  func NewLogger(config string) *Logger {
    89  	l := &Logger{}
    90  	l.formats = []_Format{FmtLongTime, FmtShortFile, FmtLevel}
    91  	l.start = time.Now().UnixNano()
    92  
    93  	parts := strings.Split(config, ",")
    94  	if len(parts) == 0 {
    95  		return l
    96  	}
    97  
    98  	x := parts[0]
    99  	if strings.Contains(x, ":") {
   100  		fn := x[strings.Index(x, ":")+1:]
   101  		x = x[:len(x)-len(fn)-1]
   102  		if parts := strings.Split(fn, "+"); len(parts) == 2 {
   103  			if rs, err := strconv.Atoi(parts[0]); err == nil {
   104  				l.LogFile(parts[1], int64(rs))
   105  			}
   106  		} else {
   107  			l.LogFile(fn, 1024*1024)
   108  		}
   109  	}
   110  	l.SetLevel(x)
   111  
   112  	r := csv.NewReader(strings.NewReader(config))
   113  	parts, _ = r.Read()
   114  	formats := make([]_Format, 0, len(parts))
   115  
   116  	for i := 1; i < len(parts); i++ {
   117  		switch x := parts[i]; x {
   118  		case "longtime", "lt":
   119  			formats = append(formats, FmtLongTime)
   120  		case "longtimeutc", "ltu":
   121  			formats = append(formats, FmtLongTimeUTC)
   122  		case "shorttime", "st":
   123  			formats = append(formats, FmtShortTime)
   124  		case "shorttimesec", "sts":
   125  			formats = append(formats, FmtShortTimeSec)
   126  		case "elapsedtime", "et":
   127  			formats = append(formats, FmtElapsedTime)
   128  		case "elapsedtimesec", "ets":
   129  			formats = append(formats, FmtElapsedTimeSec)
   130  		case "shortfile", "sf":
   131  			formats = append(formats, FmtShortFile)
   132  		case "longfile", "lf":
   133  			formats = append(formats, FmtLongFile)
   134  		case "level", "lv", "l":
   135  			formats = append(formats, FmtLevel)
   136  		case "goroutine", "go", "g":
   137  			formats = append(formats, FmtGoroutine)
   138  		case "void":
   139  			formats = append(formats, FmtVoid)
   140  		}
   141  	}
   142  
   143  	if len(formats) > 0 {
   144  		l.formats = formats
   145  	}
   146  
   147  	return l
   148  }
   149  
   150  func (l *Logger) GetLevel() _Level {
   151  	return l.logLevel
   152  }
   153  
   154  func (l *Logger) LogFile(fn string, rotateSize int64) {
   155  	if l.logFile != nil {
   156  		l.logFile.Sync()
   157  		l.logFile.Close()
   158  	}
   159  
   160  	l.logPath = fn
   161  	fn += "." + time.Now().UTC().Format("2006-01-02_15-04-05.000")
   162  
   163  	var err error
   164  	l.logFile, err = os.Create(fn)
   165  	if err != nil {
   166  		panic(err)
   167  	}
   168  	l.logFileSize = rotateSize
   169  	l.logFileTmp.Reset()
   170  }
   171  
   172  func trunc(fn string) string {
   173  	idx := strings.LastIndex(fn, "/")
   174  	if idx == -1 {
   175  		idx = strings.LastIndex(fn, "\\")
   176  	}
   177  	return fn[idx+1:]
   178  }
   179  
   180  // Widnows WSA error messages are way too long to print
   181  // ex: An established connection was aborted by the software in your host machine.write tcp 127.0.0.1:8100->127.0.0.1:52466: wsasend: An established connection was aborted by the software in your host machine.
   182  func tryShortenWSAError(err interface{}) (ret string) {
   183  	defer func() {
   184  		if recover() != nil {
   185  			ret = fmt.Sprintf("%v", err)
   186  		}
   187  	}()
   188  
   189  	if e, sysok := err.(*net.OpError).Err.(*os.SyscallError); sysok {
   190  		errno := e.Err.(syscall.Errno)
   191  		if msg, ok := WSAErrno[int(errno)]; ok {
   192  			ret = msg
   193  		} else {
   194  			// messages on linux are short enough
   195  			ret = fmt.Sprintf("C%d, %s", uintptr(errno), e.Error())
   196  		}
   197  
   198  		return
   199  	}
   200  
   201  	ret = err.(*net.OpError).Err.Error()
   202  	return
   203  }
   204  
   205  func (l *Logger) print(lvs string, format string, params ...interface{}) {
   206  	_, fn, line, _ := runtime.Caller(3)
   207  	now := time.Now()
   208  	m := csvbuffer{}
   209  	for _, f := range l.formats {
   210  		switch f {
   211  		case FmtLongTime:
   212  			m.Write(now.Format("2006-01-02 15:04:05.000 MST"))
   213  		case FmtLongTimeUTC:
   214  			m.Write(now.UTC().Format("2006-01-02 15:04:05.000"))
   215  		case FmtLongFile:
   216  			m.Write(fn + ":" + strconv.Itoa(line))
   217  		case FmtShortFile:
   218  			m.Write(trunc(fn) + ":" + strconv.Itoa(line))
   219  		case FmtShortTime:
   220  			now = now.UTC()
   221  			m.Write(fmt.Sprintf("%d%02d%02d%02d%02d%02d.%03d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), (now.UnixNano()%1e9)/1e6))
   222  		case FmtShortTimeSec:
   223  			now = now.UTC()
   224  			m.Write(fmt.Sprintf("%d%02d%02d%02d%02d%02d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()))
   225  		case FmtElapsedTime:
   226  			m.Write(strconv.FormatFloat(float64(time.Now().UnixNano()-l.start)/1e9, 'f', 6, 64))
   227  		case FmtElapsedTimeSec:
   228  			m.Write(strconv.FormatInt((time.Now().UnixNano()-l.start)/1e9, 10))
   229  		case FmtLevel:
   230  			m.Write(lvs)
   231  		case FmtGoroutine:
   232  			buf := [32]byte{}
   233  			runtime.Stack(buf[:], false)
   234  			startidx := bytes.Index(buf[:], []byte(" "))
   235  			endidx := bytes.Index(buf[:], []byte("["))
   236  			m.Write(string(buf[startidx+1 : endidx-1]))
   237  		}
   238  	}
   239  
   240  	for i := 0; i < len(params); i++ {
   241  		p := params[i]
   242  		x := ""
   243  		switch op := p.(type) {
   244  		case *net.OpError:
   245  			if op.Source == nil && op.Addr == nil {
   246  				x = fmt.Sprintf("%s, %s", op.Op, tryShortenWSAError(p))
   247  			} else {
   248  				x = fmt.Sprintf("%s %v, %s", op.Op, op.Addr, tryShortenWSAError(p))
   249  			}
   250  			params[i] = x
   251  		case *net.DNSError:
   252  			x = fmt.Sprintf("DNS lookup failed: %v", op)
   253  			params[i] = x
   254  		default:
   255  			if format == "" {
   256  				x = fmt.Sprintf("%v", op)
   257  			}
   258  		}
   259  
   260  		if format == "" {
   261  			m.Write(x)
   262  		}
   263  	}
   264  
   265  	if format != "" {
   266  		m.Write(fmt.Sprintf(format, params...))
   267  	}
   268  	m.NewLine()
   269  
   270  	if l.logFile != nil {
   271  		l.logFileTmp.Write(m.Bytes())
   272  		l.flush(lvs == LvTexts[0])
   273  	} else if l.Writer != nil {
   274  		l.Writer.Write(m.Bytes())
   275  	} else {
   276  		os.Stderr.Write(m.Bytes())
   277  	}
   278  }
   279  
   280  func (l *Logger) flush(force bool) {
   281  	l.Lock()
   282  	now := time.Now().UnixNano()
   283  	if l.logFileTmp.Len() > 4096 || now-l.lastFlush > 1e9 || force {
   284  		if l.logFile != nil {
   285  			l.logFile.Write(l.logFileTmp.Bytes())
   286  			l.lastFlush = now
   287  			l.logFileTmp.Reset()
   288  
   289  			if st, _ := l.logFile.Stat(); st.Size() > l.logFileSize {
   290  				l.LogFile(l.logPath, l.logFileSize)
   291  			}
   292  		}
   293  	}
   294  	l.Unlock()
   295  }
   296  
   297  func (l *Logger) If(b bool) *Logger {
   298  	if b {
   299  		return l
   300  	}
   301  	return nil
   302  }
   303  
   304  func (l *Logger) level(lv _Level, format string, params ...interface{}) *Logger {
   305  	if l == nil {
   306  		return nil
   307  	}
   308  	for _, n := range l.ignoreLevels {
   309  		if n == lv {
   310  			return l
   311  		}
   312  	}
   313  	if l.logLevel <= lv {
   314  		l.print(LvTexts[LvOff-lv-1], format, params...)
   315  	}
   316  	if lv == LvFatal {
   317  		os.Exit(1)
   318  	}
   319  	return l
   320  }
   321  
   322  func (l *Logger) Dbg0f(f string, a ...interface{}) *Logger  { return l.level(LvDebug0, f, a...) }
   323  func (l *Logger) Dbg0(a ...interface{}) *Logger             { return l.level(LvDebug0, "", a...) }
   324  func (l *Logger) Dbgf(f string, a ...interface{}) *Logger   { return l.level(LvDebug, f, a...) }
   325  func (l *Logger) Dbg(a ...interface{}) *Logger              { return l.level(LvDebug, "", a...) }
   326  func (l *Logger) Logf(f string, a ...interface{}) *Logger   { return l.level(LvLog, f, a...) }
   327  func (l *Logger) Log(a ...interface{}) *Logger              { return l.level(LvLog, "", a...) }
   328  func (l *Logger) Infof(f string, a ...interface{}) *Logger  { return l.level(LvInfo, f, a...) }
   329  func (l *Logger) Info(a ...interface{}) *Logger             { return l.level(LvInfo, "", a...) }
   330  func (l *Logger) Warnf(f string, a ...interface{}) *Logger  { return l.level(LvWarning, f, a...) }
   331  func (l *Logger) Warn(a ...interface{}) *Logger             { return l.level(LvWarning, "", a...) }
   332  func (l *Logger) Errorf(f string, a ...interface{}) *Logger { return l.level(LvError, f, a...) }
   333  func (l *Logger) Error(a ...interface{}) *Logger            { return l.level(LvError, "", a...) }
   334  func (l *Logger) Fatalf(f string, a ...interface{}) *Logger { return l.level(LvFatal, f, a...) }
   335  func (l *Logger) Fatal(a ...interface{}) *Logger            { return l.level(LvFatal, "", a...) }