github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/logger/logger.go (about)

     1  package logger
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/isyscore/isc-gobase/config"
     7  	"github.com/isyscore/isc-gobase/constants"
     8  	"github.com/isyscore/isc-gobase/isc"
     9  	"github.com/isyscore/isc-gobase/listener"
    10  	"github.com/isyscore/isc-gobase/store"
    11  	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
    12  	"github.com/rifflock/lfshook"
    13  	"github.com/sirupsen/logrus"
    14  	"os"
    15  	"runtime"
    16  	"strings"
    17  	"sync"
    18  	"time"
    19  )
    20  
    21  const (
    22  	white  = 29
    23  	black  = 30
    24  	red    = 31
    25  	green  = 32
    26  	yellow = 33
    27  	purple = 35
    28  	blue   = 36
    29  	gray   = 37
    30  )
    31  
    32  var gColor = false
    33  var loggerMap map[string]*logrus.Logger
    34  var rotateMap map[string]*rotatelogs.RotateLogs
    35  var rootLogger *logrus.Logger
    36  
    37  func init() {
    38  	_loggerMap := map[string]*logrus.Logger{}
    39  	loggerMap = _loggerMap
    40  	_rotateMap := map[string]*rotatelogs.RotateLogs{}
    41  	rotateMap = _rotateMap
    42  	rootLogger = Group("root")
    43  
    44  	_gColor := config.GetValueBoolDefault("base.logger.color.enable", false)
    45  	gColor = _gColor
    46  }
    47  
    48  func Group(groupNames... string) *logrus.Logger {
    49  	var resultLogger *logrus.Logger
    50  	groupNamesOfUnContain := []string{}
    51  	for _, groupName := range groupNames {
    52  		if logger, exit := loggerMap[groupName]; exit {
    53  			resultLogger = logger
    54  		} else {
    55  			groupNamesOfUnContain = append(groupNamesOfUnContain, groupName)
    56  		}
    57  	}
    58  
    59  	if resultLogger != nil {
    60  		return resultLogger
    61  	}  else {
    62  		resultLogger = logrus.New()
    63  		resultLogger.SetReportCaller(true)
    64  		formatters := &StandardFormatter{}
    65  		resultLogger.Formatter = formatters
    66  
    67  		loggerDir := config.GetValueStringDefault("base.logger.home", "./logs/")
    68  		resultLogger.AddHook(lfshook.NewHook(lfshook.WriterMap{
    69  			logrus.DebugLevel: rotateLogWithCache(loggerDir, "debug"),
    70  			logrus.InfoLevel:  rotateLogWithCache(loggerDir, "info"),
    71  			logrus.WarnLevel:  rotateLogWithCache(loggerDir, "warn"),
    72  			logrus.ErrorLevel: rotateLogWithCache(loggerDir, "error"),
    73  			logrus.PanicLevel: rotateLogWithCache(loggerDir, "panic"),
    74  			logrus.FatalLevel: rotateLogWithCache(loggerDir, "fatal"),
    75  		}, formatters))
    76  	}
    77  
    78  	// 值最大的级别,对应的level最小,比如Debug对应的数值比Info要大
    79  	maxValueLevel := logrus.PanicLevel
    80  	for _, groupName := range groupNamesOfUnContain {
    81  		var finalGroupLevel string
    82  		rootLevel := config.GetValueStringDefault("base.logger.level", "info")
    83  		groupLevel := config.GetValueString("base.logger.group." + groupName + ".level")
    84  		if groupLevel != "" {
    85  			finalGroupLevel = groupLevel
    86  		} else {
    87  			finalGroupLevel = rootLevel
    88  		}
    89  
    90  		lgLevel, err := logrus.ParseLevel(finalGroupLevel)
    91  		if err != nil {
    92  			lgLevel = logrus.InfoLevel
    93  		}
    94  
    95  		if lgLevel > maxValueLevel {
    96  			maxValueLevel = lgLevel
    97  		}
    98  	}
    99  
   100  	resultLogger.SetLevel(maxValueLevel)
   101  
   102  	for _, groupName := range groupNamesOfUnContain {
   103  		loggerMap[groupName] = resultLogger
   104  	}
   105  	return resultLogger
   106  }
   107  
   108  func doGroup(groupName string) *logrus.Logger {
   109  	if groupName == "" {
   110  		return rootLogger
   111  	}
   112  	if logger, exit := loggerMap[groupName]; exit {
   113  		return logger
   114  	}
   115  
   116  	if loggerMap == nil {
   117  		loggerMap = map[string]*logrus.Logger{}
   118  	}
   119  	logger := logrus.New()
   120  	logger.SetReportCaller(true)
   121  	formatters := &StandardFormatter{}
   122  	logger.Formatter = formatters
   123  
   124  	loggerDir := config.GetValueStringDefault("base.logger.home", "./logs/")
   125  	logger.AddHook(lfshook.NewHook(lfshook.WriterMap{
   126  		logrus.DebugLevel: rotateLogWithCache(loggerDir, "debug"),
   127  		logrus.InfoLevel:  rotateLogWithCache(loggerDir, "info"),
   128  		logrus.WarnLevel:  rotateLogWithCache(loggerDir, "warn"),
   129  		logrus.ErrorLevel: rotateLogWithCache(loggerDir, "error"),
   130  		logrus.PanicLevel: rotateLogWithCache(loggerDir, "panic"),
   131  		logrus.FatalLevel: rotateLogWithCache(loggerDir, "fatal"),
   132  	}, formatters))
   133  
   134  	var finalGroupLevel string
   135  	rootLevel := config.GetValueStringDefault("base.logger.level", "info")
   136  	groupLevel := config.GetValueString("base.logger.group." + groupName + ".level")
   137  	if groupLevel != "" {
   138  		finalGroupLevel = groupLevel
   139  	} else {
   140  		finalGroupLevel = rootLevel
   141  	}
   142  
   143  	lgLevel, err := logrus.ParseLevel(finalGroupLevel)
   144  	if err != nil {
   145  		lgLevel = logrus.InfoLevel
   146  	}
   147  	logger.SetLevel(lgLevel)
   148  
   149  	loggerMap[groupName] = logger
   150  	return logger
   151  }
   152  
   153  func InitLog() {
   154  	rootLogger = Group("root")
   155  	loggerDir := config.GetValueStringDefault("base.logger.home", "./logs/")
   156  	rootLogger.AddHook(lfshook.NewHook(lfshook.WriterMap{
   157  		logrus.DebugLevel: rotateLog(loggerDir, "debug"),
   158  		logrus.InfoLevel:  rotateLog(loggerDir, "info"),
   159  		logrus.WarnLevel:  rotateLog(loggerDir, "warn"),
   160  		logrus.ErrorLevel: rotateLog(loggerDir, "error"),
   161  		logrus.PanicLevel: rotateLog(loggerDir, "panic"),
   162  		logrus.FatalLevel: rotateLog(loggerDir, "fatal"),
   163  	}, &StandardFormatter{}))
   164  	lgLevel, err := logrus.ParseLevel(config.GetValueStringDefault("base.logger.level", "info"))
   165  	if err != nil {
   166  		lgLevel = logrus.InfoLevel
   167  	}
   168  	rootLogger.SetLevel(lgLevel)
   169  
   170  	_gColor := config.GetValueBoolDefault("base.logger.color.enable", false)
   171  	gColor = _gColor
   172  
   173  	listener.AddListener(listener.EventOfConfigChange, ConfigChangeListener)
   174  }
   175  
   176  func ConfigChangeListener(event listener.BaseEvent) {
   177  	ev := event.(listener.ConfigChangeEvent)
   178  	if ev.Key == "base.logger.level" {
   179  		SetGlobalLevel(ev.Value)
   180  	} else if strings.HasPrefix(ev.Key, "base.logger.group") {
   181  		words := strings.Split(ev.Key, ".")
   182  		if len(words) != 5 {
   183  			return
   184  		}
   185  		_group := words[3]
   186  		_level := ev.Value
   187  		le, err := logrus.ParseLevel(_level)
   188  		if err != nil {
   189  			return
   190  		}
   191  		Group(_group).SetLevel(le)
   192  	}
   193  }
   194  
   195  func SetGlobalLevel(strLevel string) {
   196  	level, err := logrus.ParseLevel(strLevel)
   197  	if err == nil {
   198  		rootLogger.SetLevel(level)
   199  	}
   200  }
   201  
   202  func InfoDirect(v ...any) {
   203  	rootLogger.Info(v...)
   204  }
   205  
   206  func WarnDirect(v ...any) {
   207  	rootLogger.Warn(v...)
   208  }
   209  
   210  func ErrorDirect(v ...any) {
   211  	rootLogger.Error(v...)
   212  }
   213  
   214  func FatalDirect(v ...any) {
   215  	rootLogger.Fatal(v...)
   216  }
   217  
   218  func PanicDirect(v ...any) {
   219  	rootLogger.Panic(v...)
   220  }
   221  
   222  func DebugDirect(v ...any) {
   223  	rootLogger.Debug(v...)
   224  }
   225  
   226  func TraceDirect(v ...any) {
   227  	rootLogger.Trace(v...)
   228  }
   229  
   230  func Info(format string, v ...any) {
   231  	rootLogger.Infof(format, v...)
   232  }
   233  
   234  func Warn(format string, v ...any) {
   235  	rootLogger.Warnf(format, v...)
   236  }
   237  
   238  func Error(format string, v ...any) {
   239  	rootLogger.Errorf(format, v...)
   240  }
   241  
   242  func Debug(format string, v ...any) {
   243  	rootLogger.Debugf(format, v...)
   244  }
   245  
   246  func Trace(format string, v ...any) {
   247  	rootLogger.Tracef(format, v...)
   248  }
   249  
   250  func Panic(format string, v ...any) {
   251  	rootLogger.Panicf(format, v...)
   252  }
   253  
   254  func Fatal(format string, v ...any) {
   255  	rootLogger.Fatalf(format, v...)
   256  }
   257  
   258  func Record(level, format string, v ...any) {
   259  	switch strings.ToLower(level) {
   260  	case "debug":
   261  		Debug(format, v)
   262  	case "info":
   263  		Info(format, v)
   264  	case "warn":
   265  		Warn(format, v)
   266  	case "error":
   267  		Error(format, v)
   268  	case "panic":
   269  		Panic(format, v)
   270  	case "fatal":
   271  		Fatal(format, v)
   272  	default:
   273  		Debug(format, v)
   274  	}
   275  }
   276  
   277  func rotateLog(path, level string) *rotatelogs.RotateLogs {
   278  	if rotateMap == nil {
   279  		rotateMap = map[string]*rotatelogs.RotateLogs{}
   280  	}
   281  
   282  	if path == "" {
   283  		path = "./logs/"
   284  	}
   285  
   286  	maxSizeStr := config.GetValueStringDefault("base.logger.rotate.max-size", "300MB")
   287  	maxHistoryStr := config.GetValueStringDefault("base.logger.rotate.max-history", "60d")
   288  	rotateTimeStr := config.GetValueStringDefault("base.logger.rotate.time", "1d")
   289  
   290  	rotateOptions := []rotatelogs.Option{rotatelogs.WithLinkName(path + "app-" + level + ".log")}
   291  	if maxSizeStr != "" {
   292  		rotateOptions = append(rotateOptions, rotatelogs.WithRotationSize(isc.ParseByteSize(maxSizeStr)))
   293  	}
   294  
   295  	_maxHistory, err := time.ParseDuration(maxHistoryStr)
   296  	if err == nil {
   297  		rotateOptions = append(rotateOptions, rotatelogs.WithMaxAge(_maxHistory))
   298  	}
   299  
   300  	_rotateTime, err := time.ParseDuration(rotateTimeStr)
   301  	if err == nil {
   302  		rotateOptions = append(rotateOptions, rotatelogs.WithRotationTime(_rotateTime))
   303  	}
   304  
   305  	data, _ := rotatelogs.New(path+"app-"+level+".%Y%m%d.log", rotateOptions...)
   306  	rotateMap[path+"-"+level] = data
   307  	return data
   308  }
   309  
   310  func rotateLogWithCache(path, level string) *rotatelogs.RotateLogs {
   311  	if pRotateValue, exist := rotateMap[path+"-"+level]; exist {
   312  		return pRotateValue
   313  	}
   314  
   315  	return rotateLog(path, level)
   316  }
   317  
   318  type StandardFormatter struct{}
   319  
   320  func (m *StandardFormatter) Format(entry *logrus.Entry) ([]byte, error) {
   321  	var b *bytes.Buffer
   322  	if entry.Buffer != nil {
   323  		b = entry.Buffer
   324  	} else {
   325  		b = &bytes.Buffer{}
   326  	}
   327  
   328  	var fields []string
   329  	for k, v := range entry.Data {
   330  		fields = append(fields, fmt.Sprintf("%v=%v", k, v))
   331  	}
   332  
   333  	level := entry.Level
   334  	timestamp := entry.Time.Format("2006-01-02 15:04:05")
   335  	var funPath string
   336  	if entry.HasCaller() {
   337  		frame := getCallerFrame()
   338  		funPath = fmt.Sprintf("%s:%d#%s", shortLogPath(frame.File), frame.Line, functionName(frame))
   339  	} else {
   340  		funPath = fmt.Sprintf("%s", entry.Message)
   341  	}
   342  
   343  	var fieldsStr string
   344  	if len(fields) != 0 {
   345  		fieldsStr = fmt.Sprintf("[\x1b[%dm%s\x1b[0m]", blue, strings.Join(fields, " "))
   346  	}
   347  	var newLog string
   348  	var levelColor = gray
   349  	if gColor {
   350  		switch level {
   351  		case logrus.DebugLevel:
   352  			levelColor = blue
   353  		case logrus.InfoLevel:
   354  			levelColor = green
   355  		case logrus.WarnLevel:
   356  			levelColor = yellow
   357  		case logrus.ErrorLevel:
   358  			levelColor = red
   359  		case logrus.FatalLevel:
   360  			levelColor = red
   361  		case logrus.PanicLevel:
   362  			levelColor = red
   363  		}
   364  		newLog = fmt.Sprintf("[%s] \x1b[%dm%s [%s]\x1b[0m [%s] [%v] \x1b[%dm%s\x1b[0m \x1b[%dm%s\x1b[0m %s %s\n",
   365  			timestamp,
   366  			black,
   367  			os.Getenv("HOSTNAME"),
   368  			config.GetValueStringDefault("base.application.name", "isc-gobase"),
   369  			store.Get(constants.TRACE_HEAD_ID), store.Get(constants.TRACE_HEAD_USER_ID),
   370  			levelColor,
   371  			strings.ToUpper(entry.Level.String()),
   372  			black,
   373  			funPath,
   374  			entry.Message,
   375  			fieldsStr)
   376  	} else {
   377  		newLog = fmt.Sprintf("[%s] %s [%s] [%s] [%v] %s %s %s %s\n",
   378  			timestamp,
   379  			os.Getenv("HOSTNAME"),
   380  			config.GetValueStringDefault("base.application.name", "isc-gobase"),
   381  			store.Get(constants.TRACE_HEAD_ID), store.Get(constants.TRACE_HEAD_USER_ID),
   382  			strings.ToUpper(entry.Level.String()),
   383  			funPath,
   384  			entry.Message,
   385  			fieldsStr)
   386  	}
   387  
   388  	b.WriteString(newLog)
   389  	return b.Bytes(), nil
   390  }
   391  
   392  const (
   393  	maximumCallerDepth    int = 25
   394  	knownBaseLoggerFrames int = 5
   395  )
   396  
   397  var callerInitOnce sync.Once
   398  var minimumCallerDepth = 0
   399  var baseLoggerPackage string
   400  
   401  func getPackageName(f string) string {
   402  	for {
   403  		lastPeriod := strings.LastIndex(f, ".")
   404  		lastSlash := strings.LastIndex(f, "/")
   405  		if lastPeriod > lastSlash {
   406  			f = f[:lastPeriod]
   407  		} else {
   408  			break
   409  		}
   410  	}
   411  	return f
   412  }
   413  
   414  func getCallerFrame() *runtime.Frame {
   415  	pcs := make([]uintptr, maximumCallerDepth)
   416  	callerInitOnce.Do(func() {
   417  		pcs := make([]uintptr, maximumCallerDepth)
   418  		_ = runtime.Callers(0, pcs)
   419  
   420  		for i := 0; i < maximumCallerDepth; i++ {
   421  			funcName := runtime.FuncForPC(pcs[i]).Name()
   422  			if strings.Contains(funcName, "logger.getCallerFrame") {
   423  				baseLoggerPackage = getPackageName(funcName)
   424  				break
   425  			}
   426  		}
   427  
   428  		minimumCallerDepth = knownBaseLoggerFrames
   429  	})
   430  
   431  	pcs = make([]uintptr, maximumCallerDepth)
   432  	depth := runtime.Callers(minimumCallerDepth, pcs)
   433  	frames := runtime.CallersFrames(pcs[:depth])
   434  
   435  	for f, again := frames.Next(); again; f, again = frames.Next() {
   436  		pkg := getPackageName(f.Function)
   437  		if pkg != baseLoggerPackage && pkg != "github.com/sirupsen/logrus" {
   438  			return &f
   439  		}
   440  	}
   441  	return nil
   442  }
   443  
   444  func functionName(frame *runtime.Frame) string {
   445  	pathMeta := strings.Split(frame.Function, ".")
   446  	if len(pathMeta) > 1 {
   447  		return pathMeta[len(pathMeta)-1]
   448  	}
   449  	return frame.Function
   450  }
   451  
   452  func shortLogPath(logPath string) string {
   453  	loggerPath := config.GetValueStringDefault("base.logger.path.type", "short")
   454  	if loggerPath == "short" {
   455  		pathMeta := strings.Split(logPath, string(os.PathSeparator))
   456  		if len(pathMeta) > 3 {
   457  			return pathMeta[len(pathMeta)-3] + string(os.PathSeparator) + pathMeta[len(pathMeta)-2] + string(os.PathSeparator) + pathMeta[len(pathMeta)-1]
   458  		}
   459  		return logPath
   460  	} else if loggerPath == "full" {
   461  		pathMeta := strings.Split(logPath, "@2/project")
   462  		if len(pathMeta) > 1 {
   463  			pathMeta[0] = "../.."
   464  			return strings.Join(pathMeta, "")
   465  		}
   466  		return logPath
   467  	} else {
   468  		return logPath
   469  	}
   470  }