github.com/tencent/goom@v1.0.1/internal/logger/logger.go (about)

     1  // Package logger 负责日志收敛,给日志添加前缀和独立文件输出,以便在本框架被集成之后的日志可读
     2  package logger
     3  
     4  import (
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  )
    15  
    16  // 日志级别定义
    17  const (
    18  	TraceLevel    = 6 // 可详细跟踪
    19  	DebugLevel    = 5 // 可调式
    20  	InfoLevel     = 4 // 日常使用关键信息
    21  	WarningLevel  = 3 // 警告级别信息
    22  	ErrorLevel    = 2 // 错误级别信息
    23  	CriticalLevel = 1 // 严重错误
    24  )
    25  
    26  const (
    27  	defaultPrefix = "[goom-mocker]" // defaultPrefix 默认日志前缀
    28  	openDebugEnv  = "GOOM_DEBUG"    // openDebugEnv 开启 debug 日志
    29  )
    30  
    31  var (
    32  	// LogLevel 日志级别
    33  	// level 总共分5个级别:debug < info< warning< error< critical
    34  	LogLevel = InfoLevel
    35  	// ConsoleLevel 控制台打印级别
    36  	ConsoleLevel = WarningLevel
    37  	// ShowError2Console 把错误同步打印到控制台
    38  	ShowError2Console = false
    39  	// Logger 独立日志文件
    40  	Logger io.Writer = os.Stdout
    41  	// EnableLogTrack 开启并发日志染色
    42  	EnableLogTrack = false
    43  	// trackGetter 日志染色器, 用于并发测试区分协程 ID
    44  	trackGetter func() string
    45  	// logFile 日志路径
    46  	logFile *os.File
    47  )
    48  
    49  var (
    50  	levelName = map[int]string{ // levelName 日志级别-级别名称映射
    51  		TraceLevel:    "trace",
    52  		DebugLevel:    "debug",
    53  		InfoLevel:     "info",
    54  		WarningLevel:  "warn",
    55  		ErrorLevel:    "error",
    56  		CriticalLevel: "critical",
    57  	}
    58  	levelColor = map[int]Color{ // levelColor 日志级别-颜色映射
    59  		TraceLevel:    None,
    60  		DebugLevel:    None,
    61  		InfoLevel:     None,
    62  		WarningLevel:  Yellow,
    63  		ErrorLevel:    Red,
    64  		CriticalLevel: Red,
    65  	}
    66  )
    67  
    68  // init 初始化
    69  func init() {
    70  	if d := os.Getenv(openDebugEnv); d != "" {
    71  		OpenDebug()
    72  	}
    73  
    74  	loggerPath, err := loggerPath()
    75  	if err != nil {
    76  		fmt.Println("loggerPath error:", err)
    77  		return
    78  	}
    79  
    80  	logFile, err = os.OpenFile(filepath.Join(loggerPath, "goom-mocker.log"),
    81  		os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
    82  	if err != nil {
    83  		fmt.Println("init log file error:", err)
    84  		return
    85  	}
    86  	Logger = logFile
    87  }
    88  
    89  // OpenDebug 开启 debug 模式
    90  func OpenDebug() {
    91  	ConsoleLevel = DebugLevel
    92  }
    93  
    94  // CloseDebug 关闭 debug 模式
    95  func CloseDebug() {
    96  	ConsoleLevel = WarningLevel
    97  }
    98  
    99  // IsDebugOpen 是否开启 debug 模式
   100  func IsDebugOpen() bool {
   101  	return ConsoleLevel >= DebugLevel
   102  }
   103  
   104  // OpenTrace 打开日志跟踪
   105  func OpenTrace() {
   106  	OpenDebug()
   107  	SetLog2Console(true)
   108  	LogLevel = TraceLevel
   109  }
   110  
   111  // CloseTrace 关闭日志跟踪
   112  func CloseTrace() {
   113  	CloseDebug()
   114  	LogLevel = InfoLevel
   115  	SetLog2Console(false)
   116  }
   117  
   118  // SetLog2Console 设置是否打印到控制台
   119  func SetLog2Console(b bool) {
   120  	if b {
   121  		Logger = os.Stdout
   122  	} else {
   123  		Logger = logFile
   124  	}
   125  }
   126  
   127  // SetLogTrack 设置日志染色
   128  func SetLogTrack(enable bool, getter func() string) {
   129  	if enable && getter != nil {
   130  		trackGetter = getter
   131  		EnableLogTrack = true
   132  	} else {
   133  		EnableLogTrack = false
   134  	}
   135  }
   136  
   137  // TraceEnable 是否开启 trace
   138  func TraceEnable() bool {
   139  	return LogLevel >= TraceLevel
   140  }
   141  
   142  // Trace 打印 trace 日志
   143  func Trace(v ...interface{}) {
   144  	if LogLevel >= TraceLevel {
   145  		_, _ = Logger.Write(layout(TraceLevel, v))
   146  	}
   147  }
   148  
   149  // Tracef 打印 trace 日志
   150  func Tracef(format string, a ...interface{}) {
   151  	if LogLevel >= TraceLevel {
   152  		_, _ = Logger.Write(layoutf(TraceLevel, format, nil, a...))
   153  	}
   154  }
   155  
   156  // DebugEnable 是否开启 debug
   157  func DebugEnable() bool {
   158  	return LogLevel >= DebugLevel
   159  }
   160  
   161  // Debug 打印 debug 日志
   162  func Debug(v ...interface{}) {
   163  	if LogLevel >= DebugLevel {
   164  		_, _ = Logger.Write(layout(DebugLevel, v))
   165  	}
   166  }
   167  
   168  // Debugf 打印 debug 日志
   169  func Debugf(format string, a ...interface{}) {
   170  	if LogLevel >= DebugLevel {
   171  		_, _ = Logger.Write(layoutf(DebugLevel, format, nil, a...))
   172  	}
   173  }
   174  
   175  // Info 打印 info 日志
   176  func Info(v ...interface{}) {
   177  	if LogLevel >= InfoLevel {
   178  		_, _ = Logger.Write(layout(InfoLevel, v))
   179  	}
   180  }
   181  
   182  // Infof 打印 info 日志
   183  func Infof(format string, a ...interface{}) {
   184  	if LogLevel >= InfoLevel {
   185  		_, _ = Logger.Write(layoutf(InfoLevel, format, nil, a...))
   186  	}
   187  }
   188  
   189  // Warning 打印 warning 日志
   190  func Warning(v ...interface{}) {
   191  	if LogLevel >= WarningLevel {
   192  		line := layout(WarningLevel, v)
   193  		_, _ = Logger.Write(line)
   194  		write2Console(line)
   195  	}
   196  }
   197  
   198  // Warningf 打印 warning 日志
   199  func Warningf(format string, a ...interface{}) {
   200  	if LogLevel >= WarningLevel {
   201  		line := layoutf(WarningLevel, format, nil, a...)
   202  		_, _ = Logger.Write(line)
   203  		write2Console(line)
   204  	}
   205  }
   206  
   207  // Important 打印重要的日志
   208  func Important(v ...interface{}) {
   209  	line := layout(InfoLevel, v)
   210  	_, _ = Logger.Write(line)
   211  	write2Console(line)
   212  }
   213  
   214  // Importantf 打印重要的日志
   215  func Importantf(format string, a ...interface{}) {
   216  	line := layoutf(InfoLevel, format, nil, a...)
   217  	_, _ = Logger.Write(line)
   218  	write2Console(line)
   219  }
   220  
   221  // Error 打印 error 日志
   222  func Error(v ...interface{}) {
   223  	if LogLevel >= ErrorLevel {
   224  		line := layout(ErrorLevel, v)
   225  		_, _ = Logger.Write(line)
   226  		write2Console(line)
   227  	}
   228  }
   229  
   230  // Errorf 打印 error 日志
   231  func Errorf(format string, a ...interface{}) {
   232  	if LogLevel >= ErrorLevel {
   233  		line := layoutf(ErrorLevel, format, nil, a...)
   234  		_, _ = Logger.Write(line)
   235  		write2Console(line)
   236  	}
   237  }
   238  
   239  // Console 打印日志到控制台
   240  func Console(level int, s string) {
   241  	if level <= ConsoleLevel {
   242  		os.Stdout.Write([]byte(s))
   243  	}
   244  }
   245  
   246  // Consolef 打印日志到控制台
   247  func Consolef(level int, format string, a ...interface{}) {
   248  	if level <= ConsoleLevel {
   249  		line := layoutf(level, format, nil, a...)
   250  		os.Stdout.Write(line)
   251  	}
   252  }
   253  
   254  // Consolefc 打印日志到控制台
   255  func Consolefc(level int, format string, callerFn CallerFn, a ...interface{}) {
   256  	if level <= ConsoleLevel {
   257  		line := layoutf(level, format, callerFn, a...)
   258  		os.Stdout.Write(line)
   259  	}
   260  }
   261  
   262  // write2Console 输出到控制台,如果 ShowError2Console==true 时
   263  func write2Console(line []byte) {
   264  	if ShowError2Console {
   265  		os.Stdout.Write(line)
   266  	}
   267  }
   268  
   269  // layout 给日志格式化,给日志带上[goom]前缀, 方便与业务日志区分
   270  func layout(level int, v []interface{}) []byte {
   271  	arr := make([]string, 0, len(v)+1)
   272  	arr = append(arr, time.Now().Format("2006-01-02 15:04:05"))
   273  	arr = append(arr, defaultPrefix, "["+levelName[level]+"]:")
   274  
   275  	if EnableLogTrack {
   276  		arr = append(arr, trackGetter())
   277  	}
   278  
   279  	for _, a := range v {
   280  		arr = append(arr, fmt.Sprintf("%s", a))
   281  	}
   282  
   283  	arr = append(arr, "\n")
   284  	line := strings.Join(arr, " ")
   285  	line = levelColor[level].Add(line)
   286  	return []byte(line)
   287  }
   288  
   289  // layoutf 给日志格式化,带上[goom]前缀和添加颜色等
   290  func layoutf(level int, format string, callerFn CallerFn, a ...interface{}) []byte {
   291  	time := time.Now().Format("2006-01-02 15:04:05")
   292  	if EnableLogTrack {
   293  		return []byte(time + " " + defaultPrefix + "[" + levelName[level] + "]: " +
   294  			trackGetter() + " " + fmt.Sprintf(format, a...) + "\n")
   295  	}
   296  
   297  	line := time + " " + defaultPrefix + "[" + levelName[level] + "]: "
   298  	if callerFn != nil {
   299  		line += callerFn() + " "
   300  	}
   301  	line += fmt.Sprintf(format, a...) + "\n"
   302  	line = levelColor[level].Add(line)
   303  	return []byte(line)
   304  }
   305  
   306  // loggerPath 获取日志存储路径
   307  func loggerPath() (string, error) {
   308  	var logFileLocation = "."
   309  	// 获取当前目录
   310  	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
   311  	if err == nil && "/" != dir {
   312  		logFileLocation = filepath.Join(dir, "logs")
   313  	}
   314  
   315  	// 判断文件夹是否存在
   316  	_, err = os.Stat(logFileLocation)
   317  	if err != nil {
   318  		if os.IsNotExist(err) {
   319  			err = os.Mkdir(logFileLocation, os.ModePerm)
   320  			if err != nil {
   321  				fmt.Println("init log file error:", err)
   322  				return ".", err
   323  			}
   324  		} else {
   325  			return ".", err
   326  		}
   327  	}
   328  	fmt.Println("goom-mocker logFileLocation:", logFileLocation)
   329  	return logFileLocation, err
   330  }
   331  
   332  // CallerFn 获取 Caller 行号的回调函数类型
   333  type CallerFn func() string
   334  
   335  // Caller 默认的 CallerFn, 用于 debug 日志获取调用者的行号
   336  func Caller(skip int) func() string {
   337  	return func() string {
   338  		return caller(skip)
   339  	}
   340  }
   341  
   342  func caller(skip int) string {
   343  	frame, defined := getCallerFrame(skip)
   344  	if !defined {
   345  		return ""
   346  	}
   347  	return path.Base(frame.File) + ":" + strconv.Itoa(frame.Line)
   348  }
   349  
   350  // getCallerFrame gets caller frame. The argument skip is the number of stack
   351  // frames to ascend, with 0 identifying the caller of getCallerFrame. The
   352  // boolean ok is false if it was not possible to recover the information.
   353  //
   354  // Note: This implementation is similar to runtime.Caller, but it returns the whole frame.
   355  func getCallerFrame(skip int) (frame runtime.Frame, ok bool) {
   356  	const skipOffset = 2 // skip getCallerFrame and Callers
   357  	pc := make([]uintptr, 1)
   358  	numFrames := runtime.Callers(skip+skipOffset, pc)
   359  	if numFrames < 1 {
   360  		return
   361  	}
   362  	frame, _ = runtime.CallersFrames(pc).Next()
   363  	return frame, frame.PC != 0
   364  }