服务端 log 方案

最近在重做青训营项目,而在后端服务中,一个完善的后端 log 方案是必不可少的。这里记录整理在服务端代码中的 log 使用方式与常见的 log 制定的规范。

服务端中何时记录 log

明确日志的功能,日志的作用是记录系统的运行状态,以便于排查问题。常见功能如下:

  • 调试功能,快速定位出现问题的模块
  • 记录保存系统运行状态,以便于后续分析
  • 分析与优化系统性能

log 打印时机

虽说日志的作用是记录系统的运行状态,但不要滥用日志,日志过多会影响系统性能,导致问题难以定位。在日志打印中只需在关键位置打印即可,如:

  • http 请求与 rpc 调用时记录请求参数与返回结果(成功或失败)
  • 程序异常时,打印异常信息
  • 组件调用,如 DB、MQ 等
  • 关键执行路径某些特殊的分支
  • 某些功能的运行时间
  • 定时任务开始结束
  • ……

日志级别

  • TRACE:追踪特定功能
  • DEBUG:代码调试
  • INFO:记录运行信息
  • WAEN:某任务处理异常,但不影响系统运行 (重试某个任务)
  • ERROR:服务系统错误,影响系统运行
  • FATAL:致命错误,导致服务宕机,数据损坏

一个有意思的图例:

log-level

zap lumberjack log 包实现

  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
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package log

import (
	"os"
	"sync"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

var (
	logger *zap.Logger
	once   sync.Once
)

var (
	Debug func(msg string, fields ...zap.Field)
	Info  func(msg string, fields ...zap.Field)
	Warn  func(msg string, fields ...zap.Field)
	Error func(msg string, fields ...zap.Field)
	Fatal func(msg string, fields ...zap.Field)

	String   = zap.String
	Int      = zap.Int
	Err      = zap.Error
	Any      = zap.Any
	Duration = zap.Duration
)

func InitLog() {
	once.Do(func() {
		logger = logConfig()
	})

	Debug = logger.Debug
	Info = logger.Info
	Warn = logger.Warn
	Error = logger.Error
	Fatal = logger.Fatal

}

func logConfig() *zap.Logger {

	homePath := os.Getenv("HOME")
	// 日志文件分割配置
	fileWriterHook := &lumberjack.Logger{
		Filename:   homePath + "/var/log/tiktokserver/server.log",
		MaxSize:    500, // megabytes
		MaxBackups: 3,
		MaxAge:     28,   //days
		Compress:   true, // disabled by default
	}

	// 日志文件输出配置
	fileEncoderConfig := zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "line",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalLevelEncoder,                        // 全大写日志等级标识
		EncodeTime:     zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05"), // 时间格式
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
		EncodeName:     zapcore.FullNameEncoder,
	}

	// 终端输出配置
	stdEncoderConfig := zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "line",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalColorLevelEncoder,
		EncodeTime:     zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05"),
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
		EncodeName:     zapcore.FullNameEncoder,
	}

	fileEncoder := zapcore.NewJSONEncoder(fileEncoderConfig)
	stdEncoder := zapcore.NewConsoleEncoder(stdEncoderConfig)

	fileWriter := zapcore.NewMultiWriteSyncer(zapcore.AddSync(fileWriterHook))
	stdWriter := zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout))

	// 日志级别过滤
	debugLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.DebugLevel
	})

	// infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
	// 	return lvl >= zapcore.InfoLevel
	// })

	warnLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.WarnLevel
	})

	core := zapcore.NewTee(
		zapcore.NewCore(fileEncoder, fileWriter, warnLevel),
		zapcore.NewCore(stdEncoder, stdWriter, debugLevel),
	)

	if os.Getenv("GIN_DEBUG") == "true" {
		caller := zap.AddCaller()
		development := zap.Development()
		return zap.New(core, caller, development)
	} else {
		return zap.New(core)
	}
}

func Sync() {
	logger.Sync()
}
0%