8.统一异常处理 + 统一记录日志

news2024/11/23 21:12:28

目录

1.统一异常处理

2.统一记录日志


1.统一异常处理


在 HomeController 类中添加请求方法(服务器发生异常之后需要统一处理异常,记录日志,然后转到 500 页面,需要人工处理重定向到 500 页面,提前把 500 页面请求访问配置)

    @RequestMapping(path = "/error", method = RequestMethod.GET)
    public String getErrorPage() {
        return "/error/500";
    }

在 controller 类下新建 advice 包,创建 ExceptionAdvice 类:

  • 添加注解 @ControllerAdvice 统一处理异常:此时这个组件会扫面所有的 Bean,做一个限制 @ControllerAdvice(annotations = Controller.class):这个组件只去扫面带有 Controller 注解的 Bean
  • 添加所有方法处理所有错误情况:添加注解 @ExceptionHandler 表示这个方法处理所有异常的方法—— @ExceptionHandler({Exception.class})
  • 通常参数为 Exception e, HttpServletRequest request, HttpServletResponse response
  • 注入日志,并且把异常记录日志,想要把异常非常详细的栈的信息记录:遍历栈的信息得到的是数组(每次遍历得到一条异常信息,打印日志)
  • 然后给浏览器响应(重定向到错误页面)
  • 这时候还需要判断这个请求是普通请求还是异步请求:浏览器访问服务器可能是普通请求(希望返回网页,然后重定向到 500)也可能是异步请求(希望返回 JSON,不可以返回到页面 HTML)
  • 通过 request.getHeader("x-requested-with") 获取请求返回 String:如果返回值等于 "XMLHttpRequest"则表示为异步请求,这个请求是以 XML 的形式访问,希望返回 XML,只有异步请求才希望返回 XML(普通请求返回 HTML)然后响应字符串("application/plain;charset=utf-8":向浏览器返回普通字符串,可以是 JSON 格式,需要人为的将字符串转化为 JS 对象),获取输出流输出JSON字符串;如果是普通请求,重定向到错误页面(获取项目访问路径 + "/error" 路径)
package com.example.demo.controller.advice;

import com.example.demo.util.CommunityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

//添加注解 @ControllerAdvice 统一处理异常:此时这个组件会扫面所有的 Bean,
//做一个限制 @ControllerAdvice(annotations = Controller.class):这个组件只去扫面带有 Controller 注解的 Bean
@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);

    //添加所有方法处理所有错误情况:添加注解 @ExceptionHandler 表示这个方法处理所有异常的方法
    @ExceptionHandler({Exception.class})
    public void handleException(Exception e, HttpServletRequest request,
                                HttpServletResponse response) throws IOException {
        //注入日志,并且把异常记录日志,想要把异常非常详细的栈的信息记录
        // 遍历栈的信息得到的是数组(每次遍历得到一条异常信息,打印日志)
        logger.error("服务器发生异常: " + e.getMessage());
        for (StackTraceElement element : e.getStackTrace()) {
            logger.error(element.toString());
        }

        //然后给浏览器响应(重定向到错误页面)
        //这时候还需要判断这个请求是普通请求还是异步请求:浏览器访问服务器可能是普通请求(希望返回网页,然后重定向到 500)
        // 也可能是异步请求(希望返回 JSON,不可以返回到页面 HTML)
        //通过 request.getHeader("x-requested-with") 获取请求返回 String:如果返回值等于 "XMLHttpRequest"则表示为异步请求,
        // 这个请求是以 XML 的形式访问,希望返回 XML,只有异步请求才希望返回 XML(普通请求返回 HTML)
        String xRequestedWith = request.getHeader("x-requested-with");
        if ("XMLHttpRequest".equals(xRequestedWith)) {
            // 然后响应字符串("application/plain;charset=utf-8":向浏览器返回普通字符串
            //可以是 JSON 格式,需要人为的将字符串转化为 JS 对象)
            response.setContentType("application/plain;charset=utf-8");
            //获取输出流输出JSON字符串;如果是普通请求,重定向到错误页面(获取项目访问路径 + "/error" 路径)
            PrintWriter writer = response.getWriter();
            writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));
        } else {
            response.sendRedirect(request.getContextPath() + "/error");
        }
    }
}

2.统一记录日志

  • 可不可以利用控制器通知统一处理?控制器通知在发生异常时可以统一处理,而统一记录日志并不是发生异常才记录
  • 可不可以使用拦截器?拦截器也是针对控制器做处理,而记录日志并不是只有在控制器中记录(目前我们对业务组件做日志记录)
  • 使用传统方法:把记录日志的内容封装到组件中,在不同的业务组件(service)方法中调用(比如在一开始记录日志,在写也业务方法之前记录日志)。这种方法也有弊端:业务组件方法是处理业务,但是在之前添加日志需求(不是业务需求,是系统需求),所以在业务方法中耦合了系统需求是有弊端的,例如现在系统需求发生变化,不想在业务之前记录日志,而是在之后记录日志,改动比较大,很麻烦
  • 解决问题的最好方法:将记录日志(系统需求)单独实现——使用 AOP,单独定义一个组件,不和业务组件发生直接关系,将业务组件通用逻辑封装在这个组件中

对于 AOP 的知识可以阅读之前学习的 AOP 博客:Spring AOP-CSDN博客

在 pom.xml 添加依赖:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

对所有业务组件记录日志(在业务组件一开始记录日志),新建 aspect 包创建 ServiceLogAspect 类(存放切面主键):

  • 添加注解 @Component、@Aspect
  • 实例化 Logger
  • 声明切点:所有业务组件都去处理
  • 使用前置通知在业务组件一开始记录日志
  • 记录格式:用户[1.2.3.4],在[xxx],访问了[com.example.demo.service.xxx()].
  • 用户 ip 通过 request 获取,获取 request:RequestContextHolder.getRequestAttributes();
  • 拼接时间:new Date,然后实例化
  • 访问某个类某个方法(类名 + 方法名):给方法添加 JoinPoint 连接点参数,连接点指代程序植入的目标方法
  • 最后再进行全部拼接
package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;

@Component
@Aspect
public class ServiceLogAspect {
    //实例化 Logger
    private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);

    //声明切点:所有业务组件都去处理
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void pointcut() {

    }
    //使用前置通知在业务组件一开始记录日志
    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        //记录格式:用户[1.2.3.4],在[xxx],访问了[com.example.demo.service.xxx()].

        //用户 ip 通过 request 获取,获取 request:RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getRemoteHost();
        //拼接时间:new Date,然后实例化
        String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        //访问某个类某个方法(类名 + 方法名):给方法添加 JoinPoint 连接点参数,连接点指代程序植入的目标方法
        String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        //全部拼接
        logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
    }
}

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

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

相关文章

Containerd Container管理功能解析

Containerd Container管理功能解析 container是containerd的一个核心功能&#xff0c;用于创建和管理容器的基本信息。 本篇containerd版本为v1.7.9。 更多文章访问 https://www.cyisme.top 本文从ctr c create命令出发&#xff0c;分析containerd的容器及镜像管理相关功能。 …

01 项目架构

关于我 曾经就职于蚂蚁金服&#xff0c;多年的后端开发经验&#xff0c;对微服务、架构这块研究颇深&#xff0c;同时也是一名热衷于技术分享、拥抱开源技术的博主。 个人技术公众号&#xff1a;码猿技术专栏个人博客&#xff1a;www.java-family.cn 前期一直在更新《Spring…

什么是美颜sdk?视频直播美颜sdk技术深度剖析

美颜sdk可以通过实时处理图像&#xff0c;提升主播或用户在视频直播中的外观。通过美颜sdk接口调用可以轻松实现美颜效果。美颜sdk的核心目标是在保持图像真实性的同时&#xff0c;为用户创造出最理想的美化效果。 一、美颜sdk的技术实现 1.面部识别技术&#xff1a;美颜sdk…

虚拟直播在文旅行业火爆发展!背后的“生意经”你抓住了吗?

&#xfeff;自疫情结束以来&#xff0c;文化和旅游行业恢复势头强劲&#xff0c;各大旅游景点消费活跃度持续攀升。在这种情况下&#xff0c;“直播文旅”模式的深度融合对文旅行业的客流导入起到了极大的带动作用。 不过&#xff0c;当前的文旅直播也出现了一些问题&#xf…

流媒体播放器EasyPlayer播放H.265与H.264时进度条样式异常该如何解决?

H5无插件流媒体播放器EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;可支持H.264与H.265编码格式&#xff0c;性能稳定、播放流畅&#xff0c;能支持WebSocket-FLV、HTTP-FLV&#xff0c;HLS&#xff08;m3u8&#…

北京 | 竹云与南方电网携手荣获“IDC 2023未来企业奖未来连接领导者”

11月22日-23日&#xff0c;2023第八届IDC中国数字化转型年度盛典在北京召开。本次大会以“竞放数字力量”为主题&#xff0c;汇聚超过1000位来自不同行业的大咖与伙伴共同参与此次盛会&#xff0c;从全球化视角出发&#xff0c;围绕本土化落地人工智能&#xff08;大模型&#…

pytest系列——pytest_collection_modifyitems钩子函数修改测试用例执行顺序

前言 pytest默认执行用例是根据项目下的文件名称按ascii码去收集运行的&#xff1b;文件中的用例是从上往下按顺序执行的。 pytest_collection_modifyitems 这个函数顾名思义就是收集测试用例、改变用例的执行顺序的。 【严格意义上来说&#xff0c;我们在用例设计原则上用例…

NFTScan | 11.20~11.26 NFT 市场热点汇总

欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期&#xff1a;2023.11.20~ 2023.11.26 NFT Hot News 01/ OKX Ordinals 市场 API 完成升级 11 月 21 日&#xff0c;OKX Ordinals 市场 API 现已完成升级&#xff0c;新增支持按币种单价查询、排序&…

TDA4VM EVM开发板调试笔记

文章目录 1. 前言2. 官网资料导读3. 安装 Linux SDK4. 制作SD 启动卡5. 验证启动1. 前言 TDA4作为一般经典的车规级SOC芯片,基于它的低阶智驾方案目前成为各家智驾方案公司的量产首选,这也使得基于TDA4的开发需求陡增,开发和使用TDA4既要熟悉Linux驱应用开发,还要熟悉传统…

uniapp基础-教程之HBuilderX配置篇-01

uniapp教程之HBuilderX配置篇-01 为什么要做这个教程的梳理&#xff0c;主要用于自己学习和总结&#xff0c;利于增加自己的积累和记忆。首先下载HBuilderX&#xff0c;并保证你的软件在C盘进行运行&#xff0c;最好使用英文或者拼音&#xff0c;这个操作是为了保证软件的稳定…

瑞芯微RK3588核心板在网络录像机产品中的应用与优势-迅为电子

你是否曾经想过&#xff0c;网络录像机是如何工作的&#xff1f;为什么它可以在没有电脑的情况下进行录像和监控&#xff1f;答案是&#xff0c;它依赖于RK3588核心板。那么&#xff0c;RK3588核心板到底在网络录像机产品中扮演着什么样的角色呢&#xff1f;它又具有哪些优势呢…

力扣:239. 滑动窗口最大值

题目&#xff1a; 给定一个数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口中的最大值。 提示&#xff1a; 1 < nums.length < 10^5-10^4 < n…

使用UI Automation库用于UI自动化测试

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

NX二次开发UF_CURVE_ask_spline_data 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_spline_data Defined in: uf_curve.h int UF_CURVE_ask_spline_data(tag_t spline_tag, UF_CURVE_spline_p_t spline_data ) overview 概述 Reads the spline data a…

FreeRTOS-任务管理

目录 任务管理 创建任务 创建任务示例1&#xff1a;创建两个同等级的任务 创建任务示例2&#xff1a;使用任务参数 删除任务 删除任务示例&#xff1a;删除任务 挂起任务 任务优先级 优先级实验&#xff1a;修改优先级 Tick 延时函数 延时示例 空闲任务 钩子函数…

创意二维码案例:意大利艺术家的最新二维码艺术展!

意大利艺术家——米开朗基罗皮斯特莱托&#xff08;Michelangelo Pistoletto&#xff09;的个人艺术展“二维码‘说’”&#xff08;QR CODE POSSESSION&#xff09;正在北京798艺术区的常青艺术画廊展出&#xff0c;这是一次别出心裁的创意艺术展&#xff01; 主要体现在3个方…

案例036:基于微信小程序的在线课堂系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

2019年3月28日 Go生态洞察:Go 2018调查结果分析

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

数据结构与算法编程题29

先序遍历二叉树&#xff08;非递归&#xff09;栈 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 #define Maxsize 100 #define STR_SIZE 1024typedef struct BiTNode {ElemType data;B…

BetaFlight模块设计之三十七:SBUS

BetaFlight模块设计之三十七&#xff1a;SBUS 1. 源由2. sbus启动&动态任务3. 主要函数3.1 sbus初始化3.2 sbusFrameStatus更新3.3 rxFrameTimeUs3.4 sbusDataReceive接收数据 4. 辅助函数4.1 sbusChannelsDecode 5. 参考资料 1. 源由 接着BetaFlight模块设计之三十六&…