目前,幾乎所有的軟體系統都具備日誌功能,通過日誌我們可以在軟體執行異常時定位軟體遇到的問題,還原應用程式異常時的執行狀態。
雖然系統上線前經過了嚴格的測試工作,但是生產環境業務的複雜性、不可預測性使得軟體工程師無法確保系統上線後不會發生故障。為了能夠在系統發生異常時對系統故障進行分析與定位,引入日誌系統成為所有軟體系統研發的必然選擇。
go-kit日誌
在上篇文章《go-kit微服務-HTTP REST》中實現了算術運算的HTTP服務,這篇文章將基於Gokit中介軟體機制為其增加日誌記錄功能。
本質上講,go-kit中介軟體採用了裝飾者模式,傳入Endpoint物件,封裝部分業務邏輯,然後返回Endpoint物件。
Step-1:建立Middleware
開啟service.go
檔案,加入如下程式碼:
// ServiceMiddleware define service middleware
type ServiceMiddleware func(Service) Service
複製程式碼
Step-2:建立日誌中介軟體
新建檔案loggings.go
,新建型別loggingMiddleware
,該型別中嵌入了Service
,還包含一個logger
屬性,程式碼如下所示:
// loggingMiddleware Make a new type
// that contains Service interface and logger instance
type loggingMiddleware struct {
Service
logger log.Logger
}
複製程式碼
下來建立一個方法LoggingMiddleware
把日誌記錄物件嵌入中介軟體。該方法接受日誌物件,返回ServiceMiddleware
,而ServiceMiddleware
可以傳入Service
物件,這樣就可以對Service
增加一層裝飾。程式碼如下:
// LoggingMiddleware make logging middleware
func LoggingMiddleware(logger log.Logger) ServiceMiddleware {
return func(next Service) Service {
return loggingMiddleware{next, logger}
}
}
複製程式碼
接下來就可以讓新的型別loggingMiddleware
實現Service
的介面方法了。實現方法時可以在其中使用日誌物件記錄呼叫方法、呼叫時間、傳入引數、輸出結果、呼叫耗時等資訊。下面以Add
方法為例進行實現,其他方法與之類似:
func (mw loggingMiddleware) Add(a, b int) (ret int) {
defer func(beign time.Time) {
mw.logger.Log(
"function", "Add",
"a", a,
"b", b,
"result", ret,
"took", time.Since(beign),
)
}(time.Now())
ret = mw.Service.Add(a, b)
return ret
}
複製程式碼
Step-3:修改main方法
開啟main.go
,呼叫LoggingMiddleware
建立日誌中介軟體實現對svc
的包裝,程式碼如下所示(帶有註釋的一行即為新增程式碼):
func main() {
ctx := context.Background()
errChan := make(chan error)
var logger log.Logger
{
logger = log.NewLogfmtLogger(os.Stderr)
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
logger = log.With(logger, "caller", log.DefaultCaller)
}
var svc Service
svc = ArithmeticService{}
// add logging middleware
svc = LoggingMiddleware(logger)(svc)
endpoint := MakeArithmeticEndpoint(svc)
r := MakeHttpHandler(ctx, endpoint, logger)
go func() {
fmt.Println("Http Server start at port:9000")
handler := r
errChan <- http.ListenAndServe(":9000", handler)
}()
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
errChan <- fmt.Errorf("%s", <-c)
}()
fmt.Println(<-errChan)
}
複製程式碼
Step-4:編譯&執行
在控制檯編譯並執行應用程式,然後通過Postman請求介面進行測試,即可看到輸出的日誌資訊:
ts=2019-02-18T05:43:57.902971Z caller=logging.go:25 function=Add a=10 b=1 result=11 took=0s
ts=2019-02-18T05:44:10.116234Z caller=logging.go:25 function=Add a=10 b=1 result=11 took=0s
ts=2019-02-18T05:44:11.2682718Z caller=logging.go:25 function=Add a=10 b=1 result=11 took=0s
複製程式碼
總結
本文藉助go-kit的中介軟體機制為微服務增加了日誌功能。由於Gokit中介軟體採用裝飾者模式,新增的日誌功能對Endpoint、Service、Transport三個層次均無程式碼入侵,實現即插即用的效果,這一機制在開發中將非常有利於團隊之間的配合。當然,本文采用的日誌記錄僅僅是通過控制檯輸出,還無法真正應用於生產環境,今後有時間繼續研究其他方式。
本文程式碼可通過github獲取。
本文首發於本人微信公眾號【兮一昂吧】,歡迎掃碼關注!