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

Gin框架入门(2)--异常捕获与日志实现

异常捕获

Go语言的异常捕获采用的是延迟处理的方法实现的,实际上就是利用defer,panic和recover三个关键字和函数来实现的。

关键字

defer关键字(函数)

这个关键字在控制语句中就有所涉及,本质上是采用一个栈的存储结构,在整个函数执行完之后,再启用这个栈,依次执行这个函数。需要注意的是以下几点:

  1. 由于是栈的结构,我们首先想到的便是先入后出,defer也是如此

    func f(){
    defer A
    defer B
    D
    defer C}
    

    执行的顺序是D,C,B,A

  2. 被defer标记的语句是放在最后执行的,所以也就衍生出defer的玩法——放在开头加一个打印语句,来判断go程的结束(原理如此,至于稍复杂的结合等待组或者响应等也大同小异)

panic函数

让程序直接崩溃的函数,用法panic("<报错信息>")

Recover

Recover只在延时函数中有效,效果是将崩溃的程序恢复过来;如果是在正常的语句中使用Recover,就会返回一个nil并且没有任何效果。

实例(没意义):

func main() {defer func() {if err := recover(); err != nil {fmt.Println("捕获异常:", err)}}()r := router.Router()r.Run(":9999")panic("雪豹毁了我的程序")}

运行结果:

服务端正常运行!

实例:

使用这个方法,使得程序不崩溃的情况下获得异常,并且保证只是使得前端无返回内容,而引起这个服务器的崩溃

改写user.go

func (u UserController) GetList(c *gin.Context) {//ReturnError(c *gin.Context, code int, msg string)defer func() {if err := recover(); err != nil {fmt.Println("捕获异常:", err)}}()num1, num2 := 1, 0num3 := num1 / num2ReturnUserGetListError(c, 404, num3)//<common.go>//func ReturnUserGetListError(c *gin.Context, code int, msg int) {//	json := &JsonErrStruct{Code: code, Msg: msg}//	c.JSON(http.StatusOK, json)//}}

运行结果

捕获异常: runtime error: integer divide by zero
[GIN] 2024/09/22 - 20:19:09 | 200 |            0s |       127.0.0.1 | POST     "/user/list"

在这里插入图片描述

日志

日志就是记录事件的记录表,主要分为以下三种

  1. 项目的请求日志
  2. 程序出现错误日志
  3. 程序员开发的时候自己保存的日志

封装保存日志的包

  1. 首先,创建一个pkg包,这个包的用途是防止开发时需要的工具。在pkg包下面创建一个子文件夹logger,用来存放日志工具,在这个子文件夹下创建logger.go文件
  2. 之后,导入一下我们将要使用的日志工具包,在终端命令行输入go get github.com/sirupsen/logrus

文件结构

中间件logger.go内容
在这里插入图片描述

package loggerimport ("fmt""github.com/gin-gonic/gin""github.com/sirupsen/logrus""io""os""path""path/filepath""runtime/debug""time"
)// 初始化日志设置
func init() {// 设置日志的 JSON 格式logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05",})logrus.SetReportCaller(false)
}// 写入程序员自定义的日志
func Write(msg string, filename string) {setOutPutFile(logrus.InfoLevel, filename)logrus.Info(msg)
}// Debug 级别日志
func Debug(fields logrus.Fields, args ...interface{}) {setOutPutFile(logrus.DebugLevel, "debug")logrus.WithFields(fields).Debug(args)
}// Info 级别日志
func Info(fields logrus.Fields, args ...interface{}) {setOutPutFile(logrus.InfoLevel, "info")logrus.WithFields(fields).Info(args)
}// Warn 级别日志
func Warn(fields logrus.Fields, args ...interface{}) {setOutPutFile(logrus.WarnLevel, "warn")logrus.WithFields(fields).Warn(args)
}// Fatal 级别日志
func Fatal(fields logrus.Fields, args ...interface{}) {setOutPutFile(logrus.FatalLevel, "fatal")logrus.WithFields(fields).Fatal(args)
}// Error 级别日志
func Error(fields logrus.Fields, args ...interface{}) {setOutPutFile(logrus.ErrorLevel, "error")logrus.WithFields(fields).Error(args)
}// Panic 级别日志
func Panic(fields logrus.Fields, args ...interface{}) {setOutPutFile(logrus.PanicLevel, "panic")logrus.WithFields(fields).Panic(args)
}// Trace 级别日志
func Trace(fields logrus.Fields, args ...interface{}) {setOutPutFile(logrus.TraceLevel, "trace")logrus.WithFields(fields).Trace(args)
}// 设置日志输出文件 在各个函数方法中调用
func setOutPutFile(level logrus.Level, logName string) {// 创建日志目录logDir := "./runtime/log"if _, err := os.Stat(logDir); os.IsNotExist(err) {err = os.MkdirAll(logDir, 0777)if err != nil {panic(fmt.Errorf("create log dir '%s' error: %s", logDir, err))}}// 获取当前日期字符串timeStr := time.Now().Format("2006-01-02")fileName := filepath.Join(logDir, logName+"_"+timeStr+".log")// 打开日志文件,如果不存在则创建var err erroros.Stderr, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)if err != nil {fmt.Println("open log file err:", err)}// 设置日志输出到文件logrus.SetOutput(os.Stderr)logrus.SetLevel(level)return
}// 创建了success开头的文件 在logger中以中间件的形式调用
func LoggerToFile() gin.LoggerConfig {logDir := "./runtime/log"if _, err := os.Stat(logDir); os.IsNotExist(err) {err = os.MkdirAll(logDir, 0777)if err != nil {panic(fmt.Errorf("create log dir '%s' error: %s", logDir, err))}}// 获取当前日期字符串timeStr := time.Now().Format("2006-01-02")fileName := path.Join(logDir, "success_"+timeStr+".log")os.Stdout, _ = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)var conf = gin.LoggerConfig{Formatter: func(param gin.LogFormatterParams) string {return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",param.TimeStamp.Format(time.RFC1123),param.ClientIP,param.Method,param.Path,param.Request.Proto,param.StatusCode,param.Latency,param.Request.UserAgent(),param.ErrorMessage,)},Output: io.MultiWriter(os.Stdout, os.Stderr),}return conf
}// 将报错放在报文中返回回来 在logger中以中间件的形式调用
func Recover(c *gin.Context) {defer func() {if err := recover(); err != nil {if _, errDir := os.Stat("./runtime/log"); os.IsNotExist(errDir) {errDir := os.MkdirAll("./runtime/log", 0777)if errDir != nil {panic(fmt.Errorf("create log dir '%s' error: %s", "./runtime/log", err))}}timeStr := time.Now().Format("2006-01-02")//文件名fileName := path.Join("./runtime/log", timeStr+".log")f, errFile := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)if errFile != nil {fmt.Println(errFile)}timeFileStr := time.Now().Format("2006-01-02 15:04:05")//写入信息f.WriteString("panic error tome:" + timeFileStr + "\n")f.WriteString(fmt.Sprintf("%v", err) + "\n")f.WriteString("stacktrace from panic" + string(debug.Stack()) + "\n")f.Close()c.JSON(200, gin.H{"code": 500,"msg":  fmt.Sprintf("%v", err),})//终止后续接口调用,不加入recover到异常之后,还会继续执行接口中的后续代码c.Abort()}}()c.Next()
}

可以结合注释简单了解以下,然后cv使用即可

调用方式

对于前两种:项目的请求日志(LoggerToFile)和 程序出现错误日志(Recover)而言,调用的方式是在路由(router.go)的函数中以中间件的方式调用

//日志r.Use(gin.LoggerWithConfig(logger.LoggerToFile()))r.Use(logger.Recover)

而对于 程序员开发的时候自己保存的日志(setOutPutFile),则是以方法的形式在想要记录的函数(方法)中调用logger.Write("日志信息", "user")

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【系统架构设计师】论文模板:快速写好一篇架构设计师论文
  • Flutter局域网广播(UDP通信)与TCP通信
  • kafka 消息位移提交几种方式:消息重复消息、消息丢失的关键
  • C++ | Leetcode C++题解之第415题字符串相加
  • Go-知识-定时器
  • KTH5762系列 低功耗、高精度 3D 霍尔角度传感器 电子手表旋钮应用
  • 【对比学习串烧】 SimSiam MoCov3 DINO
  • nacos和eureka的区别
  • java(3)数组的定义与使用
  • 数值实验作业(第一章)
  • 鸿蒙OpenHarmony【轻量系统内核扩展组件(动态加载)】子系统开发
  • Python青少年简明教程目录
  • 混合开发应用侧-JSBridge,在加载的网页中调用原生能力
  • ARM驱动学习之7 驱动模块传参数
  • CentOS入门宝典:从零到一构建你的Linux服务器帝国
  • python3.6+scrapy+mysql 爬虫实战
  • [数据结构]链表的实现在PHP中
  • 30天自制操作系统-2
  • iOS 系统授权开发
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 读懂package.json -- 依赖管理
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 机器学习学习笔记一
  • 开发基于以太坊智能合约的DApp
  • 力扣(LeetCode)21
  • 算法之不定期更新(一)(2018-04-12)
  • 通信类
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • ​第20课 在Android Native开发中加入新的C++类
  • #每日一题合集#牛客JZ23-JZ33
  • #职场发展#其他
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (3)选择元素——(17)练习(Exercises)
  • (C语言)球球大作战
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (利用IDEA+Maven)定制属于自己的jar包
  • (亲测有效)推荐2024最新的免费漫画软件app,无广告,聚合全网资源!
  • (三)Kafka 监控之 Streams 监控(Streams Monitoring)和其他
  • (三十五)大数据实战——Superset可视化平台搭建
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (转) Face-Resources
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • ./configure,make,make install的作用
  • ./和../以及/和~之间的区别
  • .env.development、.env.production、.env.staging
  • .gitignore文件—git忽略文件
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .NET C# 使用GDAL读取FileGDB要素类
  • .Net Core中Quartz的使用方法