Go 即时通讯系统:日志模块重构,并从main函数开始
重构logger
上次写的logger.go过于繁琐,有很多没用到的功能;重构后只提供了简洁的日志接口,支持日志轮转、多级别日志记录等功能,并采用单例模式确保全局只有一个日志实例
全局变量
var ( once sync.Once // 用于实现单例模式的同步控制 Logger *zap.Logger // 全局日志实例 String = zap.String // 导出常用的 zap.Field 构造方法 Any = zap.Any Err = zap.Error Int = zap.Int Float32 = zap.Float32 )
默认配置常量
const ( defaultLogPath = "./logs" // 默认日志存储目录 defaultLogLevel = "debug" // 默认日志级别 defaultMaxSize = 100 // 单个日志文件最大大小(MB) defaultMaxBackups = 30 // 保留的旧日志文件数量 defaultMaxAge = 7 // 日志保留天数 defaultCompress = true // 是否压缩旧日志 )
初始化日志记录器
// Init 初始化日志记录器(单例模式) func Init() *zap.Logger { once.Do(func() { // 确保日志目录存在 if err := os.MkdirAll(defaultLogPath, 0755); err != nil { panic(err) } // 设置日志级别 level := getLogLevel(defaultLogLevel) // 日志文件路径 logFile := getDatedLogFilename(defaultLogPath) // 设置日志轮转 writer := zapcore.AddSync(&lumberjack.Logger{ Filename: logFile, MaxSize: defaultMaxSize, // MB MaxBackups: defaultMaxBackups, // 保留的旧日志文件数量 MaxAge: defaultMaxAge, // 保留天数 Compress: defaultCompress, // 是否压缩 }) // 编码器配置 encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder // 不带颜色 // 核心配置 core := zapcore.NewCore( // zapcore.NewJSONEncoder(encoderConfig), // json格式 zapcore.NewConsoleEncoder(encoderConfig), // 使用 Console 编码器 writer, level, ) // 创建Logger Logger = zap.New(core) }) return Logger }
获取日志实例
func GetLogger() *zap.Logger { if Logger == nil { Init() // 自动初始化 } return Logger }
直接可用的日志方法
func Debug(msg string, fields ...zap.Field) { GetLogger().Debug(msg, fields...) } func Info(msg string, fields ...zap.Field) { GetLogger().Info(msg, fields...) } func Warn(msg string, fields ...zap.Field) { GetLogger().Warn(msg, fields...) } func Error(msg string, fields ...zap.Field) { GetLogger().Error(msg, fields...) } func Fatal(msg string, fields ...zap.Field) { GetLogger().Fatal(msg, fields...) } func Sync() error { return GetLogger().Sync() }
代码地址:logger.go
从main函数开始
func main() { defer log.Sync() // 记录日志 log.Info("start chat server...") newRouter := router.NewRouter() go chat.MyServer.Start() s := &http.Server{ Addr: ":8080", Handler: newRouter, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 log.Error("server start error", log.Err(err)) } } gin.SetMode(gin.ReleaseMode) server := gin.Default() server.Use(Cors()) server.Use(Recovery) socket := RunSocket group := server.Group("") { // 用户管理功能 group.GET("/user", api.GetUserList) group.GET("/user/:uuid", api.GetUserDetails) group.GET("/user/name", api.GetUserOrGroupByName) group.POST("/user/register", api.Register) group.POST("/user/login", api.Login) group.PUT("/user", api.ModifyUserInfo) group.POST("/friend", api.AddFriend) group.GET("/message", api.GetMessage) group.GET("/file/:fileName", api.GetFile) group.POST("/file", api.SaveFile) group.GET("/group/:uuid", api.GetGroup) group.POST("/group/:uuid", api.SaveGroup) group.POST("/group/join/:userUuid/:groupUuid", api.JoinGroup) group.GET("/group/user/:uuid", api.GetGroupUsers) group.GET("/socket.io", socket) } return server } return func(c *gin.Context) { method := c.Request.Method origin := c.Request.Header.Get("Origin") if origin != "" { // 设置跨域响应头 c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type") c.Header("Access-Control-Allow-Credentials", "true") } // 处理 OPTIONS 预检请求 if method == "OPTIONS" { c.JSON(http.StatusOK, "ok!") return } // 异常捕获 defer func() { if err := recover(); err != nil { log.Error("HttpError", log.Any("HttpError", err)) } }() c.Next() // 处理请求 } } defer func() { if r := recover(); r != nil { log.Error("gin catch error", log.Any("error", r)) c.JSON(http.StatusOK, response.FailMsg("系统内部错误")) } }() c.Next() } CheckOrigin: func(r *http.Request) bool { return true // 允许所有跨域 WebSocket 连接 }, } func RunSocket(c *gin.Context) { user := c.Query("user") // 获取用户标识 if user == "" { return // 无用户标识则拒绝连接 } log.Info("newUser", log.String("newUser", user)) // 升级 HTTP 连接为 WebSocket 连接 ws, err := upGrader.Upgrade(c.Writer, c.Request, nil) if err != nil { return } // 创建客户端对象 client := &server.Client{ Name: user, Conn: ws, Send: make(chan []byte), } // 注册客户端到服务器 server.MyServer.Register
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。