内容简介:日志库基本上写完了,但是就完成了,还没有。目前来说,我们的文件写日志的方式采用同步方式,如果在大量日志写入的时候,同步写入的缺陷就暴露出来了。而且日志跟我们的业务逻辑也没有什么太大的关系,哪怕掉了几条也没什么影响,所以这里可以将同步写改为异步写。这里采用go的channel进行异步写日志。首先,我们定义一个channel的结构体用来传递日志数据,在
日志库基本上写完了,但是就完成了,还没有。目前来说,我们的文件写日志的方式采用同步方式,如果在大量日志写入的时候,同步写入的缺陷就暴露出来了。而且日志跟我们的业务逻辑也没有什么太大的关系,哪怕掉了几条也没什么影响,所以这里可以将同步写改为异步写。
这里采用 go 的channel进行异步写日志。
- 在业务调用写日志时,将日志数据写入channel中
- 起一个后台线程不断的从channel中取日志,然后写入到文件中。
首先,我们定义一个channel的结构体用来传递日志数据,在 util.go
中新建一个结构体:
type LogData struct { Message string TimeStr string LevelStr string FileName string FuncName string LineNo int IsWarn bool //是否写入错误日志文件 } 复制代码
有些这个数据后,我们就可以定义一个channel来进行操作。在 file.go
中增加一个channel:
type FileLog struct { logPath string logName string file *os.File warnFile *os.File logDataChan chan *LogData } func NewFileLog(config map[string]string) (logFile Log, err error) { ... logChanSize, ok := config["log_chan_size"] if !ok { logChanSize = "50000" } chanSize, e := strconv.Atoi(logChanSize) // string to int if e != nil { chanSize = 50000 // channel最大容量 } logFile = &FileLog{ logPath: logPath, logName: logName, logDataChan: make(chan *LogData, chanSize),// 初始化channel } logFile.Init() return } func (f *FileLog) Init() { ... go f.writeLogBackGround() // go 关键字启动一个线程 } func (f *FileLog) writeLogBackGround() { for logData := range f.logDataChan { var file = f.file // 切换日志文件 if logData.IsWarn { file = f.warnFile } fmt.Fprintf(file, "%s %s [%s/%s:%d] %s\n", logData.TimeStr, logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } } func (f *FileLog) Debug(format string, args ...interface{}) { logData := writeLog(DebugLevel, format, args) select { case f.logDataChan <- logData: // 将logData放入f.logDataChan中 default: } } ... func (f *FileLog) Info(format string, args ...interface{}) { logData := writeLog(InfoLevel, format, args) select { case f.logDataChan <- logData: default: } } ... 复制代码
将之前的writeLog改进一下.
func writeLog(level int, format string, args ...interface{}) *LogData { now := time.Now() nowStr := now.Format("2006-01-02 15:04:05.999") levelStr := LogLevelString(level) fileName, funcName, lineNo := GetLineInfo() fileName = path.Base(fileName) funcName = path.Base(funcName) msg := fmt.Sprintf(format, args...) isWarn := level >= WarnLevel && level <= FatalLevel return &LogData{ Message: msg, TimeStr: nowStr, LevelStr: levelStr, FileName: fileName, FuncName: funcName, LineNo: lineNo, IsWarn: isWarn, } } 复制代码
这样就把同步改为了异步,我们日志库又健壮了一些。
由于writeLog有改动,所以还需要修改一下 console.go
的内容。
func (c *ConsoleLog) Debug(format string, args ...interface{}) { logData := MsgInfo(DebugLevel, format, args) fmt.Fprintf(os.Stdout, "%s %s [%s/%s:%d] %s\n", logData.TimeStr, logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } ... func (c *ConsoleLog) Error(format string, args ...interface{}) { logData := MsgInfo(ErrorLevel, format, args) fmt.Fprintf(os.Stdout, "%s %s [%s/%s:%d] %s\n", logData.TimeStr, logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } ... 复制代码
虽然我们修改了那么多,但是在使用日志库的时候没有什么不同,只是我们的日志库更完善了一下。
func initLog(logPath, logName string) { //log := hm_log.NewFileLog(logPath, logName) config := make(map[string]string, 8) config["log_path"] = "." config["log_name"] = "server" config["log_chan_size"] = "50000" //chan size 可以不用 err := InitLog("file", config) if err != nil { return } } 复制代码
OK,我们再来运行一下,日志正常写入,终端也正常打印。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- go开发属于自己的日志库-文件日志库实现
- go 开发属于自己的日志库-文件日志库原型实现
- 打造属于你自己的instagram!
- 属于 Unity 的 Flutter——UIWidgets
- 利用Python写属于自己的翻译命令行
- 打造属于自己的underscore系列(六)- 洗牌算法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java语言程序设计
(美) Y. Daniel Liang / 李娜 / 机械工业出版社 / 2011-6 / 75.00元
本书是Java语言的经典教材,多年来畅销不衰。本书全面整合了Java 6的特性,采用“基础优先,问题驱动”的教学方式,循序渐进地介绍了程序设计基础、解决问题的方法、面向对象程序设计、图形用户界面设计、异常处理、I/O和递归等内容。此外,本书还全面且深入地覆盖了一些高级主题,包括算法和数据结构、多线程、网络、国际化、高级GUI等内容。 本书中文版由《Java语言程序设计:基础篇》和《Java语......一起来看看 《Java语言程序设计》 这本书的介绍吧!