Java 项目创建 -- 统一结果处理、统一异常处理、统一日志处理
一、IDEA 插件使用
1、说明
此处使用 SpringBoot 2.2.6 、JDK 1.8 、mysql 8.0.18 作为演示。
使用 IDEA 作为开发工具。
2、IDEA 插件 -- Lombok
(1)简介
Lombok 能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString 等方法。
比如在实体类上使用 @Data 注解,就可以省去 getter、 setter 等方法的编写,但是在编译生成的字节码文件中有getter和setter方法。
(2)安装
Settings -> Plugins,搜索 Lombok。点击 install 安装后重启 IDEA 即可。
(3)使用
Step1:
使用 maven 添加依赖,引入对应的 jar 包。
org.projectlombok lombok 1.18.12
Step2:
使用各种注解,简化代码编写。比如 @Data。
3、IDEA 插件 -- Easy Code
(1)简介
Easy Code 是 IDEA 的一个插件,可以直接对数据的表生成 entity, controller, service, dao, mapper 等代码,无需任何编码,简单而强大。
(2)安装
Settings -> Plugins,搜索 EasyCode。点击 install 安装后重启 IDEA 即可。
(3)使用
Step1:
创建一个 SpringBoot 项目。
Step2:
连接数据库,并创建一个 数据表,用于测试。
DROP DATABASE IF EXISTS test; CREATE DATABASE test; USE test; CREATE TABLE emp( id int PRIMARY KEY auto_increment, name varchar(50), salary decimal(10, 2), age int, email varchar(50) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO emp VALUES(null, "tom", 6000, 20, "tom@163.com");
Step3:
右键表名,选择 EasyCode -> Generate Code,生成代码。
选择代码生成的路径 以及 包名。
自动创建需要的文件。
Step4:
当然这样并不能直接使用,还得添加相关依赖、注解、以及数据库连接信息才能使用。
添加依赖:
比如: lombok、mybatis-plus、mysql-connector-java 以及 web 启动类。
<dependency> <groupId>org.projectlombokgroupId> <artifactId>lombokartifactId> <version>1.18.12version> <scope>providedscope> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> <dependency> <groupId>com.baomidougroupId> <artifactId>mybatis-plus-boot-starterartifactId> <version>3.3.1version> dependency> <!-- mysql --> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> <version>8.0.18version> dependency>
添加注解:
dao 层添加 @Mapper 注解,或者在启动类上添加 @MapperScan 注解扫描 dao 层。
entity 上可以添加 @Data 注解,用于简化 getter、setter 方法。
配置 yml 文件:
# 配置数据源 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/test # 配置 mybatis-plus mybatis-plus: mapper-location: classpath:/mapper/**/*.xml global-config: db-config: id-type: auto
Step5:启动项目,并访问 controller
4、使用 mybatis-plus 代码生成器生成代码
mybatis-plus 代码生成器: https://www.cnblogs.com/l-y-h/p/12859477.html#_label1_2
二、Java 项目构建 -- 统一结果处理
1、为什么使用统一结果?
大部分前后端项目采用 JSON 格式进行数据交互,定义一个统一的数据规范,有利于前后台的交互、以及信息处理。
2、数据格式?如何处理?代码实现?
(1)数据格式?
是否响应成功(success: true / false)
响应状态码(code:200 / 400 / 500 等)
状态码描述(message:访问成功 / 系统异常等)
响应数据(data:处理的数据)
【输出格式如下所示:】 { "success": true, "code": 200, "message": "查询用户列表", "data": { "itms": [ { "id": "1", "username": "admin", "role": "ADMIN", "createTime": "2020-4-24T15:32:29", "modifiedTime": "2020-4-24T15:41:40" },{ "id": "2", "username": "zhangsan", "role": "USER", "createTime": "2020-4-24T15:32:29", "modifiedTime": "2020-4-24T15:41:40" } ] } }
(2)如何处理?
success 设置成 Boolean 类型。
code 设置成 Integer 类型。
message 设置成 String 类型。
data 设置成 HashMap 类型。
构造器私有,且使用静态方法返回类对象。
采用链式调用(即方法返回对象为其本身,return this)。
(3)代码实现?
Step1:创建一个 统一结果处理类 Result 。
【代码实现如下:】 package com.lyh.common.util; import lombok.Data; import org.apache.http.HttpStatus; import java.util.HashMap; import java.util.Map; /** * 统一结果返回类。方法采用链式调用的写法(即返回类本身 return this)。 * 构造器私有,不允许进行实例化,但提供静态方法 ok、error 返回一个实例。 * 静态方法说明: * ok 返回一个 成功操作 的结果(实例对象)。 * error 返回一个 失败操作 的结果(实例对象)。 * * 普通方法说明: * success 用于自定义响应是否成功 * code 用于自定义响应状态码 * message 用于自定义响应消息 * data 用于自定义响应数据 * * 依赖信息说明: * 此处使用 @Data 注解,需导入 lombok 相关依赖文件。 * 使用 HttpStatus 的常量表示 响应状态码,需导入 httpcore 相关依赖文件。 */ @Data public class Result { /** * 响应是否成功,true 为成功,false 为失败 */ private Boolean success; /** * 响应状态码, 200 成功,500 系统异常 */ private Integer code; /** * 响应消息 */ private String message; /** * 响应数据 */ private Mapdata = new HashMap<>(); /** * 默认私有构造器 */ private Result(){} /** * 私有自定义构造器 * @param success 响应是否成功 * @param code 响应状态码 * @param message 响应消息 */ private Result(Boolean success, Integer code, String message){ this.success = success; this.code = code; this.message = message; } /** * 返回一个默认的 成功操作 的结果,默认响应状态码 200 * @return 成功操作的实例对象 */ public static Result ok() { return new Result(true, HttpStatus.SC_OK, "success"); } /** * 返回一个自定义 成功操作 的结果 * @param success 响应是否成功 * @param code 响应状态码 * @param message 响应消息 * @return 成功操作的实例对象 */ public static Result ok(Boolean success, Integer code, String message) { return new Result(success, code, message); } /** * 返回一个默认的 失败操作 的结果,默认响应状态码为 500 * @return 失败操作的实例对象 */ public static Result error() { return new Result(false, HttpStatus.SC_INTERNAL_SERVER_ERROR, "error"); } /** * 返回一个自定义 失败操作 的结果 * @param success 响应是否成功 * @param code 响应状态码 * @param message 相应消息 * @return 失败操作的实例对象 */ public static Result error(Boolean success, Integer code, String message) { return new Result(success, code, message); } /** * 自定义响应是否成功 * @param success 响应是否成功 * @return 当前实例对象 */ public Result success(Boolean success) { this.setSuccess(success); return this; } /** * 自定义响应状态码 * @param code 响应状态码 * @return 当前实例对象 */ public Result code(Integer code) { this.setCode(code); return this; } /** * 自定义响应消息 * @param message 响应消息 * @return 当前实例对象 */ public Result message(String message) { this.setMessage(message); return this; } /** * 自定义响应数据,一次设置一个 map 集合 * @param map 响应数据 * @return 当前实例对象 */ public Result data(Map map) { this.data.putAll(map); return this; } /** * 通用设置响应数据,一次设置一个 key - value 键值对 * @param key 键 * @param value 数据 * @return 当前实例对象 */ public Result data(String key, Object value) { this.data.put(key, value); return this; } }
Step2:依赖信息
org.apache.httpcomponents httpcore 4.4.13 org.projectlombok lombok 1.18.12 provided
Step3:HttpStatus 状态码参考地址
http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/HttpStatus.html
(4)使用:
修改某个 controller 如下所示:
若查询用户成功,调用 ok 返回查询到的结果。
若查询用户失败,调用 error 返回查询失败信息。
@GetMapping("selectOne") public Result selectOne(Integer id) { Emp emp = this.empService.queryById(id); if (emp == null) { return Result.error().message("用户不存在"); } return Result.ok().data("items", emp).message("查询成功"); }
启动服务并访问:
测试查询用户不成功的返回结果:
{ "success": false, "code": 500, "message": "用户不存在", "data": {} }
测试查询用户成功的返回结果:
{ "success": true, "code": 200, "message": "查询成功", "data": { "items": { "id": 1, "name": "tom", "salary": 6000.0, "age": 20, "email": "tom@163.com" } } }
三、Java 项目构建 -- 统一异常处理
1、为什么使用统一异常处理?
使用统一结果处理时,有些异常我们可以提前预知并处理,但是一个运行时异常,我们不一定能预知并处理,这时可以使用统一异常处理,当异常发生时,触发该处理操作,从而保证程序的健壮性。
2、怎么做?如何处理?代码实现?
(1)怎么做?
使用 @ControllerAdvice 或者 @RestControllerAdvice 注解作为统一异常处理的核心。
这两个注解都是 Spring MVC 提供的。作用于 控制层 的一种切面通知。
功能:
全局异常处理。
全局数据绑定。
全局数据预处理。
【@ControllerAdvice 与 @RestControllerAdvice 区别:】
@RestControllerAdvice 注解包含了 @ControllerAdvice 与 @ResponseBody 注解。
类似于 @Controller 与 @RestController 的区别。
@RestControllerAdvice 返回 json 数据时不需要添加 @ResponseBody 注解。
(2)如何处理?
使用 @ControllerAdvice 或者 @RestControllerAdvice 注解标记一个 全局异常处理类。
全局异常处理类内部使用 @ExceptionHandler 注解去捕获异常。
可以自定义一个异常信息收集类,用于处理项目中的异常,并收集异常信息。
(3)代码实现?
Step1(可选操作):
自定义一个异常类,用于处理项目中的异常,并收集异常信息。
【代码实现:】 package com.lyh.common.exception; import lombok.Data; import org.apache.http.HttpStatus; /** * 自定义异常, * 可以自定义 异常信息 message 以及 响应状态码 code(默认为 500)。 * * 依赖信息说明: * 此处使用 @Data 注解,需导入 lombok 相关依赖文件。 * 使用 HttpStatus 的常量表示 响应状态码,需导入 httpcore 相关依赖文件。 */ @Data public class GlobalException extends RuntimeException { /** * 保存异常信息 */ private String message; /** * 保存响应状态码 */ private Integer code = HttpStatus.SC_INTERNAL_SERVER_ERROR; /** * 默认构造方法,根据异常信息 构建一个异常实例对象 * @param message 异常信息 */ public GlobalException(String message) { super(message); this.message = message; } /** * 根据异常信息、响应状态码构建 一个异常实例对象 * @param message 异常信息 * @param code 响应状态码 */ public GlobalException(String message, Integer code) { super(message); this.message = message; this.code = code; } /** * 根据异常信息,异常对象构建 一个异常实例对象 * @param message 异常信息 * @param e 异常对象 */ public GlobalException(String message, Throwable e) { super(message, e); this.message = message; } /** * 根据异常信息,响应状态码,异常对象构建 一个异常实例对象 * @param message 异常信息 * @param code 响应状态码 * @param e 异常对象 */ public GlobalException(String message, Integer code, Throwable e) { super(message, e); this.message = message; this.code = code; } }
Step2:
定义一个全局的异常处理类 GlobalExceptionHandler。
使用 @RestControllerAdvice 注解标记这个类。
内部使用 @ExceptionHandler 注解去捕获异常。
【代码实现:】 package com.lyh.common.exception; import com.lyh.common.util.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; /** * 全局异常处理类。 * 使用 slf4j 保存日志信息。 * 此处使用了 统一结果处理 类 Result 用于包装异常信息。 */ @RestControllerAdvice public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 处理 Exception 异常 * @param e 异常 * @return 处理结果 */ @ExceptionHandler(Exception.class) public Result handlerException(Exception e) { logger.error(e.getMessage(), e); return Result.error().message("系统异常"); } /** * 处理空指针异常 * @param e 异常 * @return 处理结果 */ @ExceptionHandler(NullPointerException.class) public Result handlerNullPointerException(NullPointerException e) { logger.error(e.getMessage(), e); return Result.error().message("空指针异常"); } /** * 处理自定义异常 * @param e 异常 * @return 处理结果 */ @ExceptionHandler(GlobalException.class) public Result handlerGlobalException(GlobalException e) { logger.error(e.getMessage(), e); return Result.error().message(e.getMessage()).code(e.getCode()); } }
(4)使用?
修改某个 controller 如下所示:
参数不存在时,抛出 空指针异常。
参数为 -1 时,抛出自定义异常并处理。
查询结果为 null 时,抛出自定义异常并处理。
查询成功时,正确处理并返回。
@GetMapping("selectOne") public Result selectOne(Integer id) { Emp emp = this.empService.queryById(id); if (id == null) { throw new NullPointerException(); } if (id == -1) { throw new GlobalException("参数异常", 400); } if (emp == null) { throw new GlobalException("未查询到结果,请确认输入是否正确"); } return Result.ok().data("items", emp).message("查询成功"); }
启动服务并访问:
参数不存在时:
{ "success": false, "code": 500, "message": "空指针异常", "data": {} }
参数存在,但为 -1 时:
{ "success": false, "code": 400, "message": "参数异常", "data": {} }
参数存在,但查询结果为 null 时:
{ "success": false, "code": 500, "message": "未查询到结果,请确认输入是否正确", "data": {} }
参数存在,查询结果存在时:
{ "success": true, "code": 200, "message": "查询成功", "data": { "items": { "id": 1, "name": "tom", "salary": 6000.0, "age": 20, "email": "tom@163.com" } } }
四、Java 项目构建 -- 统一日志处理
1、为什么使用统一日志处理?
实际开发中,经常遇到各种各样的问题,使用日志可以方便、快速定位到错误处,便于程序调试。
2、简单介绍?如何处理?实现?
(1)简单介绍
此处使用 logback 进行日志处理(是 SpringBoot 内置日志框架)。其与 log4j 类似。
日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出
(2)如何处理?
一般通过 logback-spring.xml (放在 resources 目录下)作为 日志的配置文件。
默认情况下,日志输出到控制台,若想输出到文件中,可以简单的配置一下。
简单了解一下 logback-spring.xml 的常用标签信息。
【configuration】 <configuration> configuration> 是 logback-spring.xml 最外层标签(基本结构), 其内部包含 <contextName>contextName> 用于定义 logger 上下文名称, <property>property> 用于定义变量值,通过 ${} 引用变量, <appender>appender> 用于格式化日志并指定输出位置(控制台、文件), <springProfile>springProfile> 用于定义不同环境的日志(test、dev、prod), <root>root> 用于定义基本的日志输出级别。 <logger>logger> 用于定义某个包、或类具体的日志输出级别。 等标签。
Step1:了解一下
property 用于定义变量值,可以通过 ${} 引用变量。
其中:
name 用于定义变量名。
value 用于定义变量值。
如下例:
log.path 用于定义文件输出路径。
CONSOLE_LOG_PATTERN 用于定义文件输出格式。
使用 ${log.path }、${CONSOLE_LOG_PATTERN} 即可获取到变量值。
<property name="log.path" value="E:/myProject/log" /> <property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
Step2:了解一下
appender 用于格式化日志并指定输出位置(控制台、文件)。
其中:
ConsoleAppender 用于输出到控制台。
RollingFileAppender 用于输出到文件。
如下例:
日志输出到控制台,
name 属性用于指定 appender 名,后续可以通过
class 属性用于指定日志生成策略, ConsoleAppender 为控制台输出策略。
filter 标签用于过滤日志输出,如下,过滤掉低于 debug 级别的日志。
encoder 用于定义日志的编码格式,可以设置输出样式、字符集等。
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>debuglevel> filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}Pattern> <charset>UTF-8charset> encoder> appender>
如下例:
日志输出到文件。
RollingFileAppender 为文件输出策略。
file 标签用于指定文件输出路径。
rollingPolicy 标签用于日志的处理(归档、切割、过期时间等)。
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/info.logfile> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/web-info-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>infolevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender>
Step3:了解一下
root 用于定义基本的日志输出级别。
其中:
level 属性用于指定日志输出级别。
appender-ref 用于引用
<root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="INFO_FILE" /> root>
Step4:了解一下
用于定义某个包、或类具体的日志输出级别。
其中:
name 属性用于指定 某个包或者具体的某个类。
level 属性用于定义日志输出级别。
<logger name="com.lyh.test" level="INFO" />
Step5:了解一下
用于定义不同环境下的日志输出。
其中:
name 属性用于指定环境(test、dev、prod)。
SpringBoot 中可以通过 yml 或者 properties 指定环境:
spring:
profiles:
active: dev
如下例:
为开发环境以及生产环境的不同日志输出处理方式。
开发环境(dev)下,可以在控制台输出。
生产环境(prod)下,只能输出到文件。
<springProfile name="dev"> <logger name="com.lyh.test" level="INFO" /> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> root> springProfile> <springProfile name="prod"> <root level="INFO"> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="WARN_FILE" /> root> springProfile>
(3)实现?
Step1:
logback-spring.xml 完整配置如下(需要做些适当修改)。
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="10 seconds"> <contextName>logbackcontextName> <property name="log.path" value="src/log"/> <property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>DEBUGlevel> filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}Pattern> <charset>UTF-8charset> encoder> appender> <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/debug.logfile> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/debug-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>debuglevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/info.logfile> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/info-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>infolevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/warn.logfile> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/warn-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warnlevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/error.logfile> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERRORlevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <springProfile name="dev"> <logger name="com.lyh.test.test_mybatis_plus" level="DEBUG"/> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="DEBUG_FILE"/> <appender-ref ref="INFO_FILE"/> <appender-ref ref="WARN_FILE"/> <appender-ref ref="ERROR_FILE"/> root> springProfile> <springProfile name="prod"> <logger name="com.lyh" level="WARN"/> <root level="INFO"> <appender-ref ref="ERROR_FILE"/> <appender-ref ref="WARN_FILE"/> root> springProfile> configuration>
需要修改的地方如下,其余的根据项目情况进行修改。
【修改一:】 // 指定日志输出路径(log.path 相关的地方都可以视情况修改) <property name="log.path" value="E:/myProject/log"/> <property name="log.path" value="src/log"/> 【修改二:】 // 使用 logger 指定包的地方,根据项目实际的包名进行修改 <logger name="com.lyh.test.test_mybatis_plus" level="DEBUG"/>
Step2:
一般日志会与异常处理相结合。
前面已经介绍了全局异常处理,在异常处理类中进行相关日志处理,即可。
【全局异常处理:】
https://www.cnblogs.com/l-y-h/p/12781586.html#_label2
可以自定义一个异常信息处理工具类(可选操作),将堆栈信息转为 String 类型。
【可以自定义一个异常信息处理工具类:(可选操作,ExceptionUtil)】
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 异常处理工具类,将异常堆栈信息转为 String 类型
*/
@Slf4j
public class ExceptionUtil {
public static String getMessage(Exception e) {
String message = null;
try(StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
e.printStackTrace(pw);
pw.flush();
sw.flush();
message = sw.toString();
}catch (IOException io) {
io.printStackTrace();
log.error(io.getMessage());
}
return message;
}
}
修改统一异常处理工具类(GlobalExceptionHandler)。
使用 @Slf4j 标注类。
使用 log.error() 打印日志信息。
import com.lyh.test.test_mybatis_plus.util.Result;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理类。
* 使用 slf4j 保存日志信息。
* 此处使用了 统一结果处理 类 Result 用于包装异常信息。
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理 Exception 异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(Exception.class)
public Result handlerException(Exception e) {
log.error(e.getMessage());
return Result.error().message("系统异常");
}
/**
* 处理空指针异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(NullPointerException.class)
public Result handlerNullPointerException(NullPointerException e) {
log.error(e.getMessage());
return Result.error().message("空指针异常");
}
/**
* 处理自定义异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(GlobalException.class)
public Result handlerGlobalException(GlobalException e) {
log.error(e.getMessage());
return Result.error().message(e.getMessage()).code(e.getCode());
}
}
简单测试一下:
如下,抛出自定义异常时,会被 GlobalExceptionHandler 捕获并处理。
@GetMapping("/test")
public Result test() {
try{
System.out.println(1/0);
} catch (Exception e) {
throw new GlobalException(ExceptionUtil.getMessage(e));
}
return Result.ok().data("items", userService.list());
}