内容简介:日志库基本上写完了,但是就完成了,还没有。目前来说,我们的文件写日志的方式采用同步方式,如果在大量日志写入的时候,同步写入的缺陷就暴露出来了。而且日志跟我们的业务逻辑也没有什么太大的关系,哪怕掉了几条也没什么影响,所以这里可以将同步写改为异步写。这里采用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系列(六)- 洗牌算法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
HTML5与CSS3基础教程(第7版)
[美] Elizabeth Castro、[美] Bruce Hyslop / 望以文 / 人民邮电出版社 / 2013-1 / 59.00元
代表下一代网页编写技术的HTML5,为网页提供布局和格式的CSS3,这两者构成了Web开发的基石,也是Web程序员和设计师必须熟练掌握的最基本技能。 本书是风靡全球的HTML和CSS最佳入门教程的最新版,上一版单单英文版的销量就超过100万册,被翻译为十多种语言,并长期雄踞亚马逊书店计算机图书排行榜榜首。 最新的第7版秉承前一版直观、透彻、全面、循序渐进的讲授特色,仍然采用独特的双栏图......一起来看看 《HTML5与CSS3基础教程(第7版)》 这本书的介绍吧!