go-kit微服務:日誌功能

rayson發表於2019-02-21

目前,幾乎所有的軟體系統都具備日誌功能,通過日誌我們可以在軟體執行異常時定位軟體遇到的問題,還原應用程式異常時的執行狀態。

雖然系統上線前經過了嚴格的測試工作,但是生產環境業務的複雜性、不可預測性使得軟體工程師無法確保系統上線後不會發生故障。為了能夠在系統發生異常時對系統故障進行分析與定位,引入日誌系統成為所有軟體系統研發的必然選擇。

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獲取。

本文首發於本人微信公眾號【兮一昂吧】,歡迎掃碼關注!

go-kit微服務:日誌功能

相關文章