https://github.com/cihub/seelog
文档学习:https://github.com/cihub/seelog/wiki
1.安装:
go get github.com/cihub/seelog
2.快速启动
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调优。
下载配置
在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
ReplaceLogger 和 UseLogger
这两个函数都更改了负责当前日志记录器的包级别变量。此变量用于包级函数“Trace”、“Debug”等。但是,请注意区别。
前者正确地关闭前一个日志记录器(使用刷新日志数据),然后用一个新的日志记录器替换它。当你更改日志配置时,这是最推荐的方法。
后者只刷新前一个日志记录器(不关闭它),然后用一个新的日志记录器替换它。当你更改日志记录器并且对关闭旧日志记录器漠不关心时,应该使用此方法。
演示配置的所有功能
有一个演示配置,它在一个地方演示了大多数功能,可见下面的 9.Example config
你可以在深入研究所有特性之前检查它。
3.日志级别
这一节展示了我们对Seelog级别层次、它们的含义和使用范围的看法。当我们根据自己的概念对Seelog代码进行调优时,建议遵循以下规则。
支持的日志级别有:
- Trace -查找关于所有基本构造的状态的普遍信息。使用“Trace”进行深度调试,查找函数的问题部分,检查临时变量的值,等等。
- Debug——用于详细的系统行为报告和诊断消息,以帮助定位开发过程中的问题。
- Info-关于应用程序工作的一般信息。在代码中使用“Info”级别,这样即使在生产环境中也可以启用它。所以这是一个“生产日志级别”。
- Warn-用于指示以安全方式自动处理的小错误、奇怪情况和故障。
- Error-严重故障影响应用程序的工作流程,但不是致命的(不强迫应用程序关闭)。
- Critical——在应用程序死亡之前生成最后的消息。注意:Critical消息强制立即刷新,因为Critical情况下,如果应用程序崩溃,避免日志消息丢失是很重要的。
- Off—用于关闭日志记录的特殊日志级别
配置文件的日志级别标识符
- "trace"——低级别
- "debug"
- "info"
- "warn"
- "error"
- "critical"——高级别
日志消息示例
- Trace
- "Entered parse function validation block"
- "Validation: entered second 'if'"
- "Dictionary 'Dict' is empty. Using default value"
- Debug
- "Web page requested: http://somesite.com Params='...'"
- "Response generated. Response size: 10000. Sending."
- "New file received. Type:PNG Size:20000"
- Info
- "Web server restarted"
- "Hourly statistics: Requested pages: 12345 Errors: 123 ..."
- "Service paused. Waiting for 'resume' call"
- Warn
- "Cache corrupted for file='test.file'. Reading from back-end"
- "Database 192.168.0.7/DB not responding. Using backup 192.168.0.8/DB"
- "No response from statistics server. Statistics not sent"
- Error
- "Internal error. Cannot process request #12345 Error:...."
- "Cannot perform login: credentials DB not responding"
- Critical
- "Critical panic received: .... Shutting down"
- "Fatal error: ... App is shutting down to prevent data corruption or loss"
例子:
下面的示例演示了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.约束和例外
限制
约束限制了规范日志级别的规则。如果没有指定约束,则允许所有日志级别。
- Min/max约束允许包含最小值和最大值之间的级别(例如,info thru error)。min和max都不需要在场。因此,你可以允许所有日志级别高于或低于最小级别1。可使用关键字“minlevel”和“maxlevel”设置这些约束。
- 约束列表只允许在列表中指定级别。例如,你可以输入字符串“debug, info, critical”来表示logger需要这三个级别。使用关键字“levels”来设置这种约束。
有两种类型的约束:全局约束和例外约束
全局约束
全局约束用于整个应用程序,它们使用的是“定期应用的”规则(而不是“例外”)。这些约束是在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名的第一个例外。如果发现这样的例外,则其约束将覆盖常规约束。
建议
根据上面所说的,有一些简单的建议:
- 例外要适度。当我们在每次记录某样东西时都运行例外列表时,用规则填充它不是一个好主意,只有少数可以这么做。我们称它们为例外是有原因的!
- 避免生产配置中允许“trace”或“debug”级别的约束。如果这样做,你将告诉分析器“允许在某些地方进行traces/debugs”,并且所有Trace/Debug调用不会立即返回,而是强制约束检查器在每次运行时运行。然后调用者将进入例外列表等。然而,对于性能并不重要的开发或生产系统,这些限制是可以接受的。(参见下面的例子)
- 首先使用更具体的规则。这只是因为我们恰好使用满足file/func名称模式的第一个例外。因此,如果你的文件名符合例外的“filepattern”—例外子系统将立即使用此例外的约束,而不会查看随后出现的例外。所以首先使用更具体的规则,最后使用不那么具体的规则(参见下面的例子)
举例说明:
让我们从“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) } }