日志库实现"/>
GoLang 异步日志库实现
GoLang 异步日志库实现
- 公共的方法
type LogLevel uint8const (L_DEBUG LogLevel = iotaL_WARNINGL_ERROR
)func getLogLevel(level LogLevel) string {switch level {case L_DEBUG:return "DEBUG"case L_WARNING:return "WARNING"case L_ERROR:return "ERROR"default:return "nil"}
}func getLogInfo(skip int) (fileName, funcName string, l int) {pc, file, line, ok := runtime.Caller(skip)if !ok {fmt.Println("runtime.Caller Error")return}fileName = path.Base(file)funcName = runtime.FuncForPC(pc).Name()l = linereturn
}
- 结构体方法
使用结构体指针作为通道传输信息,而不使用string字符串传递可以节省性能开销
在该程序中使用select语句中的defult可以避免因接收不了通道的信息而阻塞正常程序的运行
type FileLogger struct {level LogLevellogFilePath stringlogFileName stringmaxFileSize int64fileWriter *os.FileerrFileWriter *os.FilelogChan chan *logPack
}
type logPack struct {level LogLeveltime stringfileName stringfuncName stringline intmsg stringfile *os.File
}func NewFileLogger(level LogLevel, path, name string, maxSize int64) *FileLogger {logger := &FileLogger{level: level,logFilePath: path,logFileName: name,maxFileSize: maxSize,logChan: make(chan *logPack, 50000),}logger.initWriter()go logger.writeLog()return logger
}func (f *FileLogger) Debug(format string, a ...any) {f.createInfo(L_DEBUG, format, a...)
}
func (f *FileLogger) Warning(format string, a ...any) {f.createInfo(L_WARNING, format, a...)
}
func (f *FileLogger) Error(format string, a ...any) {f.createInfo(L_ERROR, format, a...)
}
func (f *FileLogger) createInfo(targetLogLevel LogLevel, format string, a ...any) {if f.level <= targetLogLevel {f.makeAndWriteInfo(f.fileWriter, targetLogLevel, format, a...)}//当错误等级大于等于ERROR时,需要将错误单独写到一个文件中去if targetLogLevel >= L_ERROR {f.makeAndWriteInfo(f.errFileWriter, targetLogLevel, format, a...)}
}
func (f *FileLogger) makeAndWriteInfo(file *os.File, targetLogLevel LogLevel, format string, a ...any) {msg := fmt.Sprintf(format, a...)fileName, funcName, line := getLogInfo(4)pack := &logPack{level: targetLogLevel,time: time.Now().Format("2006-01-02 15:04:05"),fileName: fileName,funcName: funcName,line: line,msg: msg,file: file,}select {case f.logChan <- pack:default:}
}
func (f *FileLogger) checkSize(file *os.File) bool {fileInfo, err := file.Stat()if err != nil {fmt.Println(err)panic("checkSize Error:")}size := fileInfo.Size()return size >= f.maxFileSize
}
func (f *FileLogger) writeLog() {for true {select {case pack := <-f.logChan:file := pack.fileif f.checkSize(file) {//需要切割//1.关闭旧的文件写入oldPath := path.Join(f.logFilePath, file.Name())newPath := oldPath + ".ok" + strconv.FormatInt(time.Now().UnixNano(), 10)err := file.Close()if err != nil {fmt.Println(err)return}//2.重命名旧的文件err = os.Rename(oldPath, newPath)if err != nil {fmt.Println(err)return}//3.创建新的文件写入newFile, err := os.OpenFile(oldPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModePerm)if err != nil {fmt.Println(err)return}//4.设置新的fileif *file == *f.fileWriter {f.fileWriter = newFile} else {f.errFileWriter = newFile}}_, err := fmt.Fprintf(file, "[%s][%s][%s:%s:%d]:%s\n",getLogLevel(pack.level),pack.time,pack.fileName,pack.funcName,pack.line,pack.msg)if err != nil {fmt.Println(err)return}default:time.Sleep(time.Millisecond * 500)}}
}
func (f *FileLogger) initWriter() {p := path.Join(f.logFilePath, f.logFileName)file, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModePerm)if err != nil {fmt.Println(err)return}errFile, err := os.OpenFile(p+".err", os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModePerm)if err != nil {fmt.Println(err)return}f.fileWriter = filef.errFileWriter = errFile
}
func (f *FileLogger) Close() {err := f.fileWriter.Close()if err != nil {fmt.Println(err)}err = f.errFileWriter.Close()if err != nil {fmt.Println(err)}f.fileWriter = nilf.errFileWriter = nil
}
更多推荐
GoLang 异步日志库实现
发布评论