基础日志

Gokit有自身的基础日志模块,配置如下:

1
2
3
4
5
6
	var logger log.Logger
	{
		logger = log.NewLogfmtLogger(os.Stderr) //错误输出到控制台
		logger = log.With(logger, "ts", log.DefaultTimestampUTC)
		logger = log.With(logger, "caller", log.DefaultCaller)
	}

如果我们想要加入通用日志,比如时间,调用堆栈所在行数,可以使用logger = log.With(logger, "caller", log.DefaultCaller)加入.

业务日志(service层)

本次实现基于service层的中间件 ServiceMiddleware

创建Middleware

打开service.go文件,加入如下代码:

1
2
// ServiceMiddleware define service middleware
type ServiceMiddleware func(Service) Service

创建日志中间件

新建文件loggings.go,新建类型loggingMiddleware,该类型中嵌入了Service,还包含一个logger属性,代码如下所示:

1
2
3
4
5
6
// loggingMiddleware Make a new type
// that contains Service interface and logger instance
type loggingMiddleware struct {
	Service
	logger log.Logger
}

创建一个方法LoggingMiddleware把日志记录对象嵌入中间件。该方法接受日志对象,返回ServiceMiddleware,而ServiceMiddleware可以传入Service对象,这样就可以对Service增加一层装饰。代码如下:

1
2
3
4
5
6
// LoggingMiddleware make logging middleware
func LoggingMiddleware(logger log.Logger) ServiceMiddleware {
	return func(next Service) Service {
		return loggingMiddleware{next, logger}
	}
}

接下来就可以让新的类型loggingMiddleware实现Service的接口方法了。实现方法时可以在其中使用日志对象记录调用方法、调用时间、传入参数、输出结果、调用耗时等信息。

完整logging.go代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package main

import (
	"github.com/go-kit/kit/log"
	"time"
)

// loggingMiddleware Make a new type
// that contains Service interface and logger instance
type loggingMiddleware struct {
	Service
	logger log.Logger
}

// LoggingMiddleware make logging middleware
func LoggingMiddleware(logger log.Logger) ServiceMiddleware {
	return func(next Service) Service {
		return loggingMiddleware{next, logger}
	}
}

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
}

func (mw loggingMiddleware) Subtract(a, b int) (ret int) {

	defer func(beign time.Time) {
		mw.logger.Log(
			"function", "Subtract",
			"a", a,
			"b", b,
			"result", ret,
			"took", time.Since(beign),
		)
	}(time.Now())

	ret = mw.Service.Subtract(a, b)
	return ret
}

func (mw loggingMiddleware) Multiply(a, b int) (ret int) {

	defer func(beign time.Time) {
		mw.logger.Log(
			"function", "Multiply",
			"a", a,
			"b", b,
			"result", ret,
			"took", time.Since(beign),
		)
	}(time.Now())

	ret = mw.Service.Multiply(a, b)
	return ret
}

func (mw loggingMiddleware) Divide(a, b int) (ret int, err error) {

	defer func(beign time.Time) {
		mw.logger.Log(
			"function", "Divide",
			"a", a,
			"b", b,
			"result", ret,
			"took", time.Since(beign),
		)
	}(time.Now())

	ret, err = mw.Service.Divide(a, b)
	return
}

修改main方法

打开main.go,调用LoggingMiddleware创建日志中间件实现对svc的包装,代码如下所示(在/********/中间的几行即为新增代码):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package main

import (
	"fmt"
	"github.com/go-kit/kit/log"
	kithttp "github.com/go-kit/kit/transport/http"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	errChan := make(chan error)
	var svc Service
	svc = ArithmeticService{}

    /********/
	var logger log.Logger
	{
		logger = log.NewLogfmtLogger(os.Stderr)
		logger = log.With(logger, "ts", log.DefaultTimestampUTC)
		logger = log.With(logger, "caller", log.DefaultCaller)
	}
	// add logging middleware
	svc = LoggingMiddleware(logger)(svc)
    /********/

	endpoint := MakeArithmeticEndpoint(svc)
	options := []kithttp.ServerOption{
		kithttp.ServerErrorHandler(transport.NewLogErrorHandler(logger)),
		kithttp.ServerErrorEncoder(kithttp.DefaultErrorEncoder),
	}

	http.Handle("/calculate", kithttp.NewServer(
		endpoint,
		decodeArithmeticRequest,
		encodeArithmeticResponse,
		options...,
	))
	go func() {
		fmt.Println("Http Server start at port:9000")
		errChan <- http.ListenAndServe(":9000", nil)
	}()

	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
		errChan <- fmt.Errorf("%s", <-c)
	}()

	fmt.Println(<-errChan)
}

业务日志(endpoint层)

编写endpoint层的middleware:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// NewTokenBucketLimitterWithBuildIn 使用x/time/rate创建限流中间件
func NewLogMiidleware(logger kitlog.Logger) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			r := request.(ArithmeticRequest)
			_ = logger.Log("type", r.RequestType, "A", r.A, "B", r.B)
			return next(ctx, request)
		}
	}
}

main函数加一句函数:

1
endpoint=NewLogMiidleware(logger)(endpoint)

参考:https://juejin.im/post/5c6eb317f265da2d864b3a81