iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 7
0

昨天把基礎寫完了,今天把debug等4個層級所需要的function與middleware搞定

先來想想debug,我們想要知道一些變數的值,在log/logging.go補上

type V struct {
	Key   string
	Value interface{}
}

// wrap logger debug
func Debug(msg string, params ...interface{}) {
	Logger.Debug(msg,
		zap.Any("FuncData", debug.GetFuncData(1)),
		zap.Any("param", params),
	)
}

使用時呼叫log.Debug("some msg", &V{"variable_name", variable}),就能直接print資訊在console上

接著是info,照著上面的構想,只要紀錄一些訊息與工作階段就好,補上

// wrap logger info
func Info(c *gin.Context, taskStatus string, msg string) {
	LogHandle(c, NewLogData(taskStatus, &LogErrorData{appErr.ErrorDataStruct{-1, msg}}, nil))
}

// a short cup of request success info
func Success(c *gin.Context) {
	LogHandle(c, NewLogData(TaskOK, nil, nil))
}

解釋:

  • appErr.ErrorDataStruct{-1, msg},-1是定義為ok的error code
  • Success是對info的簡單包裝,紀錄一下request順利完成

最後是warn與error,這部份就要用到ErrorHandle來處理錯誤了,補上

// handle for custom private message for logger
func LogErrorHandle(c *gin.Context, taskStatus string, errorData *LogErrorData, errorMeta *LogErrorMeta, CustomMsg ...string) {
	nld := NewLogData(taskStatus, errorData, errorMeta)
	if len(CustomMsg) > 1 {
		nld.ErrorData.Msg = CustomMsg[1]
	}
	LogHandle(c, nld)
}

// wrap logger warn, warn apperr.Error() can be ""
// first custom msg should be message return to front end
// second msg should be message for logging
func Warn(c *gin.Context, code int, err error, CustomMsg ...string) {
	errorReturn := appErr.ErrorHandle(c, code, err, 1, CustomMsg...) // skip this wrap
	LogErrorHandle(c, TaskFail, &LogErrorData{*errorReturn.ErrorData}, &LogErrorMeta{*errorReturn.ErrorMeta}, CustomMsg...)
}

// wrap logger error
// first custom msg should be message return to front end
// second msg should be message for logging
// if stack is true, the call stack will start form skip(start from 0)
func Error(c *gin.Context, code int, err error, skip int, CustomMsg ...string) {
	errorReturn := appErr.ErrorHandle(c, code, err, skip+1, CustomMsg...) // skip this wrap
	LogErrorHandle(c, TaskError, &LogErrorData{*errorReturn.ErrorData}, &LogErrorMeta{*errorReturn.ErrorMeta}, CustomMsg...)
}

解釋:

  • CustomMsg與ErrorHandle的CustomMsg相同,第一個放給client的訊息,第二個放log的訊息,如果沒給就使用error code對應的訊息
  • 之後如果有if err != nil,就能直接call log.Warn(c, code, err, "client msg", "local msg")或 log.Error來處理錯誤了

現在來寫middleware來處理這些log,在middleware下創建log.go,寫入

package middleware

import (
	"app/logger"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"time"
)

const (
	timeFormat = time.RFC3339
	utc        = true
)

// Requests with errors are logged using zap.Error().
// Requests with A known error are logged using zap.Warn().
// Requests without errors are logged using zap.Info().
func Logging() gin.HandlerFunc {
	return func(c *gin.Context) {
		// some evil middlewares modify this values
		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery

		start := time.Now()
		c.Next()

		end := time.Now()
		latency := end.Sub(start)
		if utc {
			end = end.UTC()
		}

		// get logger msg
		var (
			LogMsg *log.LogDataStruct
			ok     = false
		)
		value, exist := c.Get(log.LogCtxKey)
		if exist {
			LogMsg, ok = value.(*log.LogDataStruct)
		}
		if !ok || !exist || LogMsg == nil {
			LogMsg = &log.LogDataStruct{
				TaskStatus: log.TaskUnknow,
			}
		}

		// check apperr
		var lv zapcore.Level
		if LogMsg.TaskStatus == log.TaskFail {
			lv = zap.WarnLevel
		} else if LogMsg.TaskStatus == log.TaskError {
			lv = zap.ErrorLevel
		} else {
			lv = zap.InfoLevel
		}

		// write logger
		if ce := log.Logger.Check(lv, path); ce != nil {
			ce.Write(
				zap.Int("status", c.Writer.Status()),
				zap.String("method", c.Request.Method),
				zap.String("path", path),
				zap.String("query", query),
				zap.String("ip", c.ClientIP()),
				zap.String("user-agent", c.Request.UserAgent()),
				zap.String("time", end.Format(timeFormat)),
				zap.Duration("latency", latency),
				zap.Object("LogMsg", LogMsg),
			)
		}
	}
}

解釋:

  • 時間的格式不需要時常修改或刪除,所以我直接寫在const當作設定

最後在router/main.go使用use就大功告成了

	r := gin.New()

	r.Use(middleware.Logging())	// 注意順序!!
	r.Use(middleware.ErrorHandle())

總結

基礎的環境建置完成,之後就要接資料庫處理API了

目前的工作環境

.
├── app
│   ├── apperr
│   │   ├── error.go
│   │   └── handle.go
│   ├── config
│   │   └── app
│   │       ├── app.yaml
│   │       └── error.yaml
│   ├── go.mod
│   ├── go.sum
│   ├── log
│   │   ├── logger.go
│   │   └── logging.go
│   ├── main.go
│   ├── middleware
│   │   ├── error.go
│   │   └── log.go
│   ├── router
│   │   ├── host_switch.go
│   │   └── main.go
│   ├── serve
│   │   ├── main.go
│   │   └── main_test.go
│   ├── setting
│   │   └── setting.go
│   └── util
│       └── debug
│           ├── stack.go
│           └── stack_test.go
└── database

上一篇
Day6 Log處理(上)
下一篇
Day8 資料庫架構
系列文
從coding到上線-打造自己的blog系統30

尚未有邦友留言

立即登入留言