Go 應用日志

2022-05-13 16:58 更新

我們期望開發(fā)的Web應用程序能夠把整個程序運行過程中出現(xiàn)的各種事件一一記錄下來,Go語言中提供了一個簡易的log包,我們使用該包可以方便的實現(xiàn)日志記錄的功能,這些日志都是基于fmt包的打印再結合panic之類的函數(shù)來進行一般的打印、拋出錯誤處理。Go目前標準包只是包含了簡單的功能,如果我們想把我們的應用日志保存到文件,然后又能夠結合日志實現(xiàn)很多復雜的功能(編寫過Java或者C++的讀者應該都使用過log4j和log4cpp之類的日志工具),可以使用第三方開發(fā)的日志系統(tǒng):logrusseelog,它們實現(xiàn)了很強大的日志功能,可以結合自己項目選擇。接下來我們介紹如何通過該日志系統(tǒng)來實現(xiàn)我們應用的日志功能。

logrus介紹

logrus是用Go語言實現(xiàn)的一個日志系統(tǒng),與標準庫log完全兼容并且核心API很穩(wěn)定,是Go語言目前最活躍的日志庫

首先安裝logrus

go get -u github.com/sirupsen/logrus

簡單例子:

package main

import (
	log "github.com/Sirupsen/logrus"
)

func main() {
	log.WithFields(log.Fields{
		"animal": "walrus",
	}).Info("A walrus appears")
}

基于logrus的自定義日志處理

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語言實現(xiàn)的一個日志系統(tǒng),它提供了一些簡單的函數(shù)來實現(xiàn)復雜的日志分配、過濾和格式化。主要有如下特性:

  • XML的動態(tài)配置,可以不用重新編譯程序而動態(tài)的加載配置信息
  • 支持熱更新,能夠動態(tài)改變配置而不需要重啟應用
  • 支持多輸出流,能夠同時把日志輸出到多種流中、例如文件流、網絡流等
  • 支持不同的日志輸出
    • 命令行輸出
    • 文件輸出
    • 緩存輸出
    • 支持log rotate
    • SMTP郵件

上面只列舉了部分特性,seelog是一個特別強大的日志處理系統(tǒng),詳細的內容請參看官方wiki。接下來我將簡要介紹一下如何在項目中使用它:

首先安裝seelog

go get -u github.com/cihub/seelog

然后我們來看一個簡單的例子:

package main

import log "github.com/cihub/seelog"

func main() {
    defer log.Flush()
    log.Info("Hello from Seelog!")
}

編譯后運行如果出現(xiàn)了Hello from seelog,說明seelog日志系統(tǒng)已經成功安裝并且可以正常運行了。

基于seelog的自定義日志處理

seelog支持自定義日志處理,下面是我基于它自定義的日志處理包的部分內容:

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
}

上面主要實現(xiàn)了三個函數(shù)

  • ?DisableLog?
  • 初始化全局變量Logger為seelog的禁用狀態(tài),主要為了防止Logger被多次初始化

  • ?loadAppConfig?
  • 根據配置文件初始化seelog的配置信息,這里我們把配置文件通過字符串讀取設置好了,當然也可以通過讀取XML文件。里面的配置說明如下:

    • seelog
    • minlevel參數(shù)可選,如果被配置,高于或等于此級別的日志會被記錄,同理maxlevel。
    • outputs
    • 輸出信息的目的地,這里分成了兩份數(shù)據,一份記錄到log rotate文件里面。另一份設置了filter,如果這個錯誤級別是critical,那么將發(fā)送報警郵件。
    • formats
    • 定義了各種日志的格式
  • ?UseLogger?
  • 設置當前的日志器為相應的日志處理

上面我們定義了一個自定義的日志處理包,下面就是使用示例:

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)
}

發(fā)生錯誤發(fā)送郵件

上面的例子解釋了如何設置發(fā)送郵件,我們通過如下的smtp配置用來發(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>

郵件的格式通過criticalemail配置,然后通過其他的配置發(fā)送郵件服務器的配置,通過recipient配置接收郵件的用戶,如果有多個用戶可以再添加一行。

要測試這個代碼是否正常工作,可以在代碼中增加類似下面的一個假消息。不過記住過后要把它刪除,否則上線之后就會收到很多垃圾郵件。

logs.Logger.Critical("test Critical message")

現(xiàn)在,只要我們的應用在線上記錄一個Critical的信息,你的郵箱就會收到一個Email,這樣一旦線上的系統(tǒng)出現(xiàn)問題,你就能立馬通過郵件獲知,就能及時的進行處理。

使用應用日志

對于應用日志,每個人的應用場景可能會各不相同,有些人利用應用日志來做數(shù)據分析,有些人利用應用日志來做性能分析,有些人來做用戶行為分析,還有些就是純粹的記錄,以方便應用出現(xiàn)問題的時候輔助查找問題。

舉一個例子,我們需要跟蹤用戶嘗試登陸系統(tǒng)的操作。這里會把成功與不成功的嘗試都記錄下來。記錄成功的使用"Info"日志級別,而不成功的使用"warn"級別。如果想查找所有不成功的登陸,我們可以利用linux的grep之類的命令工具,如下:

# 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

通過這種方式我們就可以很方便的查找相應的信息,這樣有利于我們針對應用日志做一些統(tǒng)計和分析。另外我們還需要考慮日志的大小,對于一個高流量的Web應用來說,日志的增長是相當可怕的,所以我們在seelog的配置文件里面設置了logrotate,這樣就能保證日志文件不會因為不斷變大而導致我們的磁盤空間不夠引起問題。

小結

通過上面對seelog系統(tǒng)及如何基于它進行自定義日志系統(tǒng)的學習,現(xiàn)在我們可以很輕松的隨需構建一個合適的功能強大的日志處理系統(tǒng)了。日志處理系統(tǒng)為數(shù)據分析提供了可靠的數(shù)據源,比如通過對日志的分析,我們可以進一步優(yōu)化系統(tǒng),或者應用出現(xiàn)問題時方便查找定位問題,另外seelog也提供了日志分級功能,通過對minlevel的配置,我們可以很方便的設置測試或發(fā)布版本的輸出消息級別。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號