目前
- 日志库需求分析
- 日志库接口设计
- 文件日志库开发
- Console日志开发
- 日志使用以及测试
日志库需求分析
1、日志库需求分析
A. 程序运行是个黑盒
B. 而日志是程序运行的外在表现
C. 通过日志,可以知道程序的健康状态
2、日志库需求分析
A. Debug级别:用来调试程序,日志最详细。对程序性能影响比较大。
B. Trace级别:用来追踪问题。
C. Info级别:打印程序运行过程中比较重要的信息,比如访问日志
D. Warn级别:警告日志,说明程序运行出现了潜在的问题
E. Error级别:错误日志,程序运行发生错误,但不影响程序运行。
F. Fatal级别:严重错误日志,发生的错误会导致程序退出
3、日志库需求分析
A. 直接输出到控制台
B. 打印到文件里
C. 直接打印到网络中,比如kafka
日志库接口设计
1、为什么使用接口?
A. 定义日志库的规范或者标准
B. 易于可扩展性
C. 利于程序的可维护性
2、 日志库设计
A. 打印各个level的日志
B. 设置级别
C. 构造函数
文件日志库开发
1、关键代码
package logger import ( "fmt" "os" "strconv" "time" ) type FileLogger struct { level int logPath string logName string file *os.File warnFile *os.File // 异步通道 LogDataChan chan *LogData // 日志切分 logSplitType int logSplitSize int64 lastSplitHour int } func NewFileLogger(config map[string]string) (log LogInterface, err error) { logPath, ok := config["log_path"] if !ok { err = fmt.Errorf("not found log_path") return } logName, ok := config["log_name"] if !ok { err = fmt.Errorf("not found log_name") return } logLevel, ok := config["log_level"] if !ok { err = fmt.Errorf("not found log_level") return } level := getLogLevel(logLevel) // 异步管道初始化 logChanSize, ok := config["log_chan_size"] if !ok { logChanSize = "50000" } //检查logChanSize转数字 chanSize, err := strconv.Atoi(logChanSize) if err != nil { chanSize = 50000 } //日志切片 var logSplitType int = LogSplitTypeHour var logSplitSize int64 logSplitStr, ok := config["log_plit_type"] if !ok { logSplitStr = "hour" } else { if logSplitStr == "size" { logSplitSizeStr, ok := config["log_split_size"] if !ok { logSplitSizeStr = "104857600" } logSplitSize, err = strconv.ParseInt(logSplitSizeStr, 10, 64) if err != nil { logSplitSize = 104857600 } logSplitType = LogSplitTypeSize } else { logSplitType = LogSplitTypeHour } } log = &FileLogger{ level: level, logPath: logPath, logName: logName, // 异步写日志 LogDataChan: make(chan *LogData, chanSize), // 日志切分 logSplitSize: logSplitSize, logSplitType: logSplitType, lastSplitHour: time.Now().Hour(), } // 调试日志分割 // fmt.Println(logSplitSize, logSplitType, logSplitStr) log.Init() return } func (f *FileLogger) SetLevel(level int) { if level < LogLevelDebug || level > LogLevelFatal { level = LogLevelDebug } f.level = level } func (f *FileLogger) Init() { filename := fmt.Sprintf("%s/%s.log", f.logPath, f.logName) file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { panic(fmt.Sprintf("open faile %s failed, err:%v", filename, err)) } f.file = file // 写错误日志和fatal日志文件 filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName) file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { panic(fmt.Sprintf("open faile %s failed, err:%v", filename, err)) } f.warnFile = file go f.writeLogBackground() } func (f *FileLogger) splitFileHour(warnFile bool) { now := time.Now() hour := now.Hour() if hour == f.lastSplitHour { return } f.lastSplitHour = hour var backupFilename string var filename string if warnFile { backupFilename = fmt.Sprintf("%s/%s.log.wf_%4d%02d%02d%02d", f.logPath, f.logName, now.Year(), now.Month(), now.Day(), f.lastSplitHour) filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName) } else { backupFilename = fmt.Sprintf("%s/%s.log_%4d%02d%02d%02d", f.logPath, f.logName, now.Year(), now.Month(), now.Day(), f.lastSplitHour) filename = fmt.Sprintf("%s/%s.log", f.logPath, f.logName) } file := f.file if warnFile { file = f.warnFile } file.Close() os.Rename(filename, backupFilename) file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { return } if warnFile { f.warnFile = file } else { f.file = file } } func (f *FileLogger) splitFileSize(warnFile bool) { file := f.file if warnFile { file = f.warnFile } statInfo, err := file.Stat() if err != nil { return } fileSize := statInfo.Size() if fileSize <= f.logSplitSize { return } var backupFilename string var filename string now := time.Now() if warnFile { backupFilename = fmt.Sprintf("%s/%s.log.wf_%4d%02d%02d%02d%02d%02d", f.logPath, f.logName, now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()) filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName) } else { backupFilename = fmt.Sprintf("%s/%s.log_%4d%02d%02d%02d", f.logPath, f.logName, now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()) filename = fmt.Sprintf("%s/%s.log", f.logPath, f.logName) } file.Close() os.Rename(filename, backupFilename) file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { return } if warnFile { f.warnFile = file } else { f.file = file } } func (f *FileLogger) checkSplitFile(warnFile bool) { if f.logSplitType == LogSplitTypeHour { f.splitFileHour(warnFile) return } f.splitFileSize(warnFile) } func (f *FileLogger) writeLogBackground() { for data := range f.LogDataChan { var file *os.File = f.file if data.WarnAndFatal { file = f.warnFile } f.checkSplitFile(data.WarnAndFatal) fmt.Fprintf(file, "%s %s (%s:%s:%d) %s\n", data.LevelStr, data.FileName, data.FuncName, data.LineNo, data.Message) } } func (f *FileLogger) Debug(format string, args ...interface{}) { if f.level > LogLevelDebug { return } logData := writeLog(LogLevelDebug, format, args...) // 判断队列有没有满 select { case f.LogDataChan <- logData: default: } } func (f *FileLogger) Trace(format string, args ...interface{}) { if f.level > LogLevelTrace { return } logData := writeLog(LogLevelTrace, format, args...) // 判断队列有没有满 select { case f.LogDataChan <- logData: default: } } func (f *FileLogger) Info(format string, args ...interface{}) { if f.level > LogLevelInfo { return } logData := writeLog(LogLevelInfo, format, args...) // 判断队列有没有满 select { case f.LogDataChan <- logData: default: } } func (f *FileLogger) Warn(format string, args ...interface{}) { if f.level > LogLevelWarn { return } logData := writeLog(LogLevelWarn, format, args...) // 判断队列有没有满 select { case f.LogDataChan <- logData: default: } } func (f *FileLogger) Fatal(format string, args ...interface{}) { if f.level > LogLevelFatal { return } logData := writeLog(LogLevelFatal, format, args...) // 判断队列有没有满 select { case f.LogDataChan <- logData: default: } } func (f *FileLogger) Error(format string, args ...interface{}) { if f.level > LogLevelError { return } logData := writeLog(LogLevelError, format, args...) // 判断队列有没有满 select { case f.LogDataChan <- logData: default: } } func (f *FileLogger) Close() { f.file.Close() f.warnFile.Close() }
Console日志开发
1、关键代码
package logger import ( "fmt" "os" ) type ConsoleLogger struct { level int } func NewConsoleLogger(config map[string]string) (log LogInterface, err error) { logLevel, ok := config["log_level"] if !ok { err = fmt.Errorf("not found log_level") return } level := getLogLevel(logLevel) log = &ConsoleLogger{ level: level, } return } func (c *ConsoleLogger) Init() { } func (c *ConsoleLogger) SetLevel(level int) { if level < LogLevelDebug || level > LogLevelFatal { level = LogLevelDebug } c.level = level } func (c *ConsoleLogger) Debug(format string, args ...interface{}) { if c.level > LogLevelDebug { return } logData := writeLog(LogLevelDebug, format, args...) fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } func (c *ConsoleLogger) Trace(format string, args ...interface{}) { if c.level > LogLevelTrace { return } logData := writeLog(LogLevelTrace, format, args...) fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } func (c *ConsoleLogger) Info(format string, args ...interface{}) { if c.level > LogLevelInfo { return } logData := writeLog(LogLevelInfo, format, args...) fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } func (c *ConsoleLogger) Warn(format string, args ...interface{}) { if c.level > LogLevelWarn { return } logData := writeLog(LogLevelWarn, format, args...) fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } func (c *ConsoleLogger) Fatal(format string, args ...interface{}) { if c.level > LogLevelFatal { return } logData := writeLog(LogLevelFatal, format, args...) fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } func (c *ConsoleLogger) Error(format string, args ...interface{}) { if c.level > LogLevelError { return } logData := writeLog(LogLevelError, format, args...) fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } func (c *ConsoleLogger) Close() { }
日志使用以及测试
1、关键代码
package logger import "testing" // run test| debug test func TestFileLogger(t *testing.T) { logger := NewFileLogger(LogLevelDebug, "d:/logs/", "test") logger.Debug("user id[%d] is come from chaina", 3242342) logger.Warn("test warn log") logger.Fatal("test fatal log") logger.Close() } // run test| debug test func TestConsoleLogger(t *testing.T) { logger := NewConsoleLogger(LogLevelDebug) logger.Debug("user id[%d] is come from chaina", 3242342) logger.Warn("test warn log") logger.Fatal("test fatal log") logger.Close() }