当前位置: 首页 > news >正文

阿里云linux主机如何添加2个网站中山网站建设方案托管

阿里云linux主机如何添加2个网站,中山网站建设方案托管,网络设计的目的是,怎样做招聘网站分析前言 在项目开发和运维过程中,日志记录是不可或缺的一环,它帮助我们追踪请求、排查问题和监控系统状态。 Gin 框架本身提供了两个非常实用的默认中间件:gin.Logger() 和 gin.Recovery()。理解它们的功能是构建更强大日志系统的基础。本文会先…

前言

在项目开发和运维过程中,日志记录是不可或缺的一环,它帮助我们追踪请求、排查问题和监控系统状态。
Gin 框架本身提供了两个非常实用的默认中间件:gin.Logger() 和 gin.Recovery()。理解它们的功能是构建更强大日志系统的基础。本文会先介绍这两个中间件,并演示如何将 Gin 与功能强大的日志库 Zap 集成,以实现高性能、结构化的日志输出。

一、Gin 的默认日志中间件

1. gin.Logger()

gin.Logger() 是 Gin 提供的一个日志中间件,用于记录每个 HTTP 请求的基本信息。当你使用 gin.Default() 创建一个路由实例时,这个中间件会自动被加载。

它默认将日志输出到控制台(os.Stdout),记录的内容通常包括:

  • 请求方法(如 GET、POST)
  • 请求路径(URL)
  • HTTP 状态码
  • 响应耗时
  • 客户端 IP 地址

示例输出:

[GIN] 2025/09/09 - 15:00:00 | 200 |     123.456µs | 127.0.0.1 | GET /api/users

这个中间件对于开发和简单的调试非常有用,但其输出是纯文本格式,不利于后续的日志分析、搜索和监控。此外,它缺乏对日志级别、结构化字段等高级功能的支持。

2. gin.Recovery()

gin.Recovery() 是一个恢复中间件,用于捕获在处理请求过程中发生的 panic 异常,防止整个服务因单个请求的崩溃而终止。
当发生 panic 时,gin.Recovery() 会:

  • 捕获 panic。
  • 向客户端返回一个 500 Internal Server Error 响应。
  • 将 panic 的堆栈信息输出到日志(默认也是 os.Stdout)。

示例输出:

[GIN] 2025/09/09 - 15:05:00 | 500 |     1.234ms | 127.0.0.1 | GET /api/crash
panic: runtime error: invalid memory address or nil pointer dereference

与 gin.Logger() 类似,gin.Recovery() 的日志输出也是简单的文本格式,且默认输出到标准输出。

二、为什么需要集成 Zap?

虽然 Gin 的默认中间件提供了基本的日志功能,但在生产环境中,我们通常需要更强大、更灵活的日志解决方案。这就是 Zap 发挥作用的地方。

Zap 是 Uber 开源的一个高性能、结构化的 Go 日志库。它的主要优势包括:

  • 高性能:Zap 经过精心设计,性能远超标准库 log 和许多其他日志库,特别适合高并发场景。
  • 结构化日志:Zap 默认输出 JSON 格式的日志,包含明确的字段(如 level, msg, timestamp, fields 等),便于机器解析、搜索和集成到 ELK、Loki 等日志分析系统。
  • 丰富的日志级别:支持 Debug, Info, Warn, Error, DPanic, Panic, Fatal 等级别,方便进行日志分级管理。
  • 灵活的配置:可以轻松配置日志输出目标(文件、网络、标准输出等)、格式(JSON、文本)、编码器等。

如何将 Gin 与 Zap 集成

接下来,我们将一步步实现 Gin 与 Zap 的集成,替换默认的 Logger 和 Recovery 中间件

1.安装
go get -u go.uber.org/zap
2. 增加Viper log日志配置

config/config.go

Log struct {Level      string `mapstructure:"level"`       // 日志等级Format     string `mapstructure:"format"`      // 日志格式Filename   string `mapstructure:"filename"`    // 基准日志文件名MaxSize    int    `mapstructure:"maxsize"`     // 单个日志文件最大内容,单位:MBMaxAge     int    `mapstructure:"max_age"`     // 日志文件保存时间,单位:天MaxBackups int    `mapstructure:"max_backups"` // 最多保存几个日志文件Compress   bool   `mapstructure:"compress"`    // 是否压缩旧日志文件Stdout     bool   `mapstructure:"stdout"`      // 是否输出到标准输出} `mapstructure:"log"`

config.[dev|prod].yaml

log:level: "debug"format: "text" # 或 "json"filename: "./logs/dev/app.log"maxsize: 10 # 单个日志文件最大10MBmax_age: 7 # 日志保存7天max_backups: 5 # 最多保存5个日志文件compress: false # 不压缩旧日志stdout: true # 输出到标准输出
3.初始化

在initialize目录下创建logger.go文件,实现zap日志的初始化功能:

package initializeimport ("fmt""gin/global""github.com/gin-gonic/gin""go.uber.org/zap""go.uber.org/zap/zapcore""net""net/http""net/http/httputil""os""path""runtime/debug""strings""time"
)// InitLogger 初始化zap日志
func InitLogger() {// 创建编码器encoderConfig := zapcore.EncoderConfig{TimeKey:        "time",                           // 时间键LevelKey:       "level",                          // 日志级别键NameKey:        "logger",                         // 日志名称键CallerKey:      "caller",                         // 调用者键MessageKey:     "msg",                            // 消息键StacktraceKey:  "stacktrace",                     // 栈跟踪键LineEnding:     zapcore.DefaultLineEnding,        // 行结束符EncodeLevel:    zapcore.CapitalColorLevelEncoder, //使用带颜色的日志级别编码器EncodeTime:     zapcore.ISO8601TimeEncoder,       // 时间编码器EncodeDuration: zapcore.StringDurationEncoder,    // 持续时间编码器EncodeCaller:   zapcore.ShortCallerEncoder,       // 调用者编码器}// 设置日志级别var level zapcore.Levelswitch global.Config.Log.Level {case "debug":level = zapcore.DebugLevelcase "info":level = zapcore.InfoLevelcase "warn":level = zapcore.WarnLevelcase "error":level = zapcore.ErrorLeveldefault:level = zapcore.InfoLevel}// 创建核心var writers []zapcore.WriteSyncer// 如果配置了标准输出if global.Config.Log.Stdout {writers = append(writers, zapcore.AddSync(os.Stdout))}// 如果配置了文件输出if global.Config.Log.Filename != "" {fileWriter := getLogWriter(global.Config.Log.Filename,global.Config.Log.MaxSize,global.Config.Log.MaxBackups,global.Config.Log.MaxAge,global.Config.Log.Compress,)writers = append(writers, fileWriter)}// 如果没有配置任何输出,默认输出到标准输出if len(writers) == 0 {writers = append(writers, zapcore.AddSync(os.Stdout))}core := zapcore.NewCore(getEncoder(global.Config.Log.Format, encoderConfig),zapcore.NewMultiWriteSyncer(writers...),level,)// 创建Loggerlogger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))// 设置全局Loggerglobal.Logger = loggerzap.ReplaceGlobals(logger)
}// getEncoder 根据格式选择编码器
func getEncoder(format string, encoderConfig zapcore.EncoderConfig) zapcore.Encoder {if format == "json" {return zapcore.NewJSONEncoder(encoderConfig)}return zapcore.NewConsoleEncoder(encoderConfig)
}// getLogWriter 创建日志文件写入器
func getLogWriter(filename string, maxSize, maxBackup, maxAge int, compress bool) zapcore.WriteSyncer {// 创建日志目录logDir := path.Dir(filename)if logDir != "." {// 确保日志目录存在if err := os.MkdirAll(logDir, os.ModePerm); err != nil {fmt.Printf("创建日志目录失败: %v\n", err)}}// 打开文件file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)if err != nil {fmt.Printf("打开日志文件失败: %v\n", err)// 如果打开文件失败,返回标准错误输出return zapcore.AddSync(os.Stderr)}return zapcore.AddSync(file)
}
4.自定义日志中间件
// initialize/logger.go
func ZapLogger(logger *zap.Logger) gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()// 处理请求c.Next()// 计算请求耗时latency := time.Since(start)// 获取请求信息clientIP := c.ClientIP()method := c.Request.MethodstatusCode := c.Writer.Status()reqPath := c.Request.URL.PathuserAgent := c.Request.Header.Get("User-Agent")// 根据状态码决定日志级别var level zapcore.Levelswitch {case statusCode >= 500:level = zap.ErrorLevelcase statusCode >= 400:level = zap.WarnLeveldefault:level = zap.InfoLevel}// 构建日志字段fields := []zap.Field{zap.Int("status", statusCode),zap.String("method", method),zap.String("path", reqPath),zap.String("ip", clientIP),zap.String("user-agent", userAgent),zap.Duration("latency", latency),}// 添加自定义字段(例如,从上下文中获取的请求ID)if requestId, exists := c.Get("X-Request-ID"); exists {fields = append(fields, zap.String("request_id", requestId.(string)))}// 记录日志logger.Log(level, "HTTP Request", fields...)}
}
5.自定义恢复中间件

https://github.com/gin-contrib/zap/blob/master/zap.go

func ZapRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc {return func(c *gin.Context) {defer func() {if err := recover(); err != nil {// Check for a broken connection, as it is not really a// condition that warrants a panic stack trace.var brokenPipe boolif ne, ok := err.(*net.OpError); ok {if se, ok := ne.Err.(*os.SyscallError); ok {if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {brokenPipe = true}}}httpRequest, _ := httputil.DumpRequest(c.Request, false)if brokenPipe {logger.Error(c.Request.URL.Path,zap.Any("error", err),zap.String("request", string(httpRequest)),)// If the connection is dead, we can't write a status to it.c.Error(err.(error)) // nolint: errcheckc.Abort()return}if stack {logger.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),zap.String("stack", string(debug.Stack())),)} else {logger.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),)}c.AbortWithStatus(http.StatusInternalServerError)}}()c.Next()}
}
6.在 Gin 应用中使用

main.go

// 在config初始化后
initialize.InitLogger()

initialize/router.go

Router := gin.New()Router.Use(ZapLogger(global.Logger))
Router.Use(ZapRecovery(global.Logger, true))
// 记录不同级别的日志
global.Logger.Debug("这是一条调试日志", zap.String("key", "value"))
global.Logger.Info("这是一条信息日志", zap.Int("count", 10))
global.Logger.Warn("这是一条警告日志", zap.Error(err))
global.Logger.Error("这是一条错误日志", zap.String("error_type", "validation"))
global.Logger.Fatal("Failed to connect to database", zap.Error(err))global.Logger.With(zap.String("request_id", "12345")).Info("处理请求")

注:Logger.Error仅记录日志。调用 Error 方法后,程序会继续正常执行后续的代码;
Logger.Fatal 记录日志 + 立即终止程序。调用 Fatal 方法后,Zap 会先将日志写入(并调用 Sync() 确保日志被刷新到磁盘或输出目标),然后立即调用 os.Exit(1)

7.运行与验证

启动应用,观察使用日志的地方是否有日志输出。

三、其他用法

  • 日志上下文:利用 Gin 的 Context 传递请求 ID、用户 ID 等信息,并在日志中输出,便于全链路追踪。
    异步写入:对于极高性能要求的场景,可以考虑使用 Zap 的异步写入功能。
  • 日志脱敏:在记录日志时,注意对敏感信息(如密码、身份证号)进行脱敏处理。
  • 集中化管理:将日志发送到 Kafka 或直接对接 Loki、Fluentd 等日志收集系统。

四、总结

通过将 Gin 与 Zap 集成,我们成功地将一个基础的 Web 框架升级为一个具备生产级日志能力的应用。Zap 提供的高性能和结构化日志特性,使得我们的应用在面对高并发流量时依然能够稳定、高效地记录关键信息。这不仅提升了系统的可观测性,也为后续的运维、监控和问题排查奠定了坚实的基础。

日志是系统的“黑匣子”,一个设计良好的日志系统是保障服务稳定运行的基石。希望本文能帮助你更好地在 Gin 项目中使用 Zap,构建更健壮的 Go 应用。

示例代码

gitee

http://www.dtcms.com/a/482466.html

相关文章:

  • React 状态管理中的循环更新陷阱与解决方案
  • 手机h5免费模板网站深圳网页设计培训要多久
  • 网站快速建设网络营销公司介绍
  • 唐山seo网站建设企业网站的建立如何带来询盘
  • 上海虹口网站建设重庆网站建设公司的网站
  • 自动化测试之 Cucumber 工具
  • 基于MATLAB的t-SNE算法多合成数据集降维可视化实现
  • SAP 关于工单的状态更改,这个要怎么查看呢?
  • 网站建设费用会计分录男女做暧暧视频免费网站
  • 如何高效编写MySQL数据导出与导入语句?
  • 第六部分:VTK进阶(第160章 体绘制采样与空域加速)
  • 网站开发什么意思泾阳做网站
  • 什么是swc?
  • 第九章 装饰器与闭包
  • 接口测试案例从哪些维度去设计
  • 协程入门(基础篇)
  • 建设好网站的在线沟通功能广州开发区投资集团有限公司招聘
  • 如何将 iPhone 联系人同步到 Mac
  • 织梦的网站收录不好保定网站建设设计
  • 网络安全之揭秘APT Discord C2 以及如何取证
  • 第五章 神经网络的优化
  • 网络安全主动防御技术与应用
  • 5. 神经网络的学习
  • 响应式网站页面设计怎么写网站建设推广
  • 2025/10/14 redis断联 没有IPv4地址 (自用)
  • 基于多奥品牌设备的车牌识别系统与电梯门禁联动方案,核心是通过硬件信号对接+软件权限映射实现车辆身份与电梯权限的绑定。以下是具体实施步骤:
  • [Backstage] 前端插件 生命周期 | eg构建“云成本”页面
  • extractNativeLibs属性解刨
  • 实现一个通用的 `clone` 函数:从深拷贝到类型安全的 C++ 模板设计
  • dw做网站基础用友财务软件多少钱一年