我們期望開(kāi)發(fā)的Web應(yīng)用程序能夠把整個(gè)程序運(yùn)行過(guò)程中出現(xiàn)的各種事件一一記錄下來(lái),Go語(yǔ)言中提供了一個(gè)簡(jiǎn)易的log包,我們使用該包可以方便的實(shí)現(xiàn)日志記錄的功能,這些日志都是基于fmt包的打印再結(jié)合panic之類(lèi)的函數(shù)來(lái)進(jìn)行一般的打印、拋出錯(cuò)誤處理。Go目前標(biāo)準(zhǔn)包只是包含了簡(jiǎn)單的功能,如果我們想把我們的應(yīng)用日志保存到文件,然后又能夠結(jié)合日志實(shí)現(xiàn)很多復(fù)雜的功能(編寫(xiě)過(guò)Java或者C++的讀者應(yīng)該都使用過(guò)log4j和log4cpp之類(lèi)的日志工具),可以使用第三方開(kāi)發(fā)的日志系統(tǒng):logrus和
seelog,它們實(shí)現(xiàn)了很強(qiáng)大的日志功能,可以結(jié)合自己項(xiàng)目選擇。接下來(lái)我們介紹如何通過(guò)該日志系統(tǒng)來(lái)實(shí)現(xiàn)我們應(yīng)用的日志功能。
logrus是用Go語(yǔ)言實(shí)現(xiàn)的一個(gè)日志系統(tǒng),與標(biāo)準(zhǔn)庫(kù)log完全兼容并且核心API很穩(wěn)定,是Go語(yǔ)言目前最活躍的日志庫(kù)
首先安裝logrus
go get -u github.com/sirupsen/logrus
簡(jiǎn)單例子:
package main
import (
log "github.com/Sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
package main
import (
"os"
log "github.com/Sirupsen/logrus"
)
func init() {
// 日志格式化為JSON而不是默認(rèn)的ASCII
log.SetFormatter(&log.JSONFormatter{})
// 輸出stdout而不是默認(rèn)的stderr,也可以是一個(gè)文件
log.SetOutput(os.Stdout)
// 只記錄嚴(yán)重或以上警告
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// 通過(guò)日志語(yǔ)句重用字段
// logrus.Entry返回自WithFields()
contextLogger := log.WithFields(log.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}
seelog是用Go語(yǔ)言實(shí)現(xiàn)的一個(gè)日志系統(tǒng),它提供了一些簡(jiǎn)單的函數(shù)來(lái)實(shí)現(xiàn)復(fù)雜的日志分配、過(guò)濾和格式化。主要有如下特性:
上面只列舉了部分特性,seelog是一個(gè)特別強(qiáng)大的日志處理系統(tǒng),詳細(xì)的內(nèi)容請(qǐng)參看官方wiki。接下來(lái)我將簡(jiǎn)要介紹一下如何在項(xiàng)目中使用它:
首先安裝seelog
go get -u github.com/cihub/seelog
然后我們來(lái)看一個(gè)簡(jiǎn)單的例子:
package main
import log "github.com/cihub/seelog"
func main() {
defer log.Flush()
log.Info("Hello from Seelog!")
}
編譯后運(yùn)行如果出現(xiàn)了Hello from seelog
,說(shuō)明seelog日志系統(tǒng)已經(jīng)成功安裝并且可以正常運(yùn)行了。
seelog支持自定義日志處理,下面是我基于它自定義的日志處理包的部分內(nèi)容:
package logs
import (
// "errors"
"fmt"
// "io"
seelog "github.com/cihub/seelog"
)
var Logger seelog.LoggerInterface
func loadAppConfig() {
appConfig := `
<seelog minlevel="warn">
<outputs formatid="common">
<rollingfile type="size" filename="/data/logs/roll.log" maxsize="100000" maxrolls="5"/>
<filter levels="critical">
<file path="/data/logs/critical.log" formatid="critical"/>
<smtp formatid="criticalemail" senderaddress="astaxie@gmail.com" sendername="ShortUrl API" hostname="smtp.gmail.com" hostport="587" username="mailusername" password="mailpassword">
<recipient address="xiemengjun@gmail.com"/>
</smtp>
</filter>
</outputs>
<formats>
<format id="common" format="%Date/%Time [%LEV] %Msg%n" />
<format id="critical" format="%File %FullPath %Func %Msg%n" />
<format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/>
</formats>
</seelog>
`
logger, err := seelog.LoggerFromConfigAsBytes([]byte(appConfig))
if err != nil {
fmt.Println(err)
return
}
UseLogger(logger)
}
func init() {
DisableLog()
loadAppConfig()
}
// DisableLog disables all library log output
func DisableLog() {
Logger = seelog.Disabled
}
// UseLogger uses a specified seelog.LoggerInterface to output library log.
// Use this func if you are using Seelog logging system in your app.
func UseLogger(newLogger seelog.LoggerInterface) {
Logger = newLogger
}
上面主要實(shí)現(xiàn)了三個(gè)函數(shù)
DisableLog
?初始化全局變量Logger為seelog的禁用狀態(tài),主要為了防止Logger被多次初始化
loadAppConfig
?根據(jù)配置文件初始化seelog的配置信息,這里我們把配置文件通過(guò)字符串讀取設(shè)置好了,當(dāng)然也可以通過(guò)讀取XML文件。里面的配置說(shuō)明如下:
UseLogger
?設(shè)置當(dāng)前的日志器為相應(yīng)的日志處理
上面我們定義了一個(gè)自定義的日志處理包,下面就是使用示例:
package main
import (
"net/http"
"project/logs"
"project/configs"
"project/routes"
)
func main() {
addr, _ := configs.MainConfig.String("server", "addr")
logs.Logger.Info("Start server at:%v", addr)
err := http.ListenAndServe(addr, routes.NewMux())
logs.Logger.Critical("Server err:%v", err)
}
上面的例子解釋了如何設(shè)置發(fā)送郵件,我們通過(guò)如下的smtp配置用來(lái)發(fā)送郵件:
<smtp formatid="criticalemail" senderaddress="astaxie@gmail.com" sendername="ShortUrl API" hostname="smtp.gmail.com" hostport="587" username="mailusername" password="mailpassword">
<recipient address="xiemengjun@gmail.com"/>
</smtp>
郵件的格式通過(guò)criticalemail配置,然后通過(guò)其他的配置發(fā)送郵件服務(wù)器的配置,通過(guò)recipient配置接收郵件的用戶(hù),如果有多個(gè)用戶(hù)可以再添加一行。
要測(cè)試這個(gè)代碼是否正常工作,可以在代碼中增加類(lèi)似下面的一個(gè)假消息。不過(guò)記住過(guò)后要把它刪除,否則上線(xiàn)之后就會(huì)收到很多垃圾郵件。
logs.Logger.Critical("test Critical message")
現(xiàn)在,只要我們的應(yīng)用在線(xiàn)上記錄一個(gè)Critical的信息,你的郵箱就會(huì)收到一個(gè)Email,這樣一旦線(xiàn)上的系統(tǒng)出現(xiàn)問(wèn)題,你就能立馬通過(guò)郵件獲知,就能及時(shí)的進(jìn)行處理。
對(duì)于應(yīng)用日志,每個(gè)人的應(yīng)用場(chǎng)景可能會(huì)各不相同,有些人利用應(yīng)用日志來(lái)做數(shù)據(jù)分析,有些人利用應(yīng)用日志來(lái)做性能分析,有些人來(lái)做用戶(hù)行為分析,還有些就是純粹的記錄,以方便應(yīng)用出現(xiàn)問(wèn)題的時(shí)候輔助查找問(wèn)題。
舉一個(gè)例子,我們需要跟蹤用戶(hù)嘗試登陸系統(tǒng)的操作。這里會(huì)把成功與不成功的嘗試都記錄下來(lái)。記錄成功的使用"Info"日志級(jí)別,而不成功的使用"warn"級(jí)別。如果想查找所有不成功的登陸,我們可以利用linux的grep之類(lèi)的命令工具,如下:
# cat /data/logs/roll.log | grep "failed login"
2012-12-11 11:12:00 WARN : failed login attempt from 11.22.33.44 username password
通過(guò)這種方式我們就可以很方便的查找相應(yīng)的信息,這樣有利于我們針對(duì)應(yīng)用日志做一些統(tǒng)計(jì)和分析。另外我們還需要考慮日志的大小,對(duì)于一個(gè)高流量的Web應(yīng)用來(lái)說(shuō),日志的增長(zhǎng)是相當(dāng)可怕的,所以我們?cè)趕eelog的配置文件里面設(shè)置了logrotate,這樣就能保證日志文件不會(huì)因?yàn)椴粩嘧兇蠖鴮?dǎo)致我們的磁盤(pán)空間不夠引起問(wèn)題。
通過(guò)上面對(duì)seelog系統(tǒng)及如何基于它進(jìn)行自定義日志系統(tǒng)的學(xué)習(xí),現(xiàn)在我們可以很輕松的隨需構(gòu)建一個(gè)合適的功能強(qiáng)大的日志處理系統(tǒng)了。日志處理系統(tǒng)為數(shù)據(jù)分析提供了可靠的數(shù)據(jù)源,比如通過(guò)對(duì)日志的分析,我們可以進(jìn)一步優(yōu)化系統(tǒng),或者應(yīng)用出現(xiàn)問(wèn)題時(shí)方便查找定位問(wèn)題,另外seelog也提供了日志分級(jí)功能,通過(guò)對(duì)minlevel的配置,我們可以很方便的設(shè)置測(cè)試或發(fā)布版本的輸出消息級(jí)別。
更多建議: