golang echo 程式碼詳解之 log 篇

Laily發表於2017-11-26

一、echo 自帶的 log 庫

1. log 結構

echo 框架的 log 結構體是 echo.Echo 結構體的一個屬性

type Echo struct {
    ...
    Logger Logger
}

而 logger 是個這樣的介面

type (
    // Logger defines the logging interface.
    Logger interface {
        Output() io.Writer
        SetOutput(w io.Writer)
        Prefix() string
        SetPrefix(p string)
        Level() log.Lvl
        SetLevel(v log.Lvl)
        Print(i ...interface{})
        Printf(format string, args ...interface{})
        Printj(j log.JSON)
        Debug(i ...interface{})
        Debugf(format string, args ...interface{})
        Debugj(j log.JSON)
        Info(i ...interface{})
        Infof(format string, args ...interface{})
        Infoj(j log.JSON)
        Warn(i ...interface{})
        Warnf(format string, args ...interface{})
        Warnj(j log.JSON)
        Error(i ...interface{})
        Errorf(format string, args ...interface{})
        Errorj(j log.JSON)
        Fatal(i ...interface{})
        Fatalj(j log.JSON)
        Fatalf(format string, args ...interface{})
        Panic(i ...interface{})
        Panicj(j log.JSON)
        Panicf(format string, args ...interface{})
    }
)

一般的 log 也都實現了這些方法,所以我們可以使用自己的 log 包替換這個。而作者是使用的 github.com/labstack/gommon/log 這個包。

到這裡,自定義 log 級別,輸出位置都一目瞭然了。

2. 預設的 log

在生成 echo.Echo 例項的時候,會初始化一個預設的 log。

// 初始化一個 Echo 例項
func New() (e *Echo) {
    e = &Echo{
        ...
        Logger:   log.New("echo"),
    }
    ...
    e.Logger.SetLevel(log.ERROR) // 預設日誌級別
    ...
    return
}

// log.New() 方法是這樣的
func New(prefix string) (l *Logger) {
    l = &Logger{
        level:    INFO,
        prefix:   prefix,
        template: l.newTemplate(defaultHeader),
        color:    color.New(), // 這個是讓不同級別的日誌在控制檯顯示不用顏色的。
        bufferPool: sync.Pool{
            New: func() interface{} {
                return bytes.NewBuffer(make([]byte, 256))
            },
        },
    }
    l.initLevels() // 同樣是處理顏色
    l.SetOutput(output()) // 預設是 os.Stdout
    return
}

這裡的 template 是 github.com/valyala/fasttemplate 包的物件,是一個簡單的模版引擎,用來控制 log 的輸出樣式。

預設的樣式是這樣的

defaultHeader = `{"time":"${time_rfc3339_nano}","level":"${level}","prefix":"${prefix}",` +
    `"file":"${short_file}","line":"${line}"}`

這裡的很多配置都和官方 log 的配置類似,也顯示了檔名和行號。這裡預設支援的時間格式只有兩種

time_rfc3339 // "2006-01-02T15:04:05Z07:00"
time_rfc3339_nano // "2006-01-02T15:04:05.999999999Z07:00"

需要更深層次的定製的話就需要修改或者替換 log 包了。

二、 log 中介軟體

通過這樣的方法來註冊 log 中介軟體,這個中介軟體主要用來針對 http 請求列印日誌。

// 使用預設的配置
e.Use(middleware.Logger())

// 自定義配置
// 自定義配置只支援 Format 和 Output 兩個屬性
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
  Format: "method=${method}, uri=${uri}, status=${status}\n",
  Output os.Stdout,
}))

預設的配置是這樣的

DefaultLoggerConfig = LoggerConfig{
    ...
    Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` +
        `"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` +
        `"latency_human":"${latency_human}","bytes_in":${bytes_in},` +
        `"bytes_out":${bytes_out}}` + "\n",
    Output:  os.Stdout,
    colorer: color.New(),
}

自定義配置支援下面這些欄位

- time_unix
- time_unix_nano
- time_rfc3339
- time_rfc3339_nano
// 時間上多了兩個 unix 時間戳型別

- id (Request ID)
- remote_ip
- uri
- host
- method
- path
- referer
- user_agent
- status
// 常規的 http 請求內容

- latency (In nanoseconds) 
- latency_human (Human readable)
// 這個可以算作處理日誌花的時間

- bytes_in (Bytes received)
- bytes_out (Bytes sent)
// request 請求和 response 響應的大小

- header:<NAME>
- query:<NAME>
- form:<NAME>
- cookie:<NAME>
// 這幾個可以拿到具體內容,分別用下面的方法取得
// tag 就是上面的欄位
// c.Request().Header.Get(tag[7:])
// c.QueryParam(tag[6:])
// c.FormValue(tag[5:])
// c.Cookie(tag[7:]

整個日誌中介軟體在這裡 https://github.com/labstack/echo/blob/master/middleware/logger.go ,不符合也可以根據需要重新實現一個。

三、後記

之前翻譯了 echo 的中文文件 http://go-echo.org,發現文件有很多地方沒有說清楚,就萌生了邊看程式碼邊補充使用文件的想法。拖了很久終於開工了,拿最簡單的 log 開篇,後面會陸續更新關於 echo 其他模組的介紹。

原文地址:laily.net

更多原創文章乾貨分享,請關注公眾號
  • golang echo 程式碼詳解之 log 篇
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章