Go 即时通讯系统:日志模块重构,并从main函数开始

news2025/6/4 10:57:22

重构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 << 20,
	}
	err := s.ListenAndServe()
	if err != nil {
		log.Error("server start error", log.Err(err))
	}
}
  1. 初始化路由
    • 创建 HTTP 请求路由器。
    • 会在这里定义所有的 API 端点(endpoints)和对应的处理函数。
    • 返回一个实现了 http.Handler 接口的路由器对象。
  2. 启动后台服务
    • 使用 go 关键字启动一个 goroutine,异步运行 chat.MyServer.Start() 方法。
    • 这通常用于启动需要长期运行的 WebSocket 服务。
  3. 配置 HTTP 服务器
  4. 启动 HTTP 服务器:开始监听指定端口(8080)的 HTTP 请求。

自定义Gin路由

NewRouter 函数解析

func NewRouter() *gin.Engine {
	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
}
  1. Gin 模式设置gin.SetMode(gin.ReleaseMode) 将 Gin 设置为发布模式,减少调试信息输出。
  2. 中间件使用Cors() 中间件处理跨域请求,Recovery 中间件捕获并处理 panic。
  3. 路由分组:所有路由都定义在根分组 ("") 下,清晰的 RESTful 风格路由设计。
  4. 路由类型:用户管理:GET/POST/PUT 操作;文件管理:文件上传/下载;WebSocket:实时通信端点。

跨域处理中间件 (Cors)

func Cors() gin.HandlerFunc {
    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() // 处理请求
    }
}
  1. 跨域头设置
    • 允许所有来源 (*),生产环境应替换为具体域名
    • 允许的 HTTP 方法
    • 允许的请求头
    • 允许客户端访问的响应头
    • 允许携带凭证
  2. OPTIONS 请求处理:直接返回 200 状态码,满足浏览器的预检请求。
  3. 异常捕获:使用 defer+recover 捕获处理过程中的 panic,记录错误日志。

恢复中间件 (Recovery)

func Recovery(c *gin.Context) {
    defer func() {
        if r := recover(); r != nil {
            log.Error("gin catch error", log.Any("error", r))
            c.JSON(http.StatusOK, response.FailMsg("系统内部错误"))
        }
    }()
    c.Next()
}

panic 恢复:捕获路由处理函数中可能发生的 panic,记录错误日志

WebSocket 实现 (RunSocket)

var upGrader = websocket.Upgrader{
    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 <- client
    
    // 启动读写协程
    go client.Read()
    go client.Write()
}
  1. WebSocket 升级
    • 使用 gorilla/websocket 包的 Upgrader 将 HTTP 连接升级为 WebSocket 连接
    • CheckOrigin 返回 true 允许所有跨域连接(生产环境应做限制)
  2. 客户端管理
    • 通过 user 查询参数识别用户,创建客户端对象,包含 WebSocket 连接和消息通道
    • 通过注册通道将客户端注册到中央服务器。
  3. 并发处理:为每个客户端启动独立的读 (client.Read()) 和写 (client.Write()) 协程,实现全双工通信。

代码地址:IM-Go

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2396613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MYSQL MGR高可用

1&#xff0c;MYSQL MGR高可用是什么 简单来说&#xff0c;MySQL MGR 的核心目标就是&#xff1a;确保数据库服务在部分节点&#xff08;服务器&#xff09;发生故障时&#xff0c;整个数据库集群依然能够继续提供读写服务&#xff0c;最大限度地减少停机时间。 2. 核心优势 v…

阿里通义实验室突破空间音频新纪元!OmniAudio让360°全景视频“声”临其境

在虚拟现实和沉浸式娱乐快速发展的今天&#xff0c;视觉体验已经远远不够&#xff0c;声音的沉浸感成为打动用户的关键。然而&#xff0c;传统的视频配音技术往往停留在“平面”的音频层面&#xff0c;难以提供真正的空间感。阿里巴巴通义实验室&#xff08;Qwen Lab&#xff0…

异步上传石墨文件进度条前端展示记录(采用Redis中String数据结构实现-苏东坡版本)

昔者&#xff0c;有客临门&#xff0c;亟需自石墨文库中撷取卷帙若干。此等文册&#xff0c;非止一卷&#xff0c;乃累牍连篇&#xff0c;亟需批量转置。然吾辈虑及用户体验&#xff0c;当效东坡"腹有诗书气自华"之雅意&#xff0c;使操作如行云流水&#xff0c;遂定…

处理知识库文件_编写powershell脚本文件_批量转换其他格式文件到pdf文件---人工智能工作笔记0249

最近在做部门知识库&#xff0c;选用的dify&#xff0c;作为rag的工具&#xff0c;但是经过多个对比&#xff0c;最后发现&#xff0c; 比较好用的是&#xff0c;纳米搜索&#xff0c;但是可惜纳米搜索无法在内网使用&#xff0c;无法把知识库放到本地&#xff0c;导致 有信息…

rtpmixsound:实现音频混音攻击!全参数详细教程!Kali Linux教程!

简介 一种将预先录制的音频与指定目标音频流中的音频&#xff08;即 RTP&#xff09;实时混合的工具。 一款用于将预先录制的音频与指定目标音频流中的音频&#xff08;即 RTP&#xff09;实时混合的工具。该工具创建于 2006 年 8 月至 9 月之间。该工具名为 rtpmixsound。它…

【计算机网络】第3章:传输层—面向连接的传输:TCP

目录 一、PPT 二、总结 TCP&#xff08;传输控制协议&#xff09;详解 1. 概述 核心特性&#xff1a; 2. TCP报文段结构 关键字段说明&#xff1a; 3. TCP连接管理 3.1 三次握手&#xff08;建立连接&#xff09; 3.2 四次挥手&#xff08;终止连接&#xff09; 4. 可…

《信号与系统》--期末总结V1.0

《信号与系统》–期末总结V1.0 学习链接 入门&#xff1a;【拯救期末】期末必备&#xff01;8小时速成信号与系统&#xff01;【拯救期末】期末必备&#xff01;8小时速成信号与系统&#xff01;_哔哩哔哩_bilibili 精通&#xff1a;2022浙江大学信号与系统&#xff08;含配…

mac笔记本如何快捷键截图后自动复制到粘贴板

前提&#xff1a;之前只会进行部分区域截图操作&#xff08;commandshift4&#xff09;操作&#xff0c;截图后发现未自动保存在剪贴板&#xff0c;还要进行一步手动复制到剪贴板的操作。 mac笔记本如何快捷键截图后自动复制到粘贴板 截取 Mac 屏幕的一部分并将其自动复制到剪…

高考加油!UI界面生成器!

这个高考助力标语生成器具有以下特点&#xff1a; 视觉设计&#xff1a;采用了蓝色为主色调&#xff0c;搭配渐变背景和圆形装饰元素&#xff0c;营造出宁静而充满希望的氛围&#xff0c;非常适合高考主题。 标语生成&#xff1a;内置了超过 100 条精心挑选的高考加油标语&a…

window ollama部署模型

注意去官网下载ollama,这个win和linux差别不大,win下载exe,linux用官网提供的curl命令 模型下载表:deepseek-r1 使用命令:Ollama API 交互 | 菜鸟教程 示例: 1.查看已加载模型: 2.文本生成接口 curl -X POST http://localhost:11434/v1/completions -H "Conte…

用mediamtx搭建简易rtmp,rtsp视频服务器

简述&#xff1a; 平常测试的时候搭建rtmp服务器很麻烦&#xff0c;这个mediamtx服务器&#xff0c;只要下载就能运行&#xff0c;不用安装、编译、配置等&#xff0c;简单易用、ffmpeg推流、vlc拉流 基础环境&#xff1a; vmware17&#xff0c;centos10 64位&#xff0c;wi…

ubuntu安装devkitPro

建议开个魔法 wget https://apt.devkitpro.org/install-devkitpro-pacman chmod x ./install-devkitpro-pacman sudo ./install-devkitpro-pacman&#xff08;下面这句如果报错也没事&#xff09; sudo ln -s /proc/self/mounts /etc/mtab往~.bashrc添加 export DEVKITPRO/o…

Linux(10)——第二个小程序(自制shell)

目录 ​编辑 一、引言与动机 &#x1f4dd;背景 &#x1f4dd;主要内容概括 二、全局数据 三、环境变量的初始化 ✅ 代码实现 四、构造动态提示符 ✅ 打印提示符函数 ✅ 提示符生成函数 ✅获取用户名函数 ✅获取主机名函数 ✅获取当前目录名函数 五、命令的读取与…

代码随想录算法训练营 Day59 图论Ⅸ dijkstra优化版 bellman_ford

图论 题目 47. 参加科学大会&#xff08;第六期模拟笔试&#xff09; 改进版本的 dijkstra 算法&#xff08;堆优化版本&#xff09; 朴素版本的 dijkstra 算法解法的时间复杂度为 O ( n 2 ) O(n^2) O(n2) 时间复杂度与 n 有关系&#xff0c;与边无关系 类似于 prim 对应点多…

【HW系列】—安全设备介绍(开源蜜罐的安装以及使用指南)

文章目录 蜜罐1. 什么是蜜罐&#xff1f;2. 开源蜜罐搭建与使用3. HFish 开源蜜罐详解安装步骤使用指南关闭方法 总结 蜜罐 1. 什么是蜜罐&#xff1f; 蜜罐&#xff08;Honeypot&#xff09;是一种主动防御技术&#xff0c;通过模拟存在漏洞的系统或服务&#xff08;如数据库…

汽车总线分析总结(CAN、LIN、FlexRay、MOST、车载以太网)

目录 一、汽车总线技术概述 二、主流汽车总线技术对比分析 1. CAN总线&#xff08;Controller Area Network&#xff09; 2. LIN总线&#xff08;Local Interconnect Network&#xff09; 3. FlexRay总线 4. MOST总线&#xff08;Media Oriented Systems Transport&#x…

MyBatisPlus--条件构造器及自定义SQL详解

条件构造器 在前面学习快速入门的时候&#xff0c;练习的增删改查都是基于id去执行的&#xff0c;但是在实际开发业务中&#xff0c;增删改查的条件往往是比较复杂的&#xff0c;因此MyBatisPlus就提供了一个条件构造器来帮助构造复杂的条件。 MyBatisPlus支持各种复杂的wher…

OVD开放词汇检测 Detic 训练COCO数据集实践

0、引言 纯视觉检测当前研究基本比较饱和&#xff0c;继续创新提升空间很小&#xff0c;除非在CNN和transformer上提出更强基础建模方式。和文本结合是当前的一大趋势&#xff0c;也是计算机视觉和自然语言处理结合的未来趋势&#xff0c;目前和文本结合的目标检测工作还是有很…

docker、ctr、crictl命令简介与使用

概述 在使用k3s过程中&#xff0c;经常需要使用ctr和crictl两个命令&#xff0c;本文记录一下。 ctr 类似docker命令是docker-shim容器运行时的客户端工具&#xff0c;ctr是Containerd的客户端工具。一个简单的CLI接口&#xff0c;用作Containerd本身的一些调试用途&#xf…

WEB3——什么是ABI

怎么获得ABI&#xff1f; 在编译完合约后&#xff0c;可以在左边下面点击复制ABI ABI&#xff08;Application Binary Interface&#xff0c;应用二进制接口&#xff09;是用来让前端或服务端 JavaScript 代码与智能合约进行交互的桥梁&#xff0c;它描述了合约的函数、事件和…