Logger模块详解
Logger
模块最终以 HandlerFunc
函数对象的形式(Logger
处理中间件)提供给其他模块使用。
LoggerConfig类型
LoggerConfig
是创建 HandlerFunc
对象的重要参数类型。
类型定义
type LoggerConfig struct {
// 默认为gin.defaultLogFormatter
Formatter LogFormatter
// 日志写到哪里去,默认为gin.DefaultWriter
Output io.Writer
// 哪些URL路径的日志不用记录
SkipPaths []string
}
LogFormatter
LogFormatter
是一个函数类型,用于格式化日志,参数类型为 LogFormatterParams
。
函数原型
type LogFormatter func(params LogFormatterParams) string
LogFormatterParams
当使用 LogFormatter
格式化日志时,LogFormatterParams
作为参数传入。
defaultLogFormatter变量
defaultLogFormatter是一个默认的日志数据的格式化函数
var defaultLogFormatter = func(param LogFormatterParams) string {
// 获取颜色码
var statusColor, methodColor, resetColor string
if param.IsOutputColor() {
statusColor = param.StatusCodeColor()
methodColor = param.MethodColor()
resetColor = param.ResetColor()
}
// 处理请求超过一分钟,将处理请求时间对秒取整
if param.Latency > time.Minute {
// Truncate in a golang < 1.8 safe way
param.Latency = param.Latency - param.Latency%time.Second
}
// 日志数据格式化
return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s",
param.TimeStamp.Format("2006/01/02 - 15:04:05"),
statusColor, param.StatusCode, resetColor,
param.Latency,
param.ClientIP,
methodColor, param.Method, resetColor,
param.Path,
param.ErrorMessage,
)
}
DefaultWriter
DefaultWriter为默认的Logger Writer。
var DefaultWriter io.Writer = os.Stdout
DefaultErrorWriter
DefaultErrorWriter为默认的Error Writer。
// DefaultErrorWriter is the default io.Writer used by Gin to debug errors
var DefaultErrorWriter io.Writer = os.Stderr
创建HandlerFunc(Logger处理中间件)
gin提供几个函数:具有指定的日志格式函数的日志处理中间件。
类型定义
type HandlerFunc func(*Context)
Context类型见gin学习之Context篇
函数原型
// 基础函数定义
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
// 封装函数定义
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
func LoggerWithFormatter(f LogFormatter) HandlerFunc
func Logger() HandlerFunc
func ErrorLoggerT(typ ErrorType) HandlerFunc
func ErrorLogger() HandlerFunc
详细代码分析
通过LoggerWithConfig()返回一个Logger处理中间件,传入自定义LoggerConfig参数。
func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
// 配置Logger的formatter
formatter := conf.Formatter
if formatter == nil {
formatter = defaultLogFormatter
}
// 配置Logger的output
out := conf.Output
if out == nil {
out = DefaultWriter
}
// 配置Logger的notlogged(忽略一些处理URL路径下请求的日志)
notlogged := conf.SkipPaths
// 先默认日志输出目的地为终端设备
isTerm := true
// 判断日志输出目的地是否为终端
if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||
(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
isTerm = false
}
var skip map[string]struct{}
// 处理notlogged
if length := len(notlogged); length > 0 {
skip = make(map[string]struct{}, length)
for _, path := range notlogged {
skip[path] = struct{}{}
}
}
// 基于gin.Context返回日志格式化的中间件处理函数
// gin.Context详解见gin学习之context
return func(c *Context) {
// Start timer
start := time.Now()
path := c.Request.URL.Path
raw := c.Request.URL.RawQuery
// Process request
c.Next()
// 当前请求的path不跳过,才会记录日志
if _, ok := skip[path]; !ok {
// 填充LogFormatterParams结构
param := LogFormatterParams{
Request: c.Request,
isTerm: isTerm,
Keys: c.Keys,
}
// Stop timer
param.TimeStamp = time.Now()
param.Latency = param.TimeStamp.Sub(start)
param.ClientIP = c.ClientIP()
param.Method = c.Request.Method
param.StatusCode = c.Writer.Status()
// errors处理见gin学习之errors
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
param.BodySize = c.Writer.Size()
if raw != "" {
path = path + "?" + raw
}
param.Path = path
// type LogFormatter func(params LogFormatterParams) string
// 调用formatter函数(类型为LogFormatter),得到格式化日志,并输出到out
fmt.Fprint(out, formatter(param))
}
}
}
通过Logger()返回一个Logger处理中间件,使用默认配置参数。
// writer=gin.DefaultWriter(os.Stdout), formatter=gin.defaultLogFormatter
func Logger() HandlerFunc {
return LoggerWithConfig(LoggerConfig{})
}
通过LoggerWithFormatter()获得Logger处理中间件,传入LogFormatter参数。
func LoggerWithFormatter(f LogFormatter) HandlerFunc {
return LoggerWithConfig(LoggerConfig{
Formatter: f,
})
}
通过LoggerWithWriter()获得Logger处理中间件,传入自定义writer和notlogged(忽略一些处理URL路径下请求的日志)。
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc HandlerFunc {
return LoggerWithConfig(LoggerConfig{
Output: out,
SkipPaths: notlogged,
})
}
通过ErrorLogger()返回一个Logger处理中间件。
func ErrorLogger() HandlerFunc {
return ErrorLoggerT(ErrorTypeAny)
}
通过ErrorLoggerT()返回一个Logger处理中间件。
func ErrorLoggerT(typ ErrorType) HandlerFunc {
return func(c *Context) {
c.Next()
errors := c.Errors.ByType(typ)
if len(errors) > 0 {
c.JSON(-1, errors)
}
}
}
Logger运行机制
所有 HandlerFunc
对象都会注册挂载到 Engine.RouterGroup.Handlers
上面。
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
// ......
}
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
Logger机制使用举例如下
// new一个io.Writer,作为日志的输出目的地
buffer := new(bytes.Buffer)
// 创建一个Engine对象(router)
router := New()
// 向router注册一个日志处理中间件
router.Use(LoggerWithConfig(LoggerConfig{Output: buffer}))
其他方法
// 禁止终端日志有色显示
func DisableConsoleColor() {
consoleColorMode = disableColor
}
// 强制终端日志有色显示
func ForceConsoleColor() {
consoleColorMode = forceColor
}