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 }