https://github.com/cihub/seeloggit
文档学习:https://github.com/cihub/seelog/wikigithub
1.安装:golang
go get github.com/cihub/seelog
2.快速启动json
Seelog的设计很是方便。它的默认配置和包级别的日志记录器是现成的,因此开始你只须要两行代码:后端
package main import log "github.com/cihub/seelog" func main() { defer log.Flush() log.Info("Hello from Seelog!") }
Info只是Seelog支持的日志级别之一。你还可使用Trace, Debug, Info, Warn, Error, Critical级别。安全
运行返回:服务器
bogon:~ user$ go run testGo.go 1551577771885754000 [Info] Hello from Seelog!
基本配置网络
这是seelog config的一个例子,它使用默认格式选项、约束等将输出重定向到控制台,命名为seelog.xml。并发
<seelog> <outputs> <console /> </outputs> </seelog>
大多数wiki部分介绍使用configs进行的Seelog调优。app
下载配置
在Seelog包中有几个函数能够帮你加载configs。
logger, err := log.LoggerFromConfigAsFile("seelog.xml") if err != nil { return err } log.ReplaceLogger(logger)
这里还有'LoggerFromConfigAsBytes',和'LoggerFromConfigAsString'两种类型的下载函数
你能够在任什么时候候运行log.ReplaceLogger。配置转换可见Changing config on the fly
defer块和刷新
在许多状况下,没法在主goroutine中处理生成的日志信息。
在这些状况下,咱们建议异步日志记录器在非阻塞模式下依次从队列中接收缓冲消息。在这种状况下,确保在应用程序遭受紧急崩溃时不会丢失日志数据是相当重要的。咱们在main函数的defer中使用log. Flush()函数解决了这个问题,它保证日志消息队列中剩下的全部消息都将正常地独立于应用程序无论panic是否进行处理。
注意:在使用Seelog构造以前,defer块必须放在可执行文件的main函数中。在编写包时,不要担忧延迟刷新,详情可见Writing libraries with Seelog
这两个函数都更改了负责当前日志记录器的包级别变量。此变量用于包级函数“Trace”、“Debug”等。可是,请注意区别。
前者正确地关闭前一个日志记录器(使用刷新日志数据),而后用一个新的日志记录器替换它。当你更改日志配置时,这是最推荐的方法。
后者只刷新前一个日志记录器(不关闭它),而后用一个新的日志记录器替换它。当你更改日志记录器而且对关闭旧日志记录器不闻不问时,应该使用此方法。
演示配置的全部功能
有一个演示配置,它在一个地方演示了大多数功能,可见下面的 9.Example config
你能够在深刻研究全部特性以前检查它。
3.日志级别
这一节展现了咱们对Seelog级别层次、它们的含义和使用范围的见解。当咱们根据本身的概念对Seelog代码进行调优时,建议遵循如下规则。
支持的日志级别有:
配置文件的日志级别标识符
日志消息示例
例子:
下面的示例演示了Seelog级别的概念用法。
注1:这个例子实际上在计算方面没有任何意义。它只是突出了日志级别使用上下文中的差别。
注2:有时人们会将Info与Debug甚至Trace混淆。咱们试图找出最引人注目的案例。请注意“Info”用例:它是一个生产日志级别,咱们让它在不影响性能(即便在生产中)的状况下运行。
package main import ( log "github.com/cihub/seelog" "time" "errors" ) type inputData struct { x, y int } type outputData struct { result int error bool } var inputs chan inputData var outputs chan outputData var criticalChan chan int func internalCalculationFunc(x, y int) (result int, err error) { log.Debugf("calculating z. x:%d y:%d", x, y) //报告系统行为,定位开发过程 z := y switch { case x == 3 : log.Trace("x == 3")//进行深度调试:查找函数的问题部分,检查临时变量的值等 panic("Failure.") case y == 1 : log.Trace("y == 1") return 0, errors.New("Error!") case y == 2 : log.Trace("y == 2") z = x default : log.Trace("default") z += x } log.Tracef("z:%d",z) retVal := z-3 log.Debugf("Returning %d", retVal) return retVal, nil } func generateInputs(dest chan inputData) { time.Sleep(1e9) log.Debug("Sending 2 3") dest <- inputData{x : 2, y : 3} time.Sleep(1e9) log.Debug("Sending 2 1") dest <- inputData{x : 2, y : 1} time.Sleep(1e9) log.Debug("Sending 3 4") dest <- inputData{x : 3, y : 4} time.Sleep(1e9) log.Debug("Sending critical") criticalChan <- 1 } func consumeResults(res chan outputData) { for { select { case <- outputs: //在这一点上,咱们获得并输出结果值 } } } func processInput(input inputData) { defer func() { if r := recover(); r != nil {//获取panic中的错误信息 log.Errorf("Unexpected error occurred: %v", r) //记录错误信息 outputs <- outputData{result : 0, error : true} } }() log.Infof("Received input signal. x:%d y:%d", input.x, input.y) //关于应用程序工做的通常信息 res, err := internalCalculationFunc(input.x, input.y) if err != nil { log.Warnf("Error in calculation: %s", err.Error())//用于指示以安全方式自动处理的小错误、奇怪状况和故障 } log.Infof("Returning result: %d error: %t", res, err != nil) outputs <- outputData{result : res, error : err != nil} } func main() { inputs = make(chan inputData) outputs = make(chan outputData) criticalChan = make(chan int) log.Info("App started.") go consumeResults(outputs) //outputs通道等待结果,并将结果输出 log.Info("Started receiving results.") go generateInputs(inputs) log.Info("Started sending signals.")//三次将值发送到inputs通道中,并输入1给criticalChan for { select { case input := <- inputs: //generateInputs每次值输入到inputs时就并发在此输出 processInput(input) //进行内部计算并将结果输入通道outputs case <- criticalChan: //直到generateInputs的最后输入1给criticalChan log.Critical("Caught value from criticalChan: Go shut down.") //在应用程序死亡以前生成最后的消息 panic("Shut down due to critical fault.") } } }
返回:
bogon:~ user$ go run testGo.go 1551581657401394000 [Info] App started. 1551581657401416000 [Info] Started receiving results. 1551581657401419000 [Info] Started sending signals. 1551581658406575000 [Debug] Sending 2 3 1551581658406686000 [Info] Received input signal. x:2 y:3 1551581658406827000 [Debug] calculating z. x:2 y:3 1551581658406850000 [Trace] default 1551581658406860000 [Trace] z:5 1551581658406870000 [Debug] Returning 2 1551581658407009000 [Info] Returning result: 2 error: false 1551581659412207000 [Debug] Sending 2 1 1551581659412273000 [Info] Received input signal. x:2 y:1 1551581659412357000 [Debug] calculating z. x:2 y:1 1551581659412368000 [Trace] y == 1 1551581659412499000 [Warn] Error in calculation: Error! 1551581659412528000 [Info] Returning result: 0 error: true 1551581660414490000 [Debug] Sending 3 4 1551581660414708000 [Info] Received input signal. x:3 y:4 1551581660414760000 [Debug] calculating z. x:3 y:4 1551581660414774000 [Trace] x == 3 1551581660414787000 [Error] Unexpected error occurred: Failure. 1551581661420124000 [Debug] Sending critical 1551581661420188000 [Critical] Caught value from criticalChan: Go shut down. panic: Shut down due to critical fault. goroutine 1 [running]: main.main() /Users/user/testGo.go:109 +0x3bc exit status 2
4.约束和例外
限制
约束限制了规范日志级别的规则。若是没有指定约束,则容许全部日志级别。
有两种类型的约束:全局约束和例外约束
全局约束
全局约束用于整个应用程序,它们使用的是“按期应用的”规则(而不是“例外”)。这些约束是在seelog根元素属性中设置的。
举例说明
若要只容许日志级别“info”及以上,请使用如下命令启动配置:
<seelog minlevel="info">
容许级别从 info到error(即 info, warn, error),使用:
<seelog minlevel="info" maxlevel="error">
要只容许特定的级别集(例如trace, info, and critical级别),请使用如下命令启动配置:
<seelog levels="trace,info,critical">
例外约束
例外,与通常规则相反,被认为是打破(放松或增强)常规规则(通常约束)的特殊状况。例如,你可能但愿限制特定文件或文件组的日志记录。反之亦然:你确实有限制全局约束,而且你但愿容许特定的文件或函数在更深的层次上进行日志记录。
例外包括“filepattern”、“funcpattern”和约束(“minlevel”/“maxlevel”或“levels”)。所以,若是你但愿使用特定的名称模式覆盖函数或文件(或二者)的通常规则,那么能够在“filepattern”/“funcpattern”字段中指定模式,并使用覆盖约束。
例外如何使用
当你在运行时为每一个日志执行日志记录时。在底层调用调用方函数以获取当前上下文。而后咱们发现匹配模式file/func名的第一个例外。若是发现这样的例外,则其约束将覆盖常规约束。
建议
根据上面所说的,有一些简单的建议:
举例说明:
让咱们从“test”开始,为全部文件建立更多的限制规则。
<seelog minlevel="info"> <exceptions> <exception filepattern="test*" minlevel="error"/> </exceptions>
经过这种方式,你将得到全部文件的“info”、“warn”、“error”、“critical”消息,但以“test”开头的文件除外。对于以“test”开头的文件,你只会获得“error”和“critical”消息。
另外一个例子。如今让咱们建立一个相反的状况:让咱们只容许“critical”消息做为通常规则,但容许“main.testFunc”函数为“warn, error, critical”级别(package 'main', func 'testFunc'):
<seelog levels="critical"> <exceptions> <exception funcpattern="main.testFunc" minlevel="warn"/> </exceptions>
让咱们建立一个生产就绪配置:
<seelog minlevel="info"> <exceptions> <exception funcpattern="main.testFunc" minlevel="warn"/> <exception funcpattern="main.testFunc2" minlevel="error"/> <exception funcpattern="*test*" filepattern="tests.go" levels="off"/> <exception funcpattern="*perfCritical" minlevel="critical"/> </exceptions> ...
这个配置彻底能够用于生产,由于它没有任何容许级别“trace”或“debug”的例外
让咱们先测试一下“更常见的例外状况”规则:
<seelog minlevel="info"> <exceptions> <exception funcpattern="main.testFunc" levels="critical"/> <exception funcpattern="main.test*" minlevel="error"/> <exception funcpattern="main.*" minlevel="warn"/> </exceptions> ...
这个配置将像它看起来的那样工做。但若是你以另外一种顺序写这些例外,它就不同了。例如,若是你将“main.*”放在例外的前面,那么其余两个例外将被忽略。
“off”日志级别
“off”是一个特殊的日志级别,它意味着禁用日志记录。它能够在minlevel和level约束中使用,所以你能够在全局约束或例外约束中写入'minlevel= “off”'和'levels= “off”'来禁用日志。
示例
package main import ( "fmt" log "github.com/cihub/seelog" ) func main() { defer log.Flush() testMinMax() testMin() testMax() testList() testFuncException() testFileException() } func testMinMax() { //只会输出日志级别在info和error之间的日志的内容,即info\warn\error fmt.Println("testMinMax") testConfig := ` <seelog type="sync" minlevel="info" maxlevel="error"> <outputs><console/></outputs> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("NOT Printed") log.Debug("NOT Printed") log.Info("Printed") log.Warn("Printed") log.Error("Printed") log.Critical("NOT Printed") } func testMin() { //会输出大于info日志级别的日志内容,即info\warn\error\critical fmt.Println("testMin") testConfig := ` <seelog type="sync" minlevel="info"> <outputs><console/></outputs> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("NOT Printed") log.Debug("NOT Printed") log.Info("Printed") log.Warn("Printed") log.Error("Printed") log.Critical("Printed") } func testMax() {//会输出级别不大于error的日志文件的信息,即trace、debug、info、warn和error fmt.Println("testMax") testConfig := ` <seelog type="sync" maxlevel="error"> <outputs><console/></outputs> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("Printed") log.Debug("Printed") log.Info("Printed") log.Warn("Printed") log.Error("Printed") log.Critical("NOT Printed") } func testList() {//只输出日志级别为info, trace, critical的日志 fmt.Println("testList") testConfig := ` <seelog type="sync" levels="info, trace, critical"> <outputs><console/></outputs> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("Printed") log.Debug("NOT Printed") log.Info("Printed") log.Warn("NOT Printed") log.Error("NOT Printed") log.Critical("Printed") } //主限制是输出大于info日志级别的日志内容, //可是这里有个例外,要求知足函数名为"*main.test*Except*"的函数中的日志输出的是日志级别大于error的日志信息 //因此这里最后输出的是error、critical日志 func testFuncException() { fmt.Println("testFuncException") testConfig := ` <seelog type="sync" minlevel="info"> <exceptions> <exception funcpattern="*main.test*Except*" minlevel="error"/> </exceptions> <outputs> <console/> </outputs> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("NOT Printed") log.Debug("NOT Printed") log.Info("NOT Printed") log.Warn("NOT Printed") log.Error("Printed") log.Critical("Printed") log.Current.Trace("NOT Printed") log.Current.Debug("NOT Printed") log.Current.Info("NOT Printed") log.Current.Warn("NOT Printed") log.Current.Error("Printed") log.Current.Critical("Printed") } //这里由于testFileException名不知足例外中的"*main.go",因此返回的内容为大于info日志级别的日志内容,即info\warn\error\critical func testFileException() { fmt.Println("testFileException") testConfig := ` <seelog type="sync" minlevel="info"> <exceptions> <exception filepattern="*main.go" minlevel="error"/> </exceptions> <outputs> <console/> </outputs> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("NOT Printed") log.Debug("NOT Printed") log.Info("NOT Printed") log.Warn("NOT Printed") log.Error("Printed") log.Critical("Printed") }
返回:
userdeMBP:go-learning user$ go run test.go testMinMax 1551696134714593000 [Info] Printed 1551696134714624000 [Warn] Printed 1551696134714643000 [Error] Printed testMin 1551696134714668000 [Info] Printed 1551696134714674000 [Warn] Printed 1551696134714679000 [Error] Printed 1551696134714684000 [Critical] Printed testMax 1551696134714700000 [Trace] Printed 1551696134714708000 [Debug] Printed 1551696134714714000 [Info] Printed 1551696134714718000 [Warn] Printed 1551696134714723000 [Error] Printed testList 1551696134714745000 [Trace] Printed 1551696134714751000 [Info] Printed 1551696134714758000 [Critical] Printed testFuncException 1551696134714822000 [Error] Printed 1551696134714837000 [Critical] Printed 1551696134714847000 [Error] Printed 1551696134714852000 [Critical] Printed testFileException 1551696134714888000 [Info] NOT Printed 1551696134714895000 [Warn] NOT Printed 1551696134714904000 [Error] Printed 1551696134714909000 [Critical] Printed
5.Dispatchers and receivers分配器和接收器
1)
接收器Receivers
咱们对后端字节接收器使用“receiver”术语,如日志文件、网络通道等。
分配器Dispatchers
咱们使用“dispatcher”术语表示向多个底层 接收者receivers/分配器dispatchers发送消息的中间元素。
举例说明:
进行dispatcher/receiver配置的主要目标是使用公共格式选项或容许的日志级别建立不一样的组。例如,让咱们建立一个示例配置:
<seelog> <outputs> <splitter formatid="common"> <console/> <file path="file.log"/> <conn addr="192.168.0.2:8123"/> </splitter> <filter levels="critical"> <file path="critical.log" formatid="critical"/> <smtp formatid="criticalemail" senderaddress="noreply-notification-service@none.org" sendername="Automatic notification service" hostname="mail.none.org" hostport="587" username="nns" password="123"> <recipient address="john-smith@none.com"/> <recipient address="hans-meier@none.com"/> </smtp> </filter> </outputs> <formats> <format id="common" format="[%LEV] %Msg"/> <format id="critical" format="%Time %Date %RelFile %Func %Msg"/> <format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/> </formats> </seelog>
所以,在这里咱们使用一个“splitter”元素来按格式(“common”)将三个接收器分组,其余两个接收器使用“filter”按容许的日志级别分组。注意,顶部的元素“output”自己就是一个拆分器splitter,所以咱们能够简化配置:
<seelog> <outputs formatid="common"> <console/> <file path="file.log"/> <conn addr="192.168.0.2:8123"/> <filter levels="critical"> <file path="critical.log" formatid="critical"/> <smtp formatid="criticalemail" senderaddress="noreply-notification-service@none.org" sendername="Automatic notification service" hostname="mail.none.org" hostport="587" username="nns" password="123"> <recipient address="john-smith@none.com"/> <recipient address="hans-meier@none.com"/> </smtp> </filter> </outputs> <formats> <format id="common" format="[%LEV] %Msg"/> <format id="critical" format="%Time %Date %RelFile %Func %Msg"/> <format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/> </formats> </seelog>
Formatting格式化
格式仅在写入字节接收器时应用。若是没有设置“formatid”,Dispatchers将继承格式标识符。若是设置了“formatid”,分配器和字节接收器将覆盖任何继承的格式。
让咱们使用上面例子中的配置:
<seelog> <outputs formatid="common"> <console/> <file path="file.log"/> <network address="192.168.0.2" port="8123"/> <filter levels="critical"> <file path="critical.log" formatid="critical"/> <smtp formatid="criticalemail" senderaddress="noreply-notification-service@none.org" sendername="Automatic notification service" hostname="mail.none.org" hostport="587" username="nns" password="123"> <recipient address="john-smith@none.com"/> <recipient address="hans-meier@none.com"/> </smtp> </filter> </outputs> <formats> <format id="common" format="[%LEV] %Msg"/> <format id="critical" format="%Time %Date %RelFile %Func %Msg"/> <format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/> </formats> </seelog>
它演示了继承/覆盖(inheritance/overriding)特性。最上面的splitter有formatid=“common”,所以它的全部子组件都继承它:console, file, network,和filter。filter中的file和smtp接收器不会继承它,由于它们用本身的格式文件覆盖它。若是smtp没有一个“formatid”属性集,那么它将从其父组件“filter”分配器中继承formatid=“common”。
示例
package main import ( "fmt" log "github.com/cihub/seelog" "time" ) func main() { defer log.Flush() runExample(consoleWriter) runExample(fileWriter) runExample(rollingFileWriter) runExample(rollingFileWriterManyRolls) runExample(bufferedWriter) runExample(bufferedWriterWithFlushPeriod) runExample(bufferedWriterWithOverflow) runExample(splitDispatcher) runExample(filterDispatcher) //runExample(smtpWriter) } func runExample(exampleFunc func()) { exampleFunc() fmt.Println() } //在终端标准输出中输出"Console writer" //并在终端输出5个trace日志信息 func consoleWriter() { testConfig := ` <seelog> <outputs> <console /> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("Console writer") doLog() } //将5个trace日志信息写到文件./log/log.log中 func fileWriter() { testConfig := ` <seelog> <outputs> <file path="./log/log.log"/> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("File writer") doLog() } //将日志信息写到回滚文件./log/roll.log中 //以大小size存储,文件最大为100个字节,文件个数最多为5 func rollingFileWriter() { testConfig := ` <seelog> <outputs> <rollingfile type="size" filename="./log/roll.log" maxsize="100" maxrolls="5" /> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("Rolling file writer") doLog() } func rollingFileWriterManyRolls() { testConfig := ` <seelog> <outputs> <rollingfile type="size" filename="./log/manyrolls.log" maxsize="100" maxrolls="4" /> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("Rolling file writer. Many rolls") doLogBig() } func bufferedWriter() { testConfig := ` <seelog> <outputs> <buffered size="10000"> <file path="./log/bufFile.log"/> </buffered> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("Buffered file writer. NOTE: file modification time not changed until next test (buffered)") time.Sleep(3e9) for i := 0; i < 3; i++ { doLog() time.Sleep(5e9) } time.Sleep(2e9) } func bufferedWriterWithFlushPeriod() { testConfig := ` <seelog> <outputs> <buffered size="10000" flushperiod="1000"> <file path="./log/bufFileFlush.log"/> </buffered> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("Buffered file writer with flush period. NOTE: file modification time changed after each 'doLog' because of small flush period.") time.Sleep(3e9) for i := 0; i < 3; i++ { doLog() time.Sleep(5e9) } time.Sleep(2e9) } //虽然溢出,可是日志信息仍所有存储 func bufferedWriterWithOverflow() { testConfig := ` <seelog> <outputs> <buffered size="20"> <file path="./log/bufOverflow.log"/> </buffered> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("Buffered file writer with overflow. NOTE: file modification time changes after each 'doLog' because of overflow") time.Sleep(3e9) for i := 0; i < 3; i++ { doLog() time.Sleep(5e9) } time.Sleep(1e9) } //日志信息在输入日志文件split.log同时也输出到标准输出中 func splitDispatcher() { testConfig := ` <seelog> <outputs> <file path="./log/split.log"/> <console /> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("Split dispatcher") doLog() } //只有trace日志级别的信息存储到filter.log文件中,可是trace和debug的日志信息都会输出到标准输出中 func filterDispatcher() { testConfig := ` <seelog> <outputs> <filter levels="trace"> <file path="./log/filter.log"/> </filter> <console /> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("Filter dispatcher") for i:=0; i < 5; i++ { log.Trace("This message on console and in file") log.Debug("This message only on console") } } func smtpWriter() { testConfig := ` <seelog> <outputs> <smtp senderaddress="noreply-notification-service@none.org" sendername="Automatic notification service" hostname="mail.none.org" hostport="587" username="nns" password="123"> <recipient address="john-smith@none.com"/> </smtp> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) fmt.Println("SMTP writer is now sending emails to the specified recipients") doLog() } func doLog() { for i:=0; i < 5; i++ { log.Tracef("%d", i) } } func doLogBig() { for i:=0; i < 50; i++ { log.Tracef("%d", i) } }
返回:
userdeMBP:go-learning user$ go run test.go Console writer 1551697317265791000 [Trace] 0 1551697317265815000 [Trace] 1 1551697317265818000 [Trace] 2 1551697317265819000 [Trace] 3 1551697317265820000 [Trace] 4 File writer Rolling file writer Rolling file writer. Many rolls Buffered file writer. NOTE: file modification time not changed until next test (buffered) Buffered file writer with flush period. NOTE: file modification time changed after each 'doLog' because of small flush period. Buffered file writer with overflow. NOTE: file modification time changes after each 'doLog' because of overflow Split dispatcher 1551697376322223000 [Trace] 0 1551697376322243000 [Trace] 1 1551697376322250000 [Trace] 2 1551697376322256000 [Trace] 3 1551697376322262000 [Trace] 4 Filter dispatcher 1551697376322864000 [Trace] This message on console and in file 1551697376322876000 [Debug] This message only on console 1551697376322891000 [Trace] This message on console and in file 1551697376322897000 [Debug] This message only on console 1551697376322900000 [Trace] This message on console and in file 1551697376322903000 [Debug] This message only on console 1551697376322906000 [Trace] This message on console and in file 1551697376322910000 [Debug] This message only on console 1551697376323322000 [Trace] This message on console and in file 1551697376323329000 [Debug] This message only on console
一个文件中最多有100个字节,所以这里一个文件中只写入了4行日志信息,其余的日志信息将另起一个文件存储,以前的文件将被重命名为roll.log.1,后来的日志内容仍会存在roll.log中,若是再超出,目前roll.log中的内容将存在名为roll.log.2的文件中,新内容仍存储在roll.log中
好比函数rollingFileWriter运行第一次的日志文件有两个,其内容为:
userdeMBP:go-learning user$ cat ./log/roll.log 1551698497839015000 [Trace] 4 userdeMBP:go-learning user$ cat ./log/roll.log.1 1551698497838979000 [Trace] 0 1551698497839007000 [Trace] 1 1551698497839010000 [Trace] 2 1551698497839013000 [Trace] 3
再运行一次函数rollingFileWriter,可见日志文件变成了三个,内容分别为:
userdeMBP:go-learning user$ cat ./log/roll.log.1 1551698497838979000 [Trace] 0 1551698497839007000 [Trace] 1 1551698497839010000 [Trace] 2 1551698497839013000 [Trace] 3 userdeMBP:go-learning user$ cat ./log/roll.log.2 1551698497839015000 [Trace] 4 1551698882929867000 [Trace] 0 1551698882929892000 [Trace] 1 1551698882929894000 [Trace] 2 userdeMBP:go-learning user$ cat ./log/roll.log 1551698882929896000 [Trace] 3 1551698882929897000 [Trace] 4
运行函数rollingFileWriterManyRolls后,日志文件为:
可见该日志文件一直保持5个的数量,后面的日志将前面的日志覆盖
userdeMBP:go-learning user$ cat ./log/manyrolls.log.9 1551697317268125000 [Trace] 32 1551697317268127000 [Trace] 33 1551697317268128000 [Trace] 34 1551697317269659000 [Trace] 35 userdeMBP:go-learning user$ cat ./log/manyrolls.log.12 1551697317271189000 [Trace] 44 1551697317271192000 [Trace] 45 1551697317271194000 [Trace] 46 1551697317271196000 [Trace] 47 userdeMBP:go-learning user$ cat ./log/manyrolls.log 1551697317271197000 [Trace] 48 1551697317271198000 [Trace] 49
2)分配器(dispatchers)列表:Reference
下面不一样的dispatcher是用于指明发送消息的方式
功能:将接收到的消息发送到全部子节点。用于细分<outputs>日志格式,内部支持:file(文件), rollingfile(滚动文件,自动清除过时)
元素名:Splitter
容许属性:
举例说明:
<seelog> <outputs> <splitter formatid="format1"> <file path="log.log"/> <file path="log2.log"/> </splitter> <splitter formatid="format2"> <file path="log3.log"/> <file path="log4.log"/> </splitter> </outputs> <formats> ... </formats> </seelog>
注:“output”元素也是一个Splitter,这只是顶部Splitter的一个特殊别名,所以,能够写成:
<seelog> <outputs> <file path="split.log"/> <console /> </outputs> </seelog>
上面的例子等价于:
<seelog> <splitter> <file path="split.log"/> <console /> </splitter> </seelog>
但后者在句法上并不正确。
功能:仅当接收到的消息的日志级别在“allowed”列表中时,才向全部子节点发送接收到的消息。用于单独处理某个或某些级别日志
元素名:Filter
容许属性:
formatid—不覆盖它的全部子节点将继承的格式
levels—“allowed”列表中的逗号分隔的日志级别标识符(日志级别)列表
举例说明:
<seelog> <outputs> <filter levels="trace,debug"> <file path="filter.log"/> </filter> <console /> </outputs> </seelog>
3)接收器(receivers)列表:Reference
下面的不一样种类的writer的目的其实就是将接收到的信息写入不一样的地方
功能:将收到的消息写到一个文件中
元素名:file
容许属性:
<seelog> <outputs> <file path="log.log"/> </outputs> </seelog>
注意:
功能:将收到的消息写到标准输出中
元素名:Console
容许属性:
<seelog> <outputs> <console/> </outputs> </seelog>
功能:将接收到的消息写入文件,直到日期更改或文件超过指定的限制。在此以后,将重命名当前日志文件并开始将日志写到新文件中。若是按大小滚动,能够设置重命名文件计数的限制(若是须要的话),而后当文件计数超过指定的限制时,滚动写入器将删除旧的文件。
这样作的好处就是可以控制日志的大小,对于一个高流量的Web应用来讲,日志的增加是十分可怕的。这样就可以保证日志文件不会由于不断变大而致使咱们的磁盘空间不够引发问题
元素名:rollingfile
容许属性:
当容许'size'类型时的属性:
当容许'date'类型时的属性:
⚠️
举例说明:
<seelog> <outputs> <rollingfile type="size" filename="logs/roll.log" maxsize="1000" maxrolls="5" /> </outputs> </seelog>
<seelog> <outputs> <rollingfile type="date" filename="logs/roll.log" datepattern="02.01.2006" maxrolls="7" /> </outputs> </seelog>
功能:充当缓冲区包装其余写入器。缓冲写入器将数据存储在内存中,并在每次刷新周期或缓冲区满时刷新数据。将日志先存在内存中,按期写入文件,适合日志并发量较大或 IO 比较紧张的场合
元素名:buffered
容许的属性:
举例说明:
<seelog> <outputs> <buffered size="10000" flushperiod="1000"> <file path="bufFile.log"/> </buffered> </outputs> </seelog>
<seelog> <outputs> <buffered size="10000" flushperiod="1000" formatid="someFormat"> <rollingfile type="date" filename="logs/roll.log" datepattern="02.01.2006" /> </buffered> </outputs> <formats> ... </formats> </seelog>
注意:该写入器使用特定格式累计写入的数据,而后将其刷新到内部写入器中。所以,内部写入器不能有本身的格式:仅为缓冲元素设置“formatid”,如上一个示例中所示。
功能:在给定的post服务器上使用密码保护(但一般不安全)的电子邮件账户向指定的收件人发送电子邮件。经过邮件smtp方式将日志文件发送出去(通常会发给相应的运维人员)
元素名:smtp
容许使用的属性:
子元素名:recipient
容许使用的属性:
子元素名:cacertdirpath
举例说明:
<seelog> <outputs> <smtp senderaddress="noreply-notification-service@none.org" sendername="Automatic notification service" hostname="mail.none.org" hostport="587" username="nns" password="123"> <recipient address="john-smith@none.com"/> <recipient address="hans-meier@none.com"/> </smtp> </outputs> </seelog>
<seelog> <outputs> <filter levels="error,critical"> <smtp senderaddress="nns@none.org" sendername="ANS" hostname="mail.none.org" hostport="587" username="nns" password="123" subject="test"> <cacertdirpath path="cacdp1"/> <recipient address="hans-meier@none.com"/> <header name="Priority" value="Urgent" /> <header name="Importance" value="high" /> <header name="Sensitivity" value="Company-Confidential" /> <header name="Auto-Submitted" value="auto-generated" /> </smtp> </filter> </outputs> </seelog>
注意:
查看上面的第一个示例,了解如何使用SMTP writer。请记住,电子邮件noreply-notific-service@none.org不能被认为是安全可靠的。因为配置中显式的发送密码,它可能会受到黑客攻击,咱们强烈建议你不要为SMTP writer使用我的或公司的post账户。最好的作法是专门设置一个独立的邮政账户,特别是电子邮件通知服务。
第二个示例演示了使用这个编写器的最合理的方法——在(罕见的)特殊状况下使用应用程序的通知。从技术上讲,你能够设置其余过滤级别,但也要作好被诊断邮件淹没的准备。
能够设置当发生了错误的时候咱们就可以将错误信息发送给运维,这样就就可以在遇到问题时及时处理
功能:将接收到的消息写入网络链接。
元素名:conn
容许使用的属性:
举例说明:
<seelog type="sync"> <outputs> <conn net="tcp" addr=":8888" /> </outputs> </seelog>
<outputs> <conn formatid="syslog" net="tcp4" addr="server.address:5514" tls="true" insecureskipverify="true" /> </outputs> <formats> <format id="syslog" format="%CustomSyslogHeader(20) %Msg%n"/> </formats>
%CustomSyslogHeader的示例代码能够查看下面的 4)Custom formatters自定义格式器 - 2》
1)
Seelog提供了更改发送到字节接收器的消息格式的功能。格式设置在“formats”配置部分,以下:
<formats> <format id="common" format="[%LEV] %Msg"/> <format id="critical" format="%Time %Date %RelFile %Func %Msg"/> <format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/> </formats>
每一个“format”节点都有一个“id”和“format”属性。Id是用于将格式连接到dispatchers/receiver的格式的惟一标识符。“format”属性用于用特殊的格式化对象指定格式字符串,以“%”符号开头。当将消息写入字节接收器时,这些特殊符号将被上下文值或特殊字符串替换。
要查看格式是如何连接到dispatchers/receiver的,查看上面 Dispatchers and receivers分配器和接收器 的“Formatting”部分
1》Message context消息上下文
2》日期和时间
3》特殊符号
Seelog配置解析器识别一组特殊的格式标识符,称为预约义格式。引入这些标识符是为了不每次在配置文件中显式建立“xml”或“json”等公共格式。
1》使用
使用任何输出节点的“formatid”属性中的预约义格式标识符之一。
格式标识符的全名由前缀std:和下面列出的标识符之一组成。如下是完整的id-format对的列表:
<time>%Ns</time><lev>%Lev</lev><msg>%Msg</msg><path>%RelFile</path><func>%Func</func>
<t>%Ns</t><l>%l</l><m>%Msg</m><p>%RelFile</p><f>%Func</f>
<time>%Ns</time><lev>%Lev</lev><msg>%Msg</msg>
<t>%Ns</t><l>%l</l><m>%Msg</m>
{"time":%Ns,"lev":"%Lev","msg":"%Msg","path":"%RelFile","func":"%Func"}
{"t":%Ns,"l":"%Lev","m":"%Msg","p":"%RelFile","f":"%Func"}
{"time":%Ns,"lev":"%Lev","msg":"%Msg"}
{"t":%Ns,"l":"%Lev","m":"%Msg"}
[%LEVEL] %RelFile:%Func %Date %Time %Msg%n
[%LEVEL] %Date %Time %Msg%n
%Ns %l %Msg%n
注意:
使用你本身的格式覆盖预约义的格式并使用以std:开头的标识符建立你本身的格式是彻底合法的,可是不建议使用这两种作法。
举例说明:
<seelog> <outputs formatid="std:json"> <console/> </outputs> </seelog>
seelog.Info("Hello world!")输出为:
{"time":1341218159882230900,"lev":"Inf","msg":"Hello world!"}
将以json的格式输出信息到标准输出中
若是你以为上面2)formatters列表 中的标准格式化集还不够,你能够指定本身的自定义格式化程序。
要作到这一点,你必须使用seelog。使用RegisterCustomFormatter函数去为你要使用的新格式别名注册工厂。在此以后(若是返回的错误为nil),你能够在随后建立的任何日志记录器中使用指定的格式别名。
自定义格式化器能够参数化。参数字符串(括号内)若是存在,则传递给你注册的工厂func。
让咱们看几个例子。
注册你的新格式器:
var myLevelToString = map[log.LogLevel]string{ //这是我自定义的格式,重命名对应的日志级别 log.TraceLvl: "MyTrace", log.DebugLvl: "MyDebug", log.InfoLvl: "MyInfo", log.WarnLvl: "MyWarn", log.ErrorLvl: "MyError", log.CriticalLvl: "MyCritical", log.Off: "MyOff", } func createMyLevelFormatter(params string) log.FormatterFunc { return func(message string, level log.LogLevel, context log.LogContextInterface) interface{} { levelStr, ok := myLevelToString[level] if !ok { return "Broken level!" } return levelStr } } func init() { err := log.RegisterCustomFormatter("MyLevel", createMyLevelFormatter) //注册 if err != nil { ... } }
如今你能够在你的配置中使用注册的“MyLevel”:
<seelog type="sync"> <outputs formatid="main"> <console/> </outputs> <formats> <format id="main" format="%MyLevel %Msg%n"/> </formats> </seelog>
使用这个配置后一个日志记录器将建立:
log.Trace("Test message!")
输出为:
MyTrace Test message!
注册你的格式器:
var hostName string var appName = "test" var pid int var levelToSyslogSeverity = map[log.LogLevel]int{ // Mapping to RFC 5424 where possible log.TraceLvl: 7, log.DebugLvl: 7, log.InfoLvl: 6, log.WarnLvl: 4, log.ErrorLvl: 3, log.CriticalLvl: 2, log.Off: 7, } func createSyslogHeaderFormatter(params string) log.FormatterFunc { facility := 20 i, err := strconv.Atoi(params) if err == nil && i >= 0 && i <= 23 { facility = i } return func(message string, level log.LogLevel, context log.LogContextInterface) interface{} { return fmt.Sprintf("<%d>1 %s %s %s %d - -", facility*8+levelToSyslogSeverity[level], time.Now().Format("2006-01-02T15:04:05Z07:00"), hostName, appName, pid) } } func init() { hostName, _ = os.Hostname() pid = os.Getpid() err := log.RegisterCustomFormatter("CustomSyslogHeader", createSyslogHeaderFormatter) if err != nil { ... } }
如今你能够在你的配置中使用注册的“CustomSyslogHeader”:
<outputs> <conn formatid="syslog" net="tcp4" addr="server.address:5514" tls="true" insecureskipverify="true" /> </outputs> <formats> <format id="syslog" format="%CustomSyslogHeader(20) %Msg%n"/> </formats>
使用这个配置后一个日志记录器将被建立:
log.Info("Test message!")
输出为:
<167>1 2014-05-14T21:39:00+04:00 imp-pcl test 7624 - - Test message!
示例:
package main import ( log "github.com/cihub/seelog" "fmt" ) func main() { defer log.Flush() defaultFormat() stdFormat() dateTimeFormat() dateTimeCustomFormat() logLevelTypesFormat() fileTypesFormat() funcFormat() xmlFormat() } //测试的是默认的格式 func defaultFormat() { fmt.Println("Default format") testConfig := ` <seelog type="sync" />` logger, err := log.LoggerFromConfigAsBytes([]byte(testConfig)) if err != nil { fmt.Println(err) } log.ReplaceLogger(logger) log.Trace("Test message!") } //标准格式 func stdFormat() { fmt.Println("Standard fast format") testConfig := ` <seelog type="sync"> <outputs formatid="main"> <console/> </outputs> <formats> <format id="main" format="%Ns [%Level] %Msg%n"/> </formats> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("Test message!") } //日期和时间的格式默认 func dateTimeFormat() { fmt.Println("Date time format") testConfig := ` <seelog type="sync"> <outputs formatid="main"> <console/> </outputs> <formats> <format id="main" format="%Date/%Time [%LEV] %Msg%n"/> </formats> </seelog>` logger, err := log.LoggerFromConfigAsBytes([]byte(testConfig)) if err != nil { fmt.Println(err) } loggerErr := log.ReplaceLogger(logger) if loggerErr != nil { fmt.Println(loggerErr) } log.Trace("Test message!") } //自定义的日期和时间格式 func dateTimeCustomFormat() { fmt.Println("Date time custom format") testConfig := ` <seelog type="sync"> <outputs formatid="main"> <console/> </outputs> <formats> <format id="main" format="%Date(2006 Jan 02/3:04:05.000000000 PM MST) [%Level] %Msg%n"/> </formats> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("Test message!") } //日志级别类型格式 func logLevelTypesFormat() { fmt.Println("Log level types format") testConfig := ` <seelog type="sync"> <outputs formatid="main"> <console/> </outputs> <formats> <format id="main" format="%Level %Lev %LEVEL %LEV %l %Msg%n"/> </formats> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("Test message!") } //文件类型格式 func fileTypesFormat() { fmt.Println("File types format") testConfig := ` <seelog type="sync"> <outputs formatid="main"> <console/> </outputs> <formats> <format id="main" format="%File %FullPath %RelFile %Msg%n"/> </formats> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("Test message!") } //函数格式 func funcFormat() { fmt.Println("Func format") testConfig := ` <seelog type="sync"> <outputs formatid="main"> <console/> </outputs> <formats> <format id="main" format="%Func %Msg%n"/> </formats> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("Test message!") } //xml格式 func xmlFormat() { fmt.Println("Xml format") //等价于<time>%Ns</time><lev>%Lev</lev><msg>%Msg</msg><path>%RelFile</path><func>%Func</func> testConfig := ` <seelog type="sync"> <outputs formatid="main"> <console/> </outputs> <formats> <format id="main" format="` + `<log>` + `<time>%Ns</time>` + `<lev>%l</lev>` + `<msg>%Msg</msg>` + `</log>"/> </formats> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) log.Trace("Test message!") }
返回:
userdeMBP:go-learning user$ go run test.go Default format 1551702181896235000 [Trace] Test message! Standard fast format 1551702181896299000 [Trace] Test message! Date time format 2019-03-04/20:23:01 [TRC] Test message! Date time custom format 2019 Mar 04/8:23:01.896487000 PM CST [Trace] Test message! Log level types format Trace Trc TRACE TRC t Test message! File types format test.go /Users/user/go-learning/test.go test.go Test message! Func format main.funcFormat Test message! Xml format <log><time>1551702181896732000</time><lev>t</lev><msg>Test message!</msg></log>
有时你可能想要对终端输出进行着色。这一般使用CSI n [;k] m序列来完成。
Seelog格式使用XML设置,所以没法在格式标识符中使用这些序列。可是有一种特殊格式的动词叫作“EscM”,它以“n[;k]”部分做为参数。你可使用它来为终端输出执行图形化选项定制。
举例说明:
<seelog> <outputs> <console formatid="colored"/> </outputs> <formats> <format id="colored" format="%EscM(46)%Level%EscM(49) %Msg%n%EscM(0)"/> </formats> </seelog>
使用上面的配置,而后记录任何消息,日志级别将有青色背景。46设置背景,49将其重置。
建议
在消息末尾使用%EscM(0)(以及%n)重置全部图形更改
对于那些但愿在已经经过标准pkg使用日志功能的应用程序中开始使用seelog的人来讲,从日志包迁移到seelog是一项常见的任务。
1)不兼容性
Seelog概念与标准日志概念不一样,所以它们不兼容。
例1 : log.Printf。在seelog中没有相似的函数,由于Printf不携带任何日志级别的信息,因此若是要迁移,不清楚应该怎么替换log.Printf。多是seelog.Infof或seelog.Debugf 或其余。
例2 : log.Panicf。它根据格式和panic建立消息,使用格式化的消息做为panic文本。在seelog中,对任何日志函数的一次调用均可能产生具备不一样格式(取决于配置)的多个消息,所以不清楚应该使用什么做为panic文本。
例3 : log.SetPrefix。Seelog没有相似的函数,由于对任何日志函数(例如Debug)的一次调用均可能产生具备不一样格式的多个消息,所以使用一个这样的全局函数是没有意义的。此外,seelog的核心原则之一是避免经过代码进行配置。全部配置都是经过配置文件进行的。这样的函数会打破seelog的概念。
2)结论
目前(Go1.X), seelog不兼容标准日志包,不能有任何“标准日志pkg兼容性”的功能。
3)迁移注意点
尽管实际上你不能用seelog替换日志,而且具备相同的行为(如前所述,它们只是不兼容),可是你可能但愿自动化从日志到seelog的迁移。咱们建议为你的项目建立一个替代脚本
迁移脚本示例:
下面是一个Python脚本,它在多个文件中对多个日志包函数执行一些替换。它能够做为存根来知足你本身的替换需求。
import os, sys,shutil,re changeDir= './test' openFlags = 'rb' writeFlags = 'r+b' encoding = 'utf-8' backupPostfix = '.backup' goFilesRx = r'.+\.go$' patterns = [ (re.compile(ur'''(?P<before>import[\s\S]*?)"log"''', re.U | re.M), ur'''\g<before>log "github.com/cihub/seelog"'''), # change import (re.compile(ur'''log.Print(?P<after>.*?)''', re.U), ur'''log.Info\g<after>'''), # Print -> Info (re.compile(ur'''log.Println(?P<after>.*?)''', re.U), ur'''log.Info\g<after>'''), # Println -> Info (re.compile(ur'''log.Printf(?P<after>.*?)''', re.U), ur'''log.Infof\g<after>'''), # Printf -> Infof (re.compile(ur'''(?P<ws>[\t ]*)log.Panic\((?P<values>.*?)\)''', re.U), ur'''\g<ws>log.Info(\g<values>)\n\g<ws>panic(fmt.Sprint(\g<values>))'''), # Panic -> Info + panic(...) (re.compile(ur'''(?P<ws>[\t ]*)log.Panicf\((?P<values>.*?)\)''', re.U), ur'''\g<ws>log.Infof(\g<values>)\n\g<ws>panic(fmt.Sprint(\g<values>))'''), # Panicf -> Info + panic(...) # ... and so on ] def rewriteFile(fl, text): fl.read() # To preserve file creation date fl.seek(0) fl.write(text.encode(encoding)) fl.truncate() def replacePatterns(filePath, backup): # Open file and get its contents input = open(filePath, openFlags) fileText = unicode(input.read(), encoding) input.close() found = False # Make replacements for all patterns for pc in patterns: origRx = pc[0] replRx = pc[1] replacedText = re.sub(origRx, replRx, fileText) if fileText != replacedText: found = True fileText = replacedText # If any replacements were made, write the changed file if found: if backup: bckName = filePath + backupPostfix shutil.copy2(filePath, bckName) outF = open(filePath,writeFlags) rewriteFile(outF, fileText) outF.close() def replaceFunc(a, dir, files): for f in files: fPath = dir + '/' + f if re.search(goFilesRx, f, re.U) and os.path.isfile(fPath): replacePatterns(fPath, True) os.path.walk(changeDir, replaceFunc, 3)
这个配置没有任何实际价值,它只是在一个地方演示了大多数功能。有关详细信息,请查看wiki,它包含每一个seelog特性的描述。要快速参考,请查看参考reference部分。
<seelog type="asynctimer" asyncinterval="5000000" minlevel="debug" maxlevel="error"> <exceptions> <exception funcpattern="*main.test*Something*" minlevel="info"/> <exception filepattern="*main.go" minlevel="error"/> </exceptions> <outputs formatid="main"> //指明其将使用在<formats>中的<format id="main" 处指明的格式来输出信息,若是里面的子元素中有定义本身的formatid,将会将其覆盖 <console/> //指明将outputs中的日志内容在输入到文件、内存、SMTP等的同时还输出到标准输出,即终端中 <splitter formatid="format1"> //指明将输出以<format id="format1"格式写到log.log和log2.log文件中 <file path="log.log"/> <file path="log2.log"/> </splitter> <splitter formatid="format2"> //splitter用于细分<outputs>日志格式 <file path="log3.log"/> <file path="log4.log"/> </splitter> <rollingfile formatid="someformat" type="size" filename="./log/roll.log" maxsize="100" maxrolls="5" /> //指明将输出以<format id="someformat"格式写到./log/roll.log 回滚文件中 <buffered formatid="testlevels" size="10000" flushperiod="1000"> //指明将输出以<format id="testlevels"格式写到缓冲区(即内存)中,而后再存储到./log/bufFileFlush.log文件中 <file path="./log/bufFileFlush.log"/> </buffered> <filter levels="error"> //只显示error日志级别 <file path="./log/error.log"/> //写到该文件中 <smtp senderaddress="noreply-notification-service@none.org" //并将日志内容发送给下面<recipient指定的接收者 sendername="Automatic notification service" hostname="mail.none.org" hostport="587" username="nns" password="123"> <recipient address="john-smith@none.com"/> <recipient address="hans-meier@none.com"/> </smtp> <conn net="tcp4" addr="server.address:5514" tls="true" insecureskipverify="true" /> //同时也将这个日志内容经过网络传输到地址server.address:5514上 </filter> </outputs> <formats> //这里就是指定上面对应的日志内容输出应该分别使用什么样的格式 <format id="main" format="%Date(2006 Jan 02/3:04:05.000000000 PM MST) [%Level] %Msg%n"/> <format id="someformat" format="%Ns [%Level] %Msg%n"/> <format id="testlevels" format="%Level %Lev %LEVEL %LEV %l %Msg%n"/> <format id="usetags" format="<msg>%Msg</time>"/> <format id="format1" format="%Date/%Time [%LEV] %Msg%n"/> <format id="format2" format="%File %FullPath %RelFile %Msg%n"/> </formats> </seelog>
日志记录器类型设置在顶部的“seelog”元素中。属性名是“type”。
功能:在调用log func的相同goroutine中处理日志消息
类型属性值:Sync
额外属性:None
举例说明:
<seelog type="sync"> ... </seelog>
功能:在单独的goroutine中处理日志消息。从“for”循环中的消息队列获取消息。
类型属性值:asyncloop(这是默认类型,因此你能够忽略“type”属性)
额外属性:none
举例说明,下面的两种写法是等价的:
<seelog type="asyncloop"> ... </seelog>
等价于:
<seelog>
...
</seelog>
功能:在单独的goroutine中处理日志消息。获取具备指定时间间隔的消息队列中的消息。
类型属性值:asynctimer
额外属性:
举例说明:
<seelog type="asynctimer" asyncinterval="5000"> ... </seelog>
package main import ( "fmt" log "github.com/cihub/seelog" "strings" "time" ) var longMessage = strings.Repeat("A", 1024*100) func main() { defer log.Flush() syncLogger() fmt.Println() asyncLoopLogger() fmt.Println() asyncTimerLogger() } func syncLogger() { fmt.Println("Sync test") testConfig := ` <seelog type="sync"> <outputs> <filter levels="trace"> <file path="log.log"/> </filter> <filter levels="debug"> <console /> </filter> </outputs> </seelog> ` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.UseLogger(logger) doTest() } func asyncLoopLogger() { fmt.Println("Async loop test") testConfig := ` <seelog> <outputs> <filter levels="trace"> <file path="log.log"/> </filter> <filter levels="debug"> <console /> </filter> </outputs> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.UseLogger(logger) doTest() time.Sleep(1e9) } func asyncTimerLogger() { fmt.Println("Async timer test") testConfig := ` <seelog type="asynctimer" asyncinterval="5000000"> <outputs> <filter levels="trace"> <file path="log.log"/> </filter> <filter levels="debug"> <console /> </filter> </outputs> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.UseLogger(logger) doTest() time.Sleep(1e9) } func doTest() { start := time.Now() for i := 0; i < 50; i += 2 { fmt.Printf("%d\n", i) log.Trace(longMessage) log.Debugf("%d", i+1) } end := time.Now() dur := end.Sub(start) fmt.Printf("Test took %d ns\n", dur) }
返回
userdeMBP:go-learning user$ go run test.go Sync test //同步进行 0 1551702598965060000 [Debug] 1 2 1551702598965356000 [Debug] 3 4 1551702598965630000 [Debug] 5 6 1551702598965883000 [Debug] 7 8 1551702598966119000 [Debug] 9 10 1551702598966379000 [Debug] 11 12 1551702598966609000 [Debug] 13 14 1551702598966909000 [Debug] 15 16 1551702598967173000 [Debug] 17 18 1551702598967419000 [Debug] 19 20 1551702598968144000 [Debug] 21 22 1551702598968266000 [Debug] 23 24 1551702598968362000 [Debug] 25 26 1551702598968473000 [Debug] 27 28 1551702598968555000 [Debug] 29 30 1551702598968641000 [Debug] 31 32 1551702598968725000 [Debug] 33 34 1551702598968810000 [Debug] 35 36 1551702598968901000 [Debug] 37 38 1551702598968985000 [Debug] 39 40 1551702598969240000 [Debug] 41 42 1551702598969625000 [Debug] 43 44 1551702598969757000 [Debug] 45 46 1551702598969858000 [Debug] 47 48 1551702598969947000 [Debug] 49 Test took 5368269 ns Async loop test //循环同时获取日志消息 0 2 4 1551702598970071000 [Debug] 1 1551702598970077000 [Debug] 3 6 8 10 1551702598970084000 [Debug] 5 1551702598970504000 [Debug] 7 1551702598970511000 [Debug] 9 12 14 16 18 20 1551702598970742000 [Debug] 11 1551702598970755000 [Debug] 13 1551702598970761000 [Debug] 15 1551702598970766000 [Debug] 17 1551702598970772000 [Debug] 19 22 24 1551702598972970000 [Debug] 21 1551702598973067000 [Debug] 23 26 28 1551702598973221000 [Debug] 25 1551702598973232000 [Debug] 27 30 1551702598973481000 [Debug] 29 32 1551702598973598000 [Debug] 31 34 1551702598973706000 [Debug] 33 36 38 1551702598973967000 [Debug] 35 1551702598973974000 [Debug] 37 40 1551702598974162000 [Debug] 39 42 44 1551702598974171000 [Debug] 41 1551702598974376000 [Debug] 43 46 48 1551702598974470000 [Debug] 45 1551702598974476000 [Debug] 47 Test took 4692732 ns 1551702598974482000 [Debug] 49 Async timer test 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 Test took 989547 ns //async Timer设置是5000000ns后才开始读取日志消息 1551702599976687000 [Debug] 1 1551702599976713000 [Debug] 3 1551702599976736000 [Debug] 5 1551702599977358000 [Debug] 7 1551702599977372000 [Debug] 9 1551702599977386000 [Debug] 11 1551702599977404000 [Debug] 13 1551702599977418000 [Debug] 15 1551702599977431000 [Debug] 17 1551702599977444000 [Debug] 19 1551702599977458000 [Debug] 21 1551702599977471000 [Debug] 23 1551702599977484000 [Debug] 25 1551702599977497000 [Debug] 27 1551702599977510000 [Debug] 29 1551702599977523000 [Debug] 31 1551702599977537000 [Debug] 33 1551702599977550000 [Debug] 35 1551702599977563000 [Debug] 37 1551702599977576000 [Debug] 39 1551702599977589000 [Debug] 41 1551702599977602000 [Debug] 43 1551702599977615000 [Debug] 45 1551702599977628000 [Debug] 47 1551702599977641000 [Debug] 49
功能:相似于异步计时器日志记录器,但其间隔取决于队列中剩下的消息数量。建立这种类型的日志记录器是为了不日志消息队列溢出:队列中的消息越多,获取它们的速度就越快。在seelogg -example:adaptive_main.go中进行了演示:
package main import ( "time" log "github.com/cihub/seelog" ) func main() { defer log.Flush() loadAdaptiveConfig() testMsgIntensity(1) testMsgIntensity(5) testMsgIntensity(10) } func testMsgIntensity(intensity int) { log.Default.Infof("Intensity test: %d", intensity) for j := 0; j < 4; j++ { for i := 0; i < intensity; i++ { log.Tracef("trace %d", i) <-time.After(time.Second / time.Duration(intensity)) //time.Second / time.Duration(5)表示1秒除以5的时间进行一次循环 } } log.Default.Info("Messages sent") <-time.After(time.Second * time.Duration(intensity)) } func loadAdaptiveConfig() { testConfig := `<seelog type="adaptive" mininterval="200000000" maxinterval="1000000000" critmsgcount="5"> <outputs formatid="msg"> <console/> </outputs> <formats> <format id="msg" format="%Time: %Msg%n"/> </formats> </seelog>` logger, _ := log.LoggerFromConfigAsBytes([]byte(testConfig)) log.ReplaceLogger(logger) }
返回:
userdeMBP:go-learning user$ go run test.go 1551693376581324000 [Info] Intensity test: 1 17:56:16: trace 0 17:56:17: trace 0 17:56:18: trace 0 17:56:19: trace 0 1551693380594712000 [Info] Messages sent 1551693381595019000 [Info] Intensity test: 5 17:56:21: trace 0 17:56:21: trace 1 17:56:21: trace 2 17:56:22: trace 3 17:56:22: trace 4 17:56:22: trace 0 17:56:22: trace 1 17:56:23: trace 2 17:56:23: trace 3 17:56:23: trace 4 17:56:23: trace 0 17:56:23: trace 1 17:56:24: trace 2 17:56:24: trace 3 1551693385632063000 [Info] Messages sent 17:56:24: trace 4 17:56:24: trace 0 17:56:24: trace 1 17:56:25: trace 2 17:56:25: trace 3 17:56:25: trace 4 17:56:30: trace 0 1551693390634801000 [Info] Intensity test: 10 17:56:30: trace 1 17:56:30: trace 2 17:56:30: trace 3 17:56:31: trace 4 17:56:31: trace 5 17:56:31: trace 6 17:56:31: trace 7 17:56:31: trace 8 17:56:31: trace 9 17:56:31: trace 0 17:56:31: trace 1 17:56:31: trace 2 17:56:31: trace 3 17:56:32: trace 4 17:56:32: trace 5 1551693394732474000 [Info] Messages sent 17:56:32: trace 6 17:56:32: trace 7 17:56:32: trace 8 17:56:32: trace 9 17:56:32: trace 0 17:56:32: trace 1 17:56:32: trace 2 17:56:33: trace 3 17:56:33: trace 4 17:56:33: trace 5 17:56:33: trace 6 17:56:33: trace 7 17:56:33: trace 8 17:56:33: trace 9 17:56:33: trace 0 17:56:33: trace 1 17:56:33: trace 2 17:56:34: trace 3 17:56:34: trace 4 17:56:34: trace 5 17:56:34: trace 6 17:56:34: trace 7 17:56:34: trace 8 17:56:34: trace 9
若是咱们使用下面的概念:
计算间隔的公式为:
I = m + (C - Min(c, C)) / C * (M - m)
类型属性值:adaptive
额外属性:
举例说明:
<seelog type="adaptive" mininterval="2000000" maxinterval="1000000000" critmsgcount="500"> <outputs formatid="msg"> <console/> </outputs> <formats> <format id="msg" format="%Time: %Msg%n"/> </formats> </seelog>
11.简单举例:
1)仅将日志输出到终端
配置文件seelog.xml为,参考https://blog.csdn.net/luckytanggu/article/details/80345134:
<seelog type="asynctimer" asyncinterval="1000000" minlevel="debug" maxlevel="error"> <outputs formatid="main"> <!-- 仅实现将日志内容输出到终端 --> <console/> </outputs> <formats> <!-- 设置格式,输出UTC日期 UTC时间 - 缩写版大写日志级别 - 相对于应用程序运行目录的调用者路径 - 日志记录器被调用时的行号 - 消息文本(最后换行) --> <format id="main" format="%UTCDate %UTCTime - [%LEV] - %RelFile - l%Line - %Msg%n"/> </formats> </seelog>
而后应用为:
package main import ( log "github.com/cihub/seelog" "fmt" ) func main() { logger, err := log.LoggerFromConfigAsFile("seelog.xml") if err != nil { fmt.Println("parse seelog.xml error") } log.ReplaceLogger(logger) defer log.Flush() log.Info("Hello from Seelog!") }
输出为:
userdeMBP:go-learning user$ go run test.go 2019-03-04 09:19:11 - [INF] - test.go - l20 - Hello from Seelog!
2)将日志输出到终端和文件中
<seelog type="asynctimer" asyncinterval="1000000" minlevel="debug" maxlevel="error"> <outputs formatid="main"> <!-- 仅实现将日志内容输出到终端 --> <console/> <!-- 该文件将使用"format1"格式来覆盖"main"格式,并将文件信息写到log.log文件中--> <splitter formatid="format1"> <file path="log.log"/> </splitter> </outputs> <formats> <!-- 设置格式,输出UTC日期 UTC时间 - 缩写版大写日志级别 - 相对于应用程序运行目录的调用者路径 - 日志记录器被调用时的行号 - 消息文本(最后换行) --> <format id="main" format="%UTCDate %UTCTime - [%LEV] - %RelFile - l%Line - %Msg%n"/> <!-- 格式为以2006 Jan 02/3:04:05.000000000 PM MST格式输出日期 正常的日志级别 消息文本(最后换行)--> <format id="format1" format="%Date(2006 Jan 02/3:04:05.000000000 PM MST) [%Level] %Msg%n"/> </formats> </seelog>
而后返回:
userdeMBP:go-learning user$ go run test.go 2019-03-04 09:28:35 - [INF] - test.go - l20 - Hello from Seelog!
日志文件中的输出为:
userdeMBP:go-learning user$ cat log.log 2019 Mar 04/5:28:35.215823000 PM CST [Info] Hello from Seelog!