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")

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙Harmony应用开发,数据驾驶舱登录页面的实现

鸿蒙Harmony应用开发&#xff0c;数据驾驶舱登录页面的实现 ​ 首先我们有个Splash 过渡页面来判断当前是用户是否登录&#xff0c;我们先从preferences中获取token是否存在。如果不存在直接跳转登录即可&#xff0c;如果存在的情况我们再去获取下用户的信息看看token是否过期…

MySQL---创建数据库(基于SQLyog)

目录 0.前言 1.基本认识 1.1编码集 1.2检验规则 2.库的创建和销毁 2.1指令介绍 2.2你可能会出现的问题 3.查看数据库属性 4.创建指定数据库 5.创建表操作 0.前言 之前写过一篇这个关于表的创建和销毁的操作&#xff0c;但是当时是第一次学习&#xff0c;肯定有些地方…

Docker的安装和使用

Docker概述 Docker简介 Docker 是基于 Go 语言实现的云开源项目。 Docker 的主要目标是&#xff1a; Build, Ship and Run Any App, Anywhere &#xff0c;也就是通过对应用组件的封装、 分发、部署、运行等生命周期的管理&#xff0c;使用户的 APP 及其运行环境能做到 一次…

【Python】 已解决:ModuleNotFoundError: No module named…

个人简介&#xff1a;某不知名博主&#xff0c;致力于全栈领域的优质博客分享 | 用最优质的内容带来最舒适的阅读体验&#xff01;文末获取免费IT学习资料&#xff01; &#x1f345; 文末获取更多信息 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅收藏 &#x…

Qt clicked()、clicked(bool)、toggled(bool)信号的区别和联系

clicked() 信号 所属控件&#xff1a;clicked()信号是QAbstractButton类&#xff08;及其子类&#xff0c;如QPushButton、QRadioButton、QCheckBox等&#xff09;的一个信号。clicked信号可以说是许多控件&#xff08;特别是按钮类控件&#xff0c;如QPushButton&#xff09;…

linux下共享内存的3种使用方式

进程是资源封装的单位&#xff0c;内存就是进程所封装的资源的一种。一般情况下&#xff0c;进程间的内存是相互隔离的&#xff0c;也就是说一个进程不能访问另一个进程的内存。如果一个进程想要访问另一个进程的内存&#xff0c;那么必须要进过内核这个桥梁&#xff0c;这就是…

Linux基础3-基础工具4(git),冯诺依曼计算机体系结构

上篇文章&#xff1a;Linux基础3-基础工具3&#xff08;make,makefile,gdb详解&#xff09;-CSDN博客 本章重点&#xff1a; 1. git简易使用 2. 冯诺依曼计算机体系结构介绍 目录 一. git使用 1.1 什么是git? 1.2 git发展史 1.3 git创建仓库 1.4 git命令操作 二. 冯诺依…

1.3 计算机网络的分类

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言一、按分布范围分类二、按传输技术分类三、按拓扑结构分类四、按使用者分类五、按传输介质分类 前言 计算机网络根据不同的标准可以被分为多种类型&#xff0c;本章从分布…

Flink系列知识之:Checkpoint原理

Flink系列知识之&#xff1a;Checkpoint原理 在介绍checkpoint的执行流程之前&#xff0c;需要先明白Flink中状态的存储机制&#xff0c;因为状态对于检查点的持续备份至关重要。 State Backends分类 下图显示了Flink中三个内置的状态存储种类。MemoryStateBackend和FsState…

二叉搜索树(BSTree)原理及应用场景

目录 引言 二叉搜索树的基本概念 常见算法 插入节点 查找节点 删除节点 二叉搜索树的应用场景 1. 数据库索引 2. 符号表 3. 字典和词汇表 4. 动态集合 结论 引言 二叉搜索树&#xff08;Binary Search Tree, BST&#xff09;是一种特殊的二叉树&#xff0c;其每个节…

JavaEE: 深入探索TCP网络编程的奇妙世界(五)

文章目录 TCP核心机制TCP核心机制六: 拥塞控制为什么要有拥塞控制?动态调整的拥塞控制拥塞控制中,窗口大小具体的变化过程 TCP核心机制七: 延时应答TCP核心机制八: 捎带应答 TCP核心机制 前一篇文章 JavaEE: 深入探索TCP网络编程的奇妙世界(四) 书接上文~ TCP核心机制六: 拥…

Ubuntu20.04 搜索不到任何蓝牙设备

电脑信息 联想扬天YangTianT4900k 问题描述 打开蓝牙之后&#xff0c;一直转圈&#xff0c;搜索不到任何蓝牙设备 排查 dmesg | grep -i blue 有如下错误&#xff1a; Bluetooth: hci0: RTL: unknown IC info, lmp subver 8852, hci rev 000b, hci ver 000b lsusb 芯片型号如…

spark读取数据性能提升

1. 背景 spark默认的jdbc只会用单task读取数据&#xff0c;读取大数据量时&#xff0c;效率低。 2. 解决方案 根据分区字段&#xff0c;如日期进行划分&#xff0c;增加task数量提升效率。 /*** 返回每个task按时间段划分的过滤语句* param startDate* param endDate* param …

每日学习一个数据结构-Trie树(字典树)

文章目录 定义节点结构根节点插入操作查找操作删除操作特点应用示例 “Trie”树&#xff0c;又称为前缀树或字典树&#xff0c;是一种专门用于存储字符串的数据结构。它在许多应用程序中都非常有用&#xff0c;特别是在那些需要高效查找、插入和删除字符串的应用场景中。下面是…

[项目:微服务即时通讯系统客户端(基于C++QT)]三,左侧界面搭建

三&#xff0c;左侧界面搭建 一&#xff0c;导入 先把MainWidget类做成“单例类” 采用的是单例模式&#xff0c;让某一个类&#xff0c;在指定进程中只有唯一的实例 先看一下MainWidget的框架 QWidget//这部分是头文件保护宏&#xff0c;确保该头文件只被包含一次&#x…

低级编程语言和高级编程语言

一.区分低级编程语言和高级编程语言的方法 1.低级编程语言 低级编程语言,并不是简单的编程语言,而是写起来很费事的编程语言,如所有编程语言的"祖宗":汇编语言,写起来极其麻烦,说不定一个 int a1; 它就得写好几行,甚至十几行 这样麻烦的编程语言为什么还没消失那,因…

基于微信小程序的家教信息管理系统的设计与实现(论文+源码)_kaic

摘 要 随着互联网时代的来临&#xff0c;使得传统的家教模式已不复存在&#xff0c;亟需一种方便、快捷的在线教学平台。因此&#xff0c;利用Java语言作为支撑和MySQL数据库存储数据&#xff0c;结合微信小程序的便利性&#xff0c;为用户开发出了一个更加人性化、方便的家庭…

超越sora,最新文生视频CogVideoX-5b模型分享

CogVideoX-5B是由智谱 AI 开源的一款先进的文本到视频生成模型&#xff0c;它是 CogVideoX 系列中的更大尺寸版本&#xff0c;旨在提供更高质量的视频生成效果。 CogVideoX-5B 采用了 3D 因果变分自编码器&#xff08;3D causal VAE&#xff09;技术&#xff0c;通过在空间和时…

ps证件照蓝底换白底

ps证件照蓝底换白底 1、打开 Photoshop&#xff0c;导入需要处理的照片。 2、左侧工具栏中选择“魔棒工具”&#xff0c;点击证件照的背景区域进行选择。 3、使用快捷键 Shift F5 或者从顶部菜单选择“编辑” -> “填充”&#xff0c;在弹出的对话框中选择“填充内容”中…

【全网最全】2024年华为杯研究生数学建模A题成品论文

您的点赞收藏是我继续更新的最大动力! 一定要点击如下的卡片&#xff0c;那是获取资料的入口&#xff01; 点击链接获取群聊【2024华为杯研赛资料汇总】&#xff1a;https://qm.qq.com/q/yB6JDUTaWAhttps://qm.qq.com/q/yB6JDUTaWAA题第一问是关于如何建立一个低复杂度模型&a…