goBlog 项目目录
1.创建日志文件
mkdir storeage/logs/logs.log
2.sudo vim bootstrap/logger.go 初始化 日志的文件
package bootstrap
import (
"github.com/spf13/cast"
"github.com/spf13/viper"
"goBlog/pkg/logger"
)
// SetupLogger 初始化 Logger
func SetupLogger() {
logger.InitLogger(
"storage/logs/logs.log",
64,
5,
30,
true,
cast.ToString(viper.Get("log.log_type")),
cast.ToString(viper.Get("log.log_level")),
)
}
3.sudo vim pkg/logger/logger.go 日志包
// Package logger.go 处理日志相关逻辑
package logger
import (
"encoding/json"
"fmt"
"gopkg.in/natefinch/lumberjack.v2"
"os"
"strings"
"time"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// Logger 全局 Logger 对象
var Logger *zap.Logger
// InitLogger 日志初始化
func InitLogger(filename string, maxSize, maxBackup, maxAge int, compress bool, logType string, level string) {
// 获取日志写入介质
writeSyncer := getLogWriter(filename, maxSize, maxBackup, maxAge, compress, logType)
// 设置日志等级,具体请见 config/log.go 文件
logLevel := new(zapcore.Level)
if err := logLevel.UnmarshalText([]byte(level)); err != nil {
fmt.Println("日志初始化错误,日志级别设置有误。请修改 config/log.go 文件中的 log.level 配置项")
}
// 初始化 core
core := zapcore.NewCore(getEncoder(), writeSyncer, logLevel)
// 初始化 Logger
Logger = zap.New(core,
zap.AddCaller(), // 调用文件和行号,内部使用 runtime.Caller
zap.AddCallerSkip(1), // 封装了一层,调用文件去除一层(runtime.Caller(1))
zap.AddStacktrace(zap.ErrorLevel), // Error 时才会显示 stacktrace
)
// 将自定义的 logger.go 替换为全局的 logger.go
// zap.L().Fatal() 调用时,就会使用我们自定的 Logger
zap.ReplaceGlobals(Logger)
}
// getEncoder 设置日志存储格式
func getEncoder() zapcore.Encoder {
// 日志格式规则
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger.go",
CallerKey: "caller", // 代码调用,如 paginator/paginator.go:148
FunctionKey: zapcore.OmitKey,
MessageKey: "message",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding, // 每行日志的结尾添加 "\n"
EncodeLevel: zapcore.CapitalLevelEncoder, // 日志级别名称大写,如 ERROR、INFO
EncodeTime: customTimeEncoder, // 时间格式,我们自定义为 2006-01-02 15:04:05
EncodeDuration: zapcore.SecondsDurationEncoder, // 执行时间,以秒为单位
EncodeCaller: zapcore.ShortCallerEncoder, // Caller 短格式,如:types/converter.go:17,长格式为绝对路径
}
// 本地环境配置
if viper.Get("app_env") == "local" {
// 终端输出的关键词高亮
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
// 本地设置内置的 Console 解码器(支持 stacktrace 换行)
return zapcore.NewConsoleEncoder(encoderConfig)
}
// 线上环境使用 JSON 编码器
return zapcore.NewJSONEncoder(encoderConfig)
}
// customTimeEncoder 自定义友好的时间格式
func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05"))
}
// getLogWriter 日志记录介质。Gohub 中使用了两种介质,os.Stdout 和文件
func getLogWriter(filename string, maxSize, maxBackup, maxAge int, compress bool, logType string) zapcore.WriteSyncer {
// 如果配置了按照日期记录日志文件
if logType == "daily" {
logname := time.Now().Format("2006-01-02.log")
filename = strings.ReplaceAll(filename, "logs.log", logname)
}
// 滚动日志,详见 config/log.go
lumberJackLogger := &lumberjack.Logger{
Filename: filename,
MaxSize: maxSize,
MaxBackups: maxBackup,
MaxAge: maxAge,
Compress: compress,
}
// 配置输出介质
if viper.Get("app_env") == "local" {
// 本地开发终端打印和记录文件
return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger))
} else {
// 生产环境只记录文件
return zapcore.AddSync(lumberJackLogger)
}
}
// Dump 调试专用,不会中断程序,会在终端打印出 warning 消息。
// 第一个参数会使用 json.Marshal 进行渲染,第二个参数消息(可选)
// logger.Dump(user.User{Name:"test"})
// logger.Dump(user.User{Name:"test"}, "用户信息")
func Dump(value interface{}, msg ...string) {
valueString := jsonString(value)
// 判断第二个参数是否传参 msg
if len(msg) > 0 {
Logger.Warn("Dump", zap.String(msg[0], valueString))
} else {
Logger.Warn("Dump", zap.String("data", valueString))
}
}
// LogIf 当 err != nil 时记录 error 等级的日志
func LogIf(err error) {
if err != nil {
Logger.Error("Error Occurred:", zap.Error(err))
}
}
// LogWarnIf 当 err != nil 时记录 warning 等级的日志
func LogWarnIf(err error) {
if err != nil {
Logger.Warn("Error Occurred:", zap.Error(err))
}
}
// LogInfoIf 当 err != nil 时记录 info 等级的日志
func LogInfoIf(err error) {
if err != nil {
Logger.Info("Error Occurred:", zap.Error(err))
}
}
// Debug 调试日志,详尽的程序日志
// 调用示例:
// logger.Debug("Database", zap.String("sql", sql))
func Debug(moduleName string, fields ...zap.Field) {
Logger.Debug(moduleName, fields...)
}
// Info 告知类日志
func Info(moduleName string, fields ...zap.Field) {
Logger.Info(moduleName, fields...)
}
// Warn 警告类
func Warn(moduleName string, fields ...zap.Field) {
Logger.Warn(moduleName, fields...)
}
// Error 错误时记录,不应该中断程序,查看日志时重点关注
func Error(moduleName string, fields ...zap.Field) {
Logger.Error(moduleName, fields...)
}
// Fatal 级别同 Error(), 写完 log 后调用 os.Exit(1) 退出程序
func Fatal(moduleName string, fields ...zap.Field) {
Logger.Fatal(moduleName, fields...)
}
// DebugString 记录一条字符串类型的 debug 日志,调用示例:
// logger.DebugString("SMS", "短信内容", string(result.RawResponse))
func DebugString(moduleName, name, msg string) {
Logger.Debug(moduleName, zap.String(name, msg))
}
func InfoString(moduleName, name, msg string) {
Logger.Info(moduleName, zap.String(name, msg))
}
func WarnString(moduleName, name, msg string) {
Logger.Warn(moduleName, zap.String(name, msg))
}
func ErrorString(moduleName, name, msg string) {
Logger.Error(moduleName, zap.String(name, msg))
}
func FatalString(moduleName, name, msg string) {
Logger.Fatal(moduleName, zap.String(name, msg))
}
// DebugJSON 记录对象类型的 debug 日志,使用 json.Marshal 进行编码。调用示例:
// logger.DebugJSON("Auth", "读取登录用户", auth.CurrentUser())
func DebugJSON(moduleName, name string, value interface{}) {
Logger.Debug(moduleName, zap.String(name, jsonString(value)))
}
func InfoJSON(moduleName, name string, value interface{}) {
Logger.Info(moduleName, zap.String(name, jsonString(value)))
}
func WarnJSON(moduleName, name string, value interface{}) {
Logger.Warn(moduleName, zap.String(name, jsonString(value)))
}
func ErrorJSON(moduleName, name string, value interface{}) {
Logger.Error(moduleName, zap.String(name, jsonString(value)))
}
func FatalJSON(moduleName, name string, value interface{}) {
Logger.Fatal(moduleName, zap.String(name, jsonString(value)))
}
func jsonString(value interface{}) string {
b, err := json.Marshal(value)
if err != nil {
Logger.Error("Logger", zap.String("JSON marshal error", err.Error()))
}
return string(b)
}
4.sudo vim middlewares/middleware_log/logger.go 中间件
package middleware_log
import (
"bytes"
"goBlog/pkg/helps"
"goBlog/pkg/logger"
"io/ioutil"
"time"
"github.com/gin-gonic/gin"
"github.com/spf13/cast"
"go.uber.org/zap"
)
type responseBodyWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (r responseBodyWriter) Write(b []byte) (int, error) {
r.body.Write(b)
return r.ResponseWriter.Write(b)
}
// Logger 记录请求日志
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
// 获取 response 内容
w := &responseBodyWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer}
c.Writer = w
// 获取请求数据
var requestBody []byte
if c.Request.Body != nil {
// c.Request.Body 是一个 buffer 对象,只能读取一次
requestBody, _ = ioutil.ReadAll(c.Request.Body)
// 读取后,重新赋值 c.Request.Body ,以供后续的其他操作
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody))
}
// 设置开始时间
start := time.Now()
c.Next()
// 开始记录日志的逻辑
cost := time.Since(start)
resposeStatus := c.Writer.Status()
logFields := []zap.Field{
zap.Int("status", resposeStatus),
zap.String("request", c.Request.Method+" "+c.Request.URL.String()),
zap.String("query", c.Request.URL.RawQuery),
zap.String("ip", c.ClientIP()),
zap.String("user-agent", c.Request.UserAgent()),
zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
zap.String("time", helpers.MicrosecondsStr(cost)),
}
if c.Request.Method == "POST" || c.Request.Method == "PUT" || c.Request.Method == "DELETE" {
// 请求的内容
logFields = append(logFields, zap.String("Request Body", string(requestBody)))
// 响应的内容
logFields = append(logFields, zap.String("Response Body", w.body.String()))
}
if resposeStatus > 400 && resposeStatus <= 499 {
// 除了 StatusBadRequest 以外,warning 提示一下,常见的有 403 404,开发时都要注意
logger.Warn("HTTP Warning "+cast.ToString(resposeStatus), logFields...)
} else if resposeStatus >= 500 && resposeStatus <= 599 {
// 除了内部错误,记录 error
logger.Error("HTTP Error "+cast.ToString(resposeStatus), logFields...)
} else {
logger.Debug("HTTP Access Log", logFields...)
}
}
}
5.将 main.go 加上
bootstrap.SetupLogger()
6.配置文件 config.toml 追加
log_level = “DEBUG”
7.给文件赋予权限
chmod -R 777 storage/logs/logs.log
版权声明:本文为weixin_37647596原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。