Files
build-web-application-with-…/zh-tw/12.1.md
2019-02-26 01:40:54 +08:00

253 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 12.1 應用日誌
我們期望開發的Web應用程式能夠把整個程式執行過程中出現的各種事件一一記錄下來Go語言中提供了一個簡易的log套件我們使用該套件可以方便的實現日誌記錄的功能這些日誌都是基於fmt套件的列印再結合panic之類別的函式來進行一般的列印、丟擲錯誤處理。Go目前標準套件只是包含了簡單的功能如果我們想把我們的應用日誌儲存到檔案然後又能夠結合日誌實現很多複雜的功能編寫過Java或者C++的讀者應該都使用過log4j和log4cpp之類別的日誌工具可以使用第三方開發的日誌系統:[logrus](https://github.com/sirupsen/logrus)和[seelog](https://github.com/cihub/seelog),它們實現了很強大的日誌功能,可以結合自己專案選擇。接下來我們介紹如何透過該日誌系統來實現我們應用的日誌功能。
## logrus介紹
logrus是用Go語言實現的一個日誌系統與標準函式庫log完全相容並且核心API很穩定,是Go語言目前最活躍的日誌函式庫
首先安裝logrus
```Go
go get -u github.com/sirupsen/logrus
```
簡單例子:
```Go
package main
import (
log "github.com/Sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
```
### 基於logrus的自訂日誌處理
```Go
package main
import (
"os"
log "github.com/Sirupsen/logrus"
)
func init() {
// 日誌格式化為JSON而不是預設的ASCII
log.SetFormatter(&log.JSONFormatter{})
// 輸出stdout而不是預設的stderr也可以是一個檔案
log.SetOutput(os.Stdout)
// 只記錄嚴重或以上警告
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!")
// 透過日誌語句重用欄位
// 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介紹
seelog是用Go語言實現的一個日誌系統它提供了一些簡單的函式來實現複雜的日誌分配、過濾和格式化。主要有如下特性
- XML的動態配置可以不用重新編譯程式而動態的載入配置資訊
- 支援熱更新,能夠動態改變配置而不需要重啟應用
- 支援多輸出流,能夠同時把日誌輸出到多種流中、例如檔案流、網路流等
- 支援不同的日誌輸出
- 命令列輸出
- 檔案輸出
- 快取輸出
- 支援log rotate
- SMTP郵件
上面只列舉了部分特性seelog是一個特別強大的日誌處理系統詳細的內容請參看官方wiki。接下來我將簡要介紹一下如何在專案中使用它
首先安裝seelog
```Go
go get -u github.com/cihub/seelog
```
然後我們來看一個簡單的例子:
```Go
package main
import log "github.com/cihub/seelog"
func main() {
defer log.Flush()
log.Info("Hello from Seelog!")
}
```
編譯後執行如果出現了`Hello from seelog`說明seelog日誌系統已經成功安裝並且可以正常運行了。
### 基於seelog的自訂日誌處理
seelog支援自訂日誌處理下面是我基於它自訂的日誌處理套件的部分內容
```Go
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
}
```
上面主要實現了三個函式,
- `DisableLog`
初始化全域性變數Logger為seelog的禁用狀態主要為了防止Logger被多次初始化
- `loadAppConfig`
根據配置檔案初始化seelog的配置資訊這裡我們把配置檔案透過字串讀取設定好了當然也可以透過讀取XML檔案。裡面的配置說明如下
- seelog
minlevel引數可選如果被配置,高於或等於此級別的日誌會被記錄同理maxlevel。
- outputs
輸出資訊的目的地這裡分成了兩份資料一份記錄到log rotate檔案裡面。另一份設定了filter如果這個錯誤級別是critical那麼將傳送報警郵件。
- formats
定義了各種日誌的格式
- `UseLogger`
設定當前的日誌器為相應的日誌處理
上面我們定義了一個自訂的日誌處理套件,下面就是使用示例:
```Go
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)
}
```
## 發生錯誤傳送郵件
上面的例子解釋瞭如何設定傳送郵件我們透過如下的smtp配置用來發送郵件
```html
<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>
```
郵件的格式透過criticalemail配置然後透過其他的配置傳送郵件伺服器的配置透過recipient配置接收郵件的使用者如果有多個使用者可以再新增一行。
要測試這個程式碼是否正常工作,可以在程式碼中增加類似下面的一個假訊息。不過記住過後要把它刪除,否則上線之後就會收到很多垃圾郵件。
```Go
logs.Logger.Critical("test Critical message")
```
現在只要我們的應用在線上記錄一個Critical的資訊你的郵箱就會收到一個Email這樣一旦線上的系統出現問題你就能立馬透過郵件獲知就能及時的進行處理。
## 使用應用日誌
對於應用日誌,每個人的應用場景可能會各不相同,有些人利用應用日誌來做資料分析,有些人利用應用日誌來做效能分析,有些人來做使用者行為分析,還有些就是純粹的記錄,以方便應用出現問題的時候輔助查詢問題。
舉一個例子,我們需要追蹤使用者嘗試登陸系統的操作。這裡會把成功與不成功的嘗試都記錄下來。記錄成功的使用"Info"日誌級別,而不成功的使用"warn"級別。如果想查詢所有不成功的登陸我們可以利用linux的grep之類別的命令工具如下
```Go
# 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
```
透過這種方式我們就可以很方便的查詢相應的資訊這樣有利於我們針對應用日誌做一些統計和分析。另外我們還需要考慮日誌的大小對於一個高流量的Web應用來說日誌的增長是相當可怕的所以我們在seelog的配置檔案裡面設定了logrotate這樣就能保證日誌檔案不會因為不斷變大而導致我們的磁碟空間不夠引起問題。
## 小結
透過上面對seelog系統及如何基於它進行自訂日誌系統的學習現在我們可以很輕鬆的隨需建構一個合適的功能強大的日誌處理系統了。日誌處理系統為資料分析提供了可靠的資料來源比如透過對日誌的分析我們可以進一步優化系統或者應用出現問題時方便查詢定位問題另外seelog也提供了日誌分級功能透過對minlevel的配置我們可以很方便的設定測試或釋出版本的輸出訊息級別。
## links
* [目錄](<preface.md>)
* 上一章: [部署與維護](<12.0.md>)
* 下一節: [網站錯誤處理](<12.2.md>)