# 日志
# 概述
系统日志是记录系统中硬件、软件和系统问题的信息,同时还可以监视系统中发生的事件。用户可以通过它来检查错误发生的原因,或者寻找受到攻击时攻击者留下的痕迹。
本章节将日志分为4种使用场景分别介绍在HOS平台如何使用
- slf4j+logback:最灵活的一种方式,根据具体业务在代码中调用logAPI输出日志信息
- 全局异常日志:在HOS平台中有一个全局异常拦截机制,可以将全局异常进行统一拦截,并且在拦截后将异常信息输出error日志中(底层)
- OperLog操作日志接口:HOS平台定义了业务行为操作日志接口,可以将操作日志存到数据库中,方便查询。
- @OperLog注解:添加在controller层的方法上即可将操作操作日志存储到数据库中或输出到log文件中。
# 一、slf4j+logback
在HOS平台中,底层日志框架使用slf4j
+logback
实现。
使用步骤如下:
# 使用说明
# 1.引入依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
由于springboot内置了logback,所以无需手动添加logback依赖
# 2.获取Logger对象
在项目中使用Slf4j
有两种方式:
备注:在代码中应该使用Slf4j输出日志,不可以直接使用logback输出日志
方式一:通过工厂方式使用
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger log = LoggerFactory.getLogger(XXX.class);
方式二:即使用lombok的注解@Slf4j
,不过需要提前配置好lombok依赖及idea中下载好插件,这块前边文档已经有所说明。使用注解方式如下:
@Slf4j
@RestController
@RequestMapping("/user/staff")
public class StaffController extends BaseController<Staff> {
//...
}
备注:在代码中应该使用Slf4j输出日志,不可以直接使用logback输出日志,
# 3.日志打印
@GetMapping("/selectPageStaff")
public BaseResponse<IPage<Staff>> selectPageStaff(@RequestBody Staff staff) {
log.info("StaffController.selectPageStaff(),args:{}", staff.toString());
return BaseResponse.success(staffService.selectPageStaff(staff));
}
slf4j支持使用占位符方式打印参数
# 4.添加配置文件
在 resources 文件夹下新建 logback-spring.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="/logs/hos/" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="info_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/hos.info.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MB</MaxFileSize>
</triggeringPolicy>
<!-- ThresholdFilter:临界值过滤器,过滤掉 TRACE 和 DEBUG 级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level><!--等级从低到高分别是ALL < TRACE < DEBUG < INFO < WARN < ERROR < OFF-->
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="error_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/hos.error.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MB</MaxFileSize>
</triggeringPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印ERROR日志 -->
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="info_log"/>
<appender-ref ref="error_log"/>
</root>
</configuration>
# 5.打印效果
# 二、全局异常日志
在HOS平台中有一个全局异常拦截机制,可以将全局异常进行统一拦截,并且在拦截后将异常信息输出error日志中,该机制已集成到了HOS平台,不需要开发人员实现。
@Slf4j
@RestControllerAdvice(basePackages = {"com"})
public class ExceptionHandlerAdvice {
/**
* 自定义异常
*
* @param e
* @return
*/
@ExceptionHandler({BaseBusinessException.class})
public BaseResponse handleBusinessMsgException(BaseBusinessException e) {
log.error("BusinessException code:{}, msg:{}", e.getCode(), e.getMessage());
return BaseResponse.error(e.getCode(), e.getMessage());
}
/**
* 异常
*
* @param e
* @return
*/
@ExceptionHandler({Exception.class})
public BaseResponse handleException(Exception e) {
log.error("系统处理异常", e);
return BaseResponse.error(SysExceptionEnum.BUSINESS_EXE_ERROR.getCode(), SysExceptionEnum.BUSINESS_EXE_ERROR.getMsg());
}
}
# 三、OperLog操作日志接口
# 介绍
HOS平台定义了一个业务行为操作日志接口,可以将操作日志存到数据库中。
# 使用说明
1.导入依赖
<dependency>
<groupId>com.mediway.hos</groupId>
<artifactId>hos-framework-log-starter</artifactId>
</dependency>
2.创建sys_oper_log表
CREATE TABLE `sys_oper_log` (
`id` varchar(32) NOT NULL,
`type` varchar(10) DEFAULT NULL COMMENT '类型 1:操作日志',
`title` varchar(50) DEFAULT NULL COMMENT '模块标题',
`content` varchar(50) DEFAULT NULL COMMENT '日志内容',
`method` varchar(200) DEFAULT NULL COMMENT '方法名称',
`request_method` varchar(10) DEFAULT NULL COMMENT '请求方式',
`request_url` varchar(100) DEFAULT NULL COMMENT '请求URL',
`ip` varchar(32) DEFAULT NULL COMMENT '请求IP地址',
`client_ip` varchar(32) DEFAULT NULL COMMENT '客户端ip',
`client_mac` varchar(255) DEFAULT NULL COMMENT '客户端mac地址',
`ip_location` varchar(255) DEFAULT NULL COMMENT 'ip_location 暂时不赋值',
`request_param` text COMMENT '请求参数',
`response_result` text COMMENT '方法响应参数',
`status` tinyint(1) DEFAULT NULL COMMENT 'status',
`error_msg` text COMMENT '错误消息',
`take_time` int(11) DEFAULT NULL COMMENT '方法执行耗时',
`oper_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '操作时间',
`user_id` varchar(32) DEFAULT NULL COMMENT '用户id',
`user_name` varchar(255) DEFAULT NULL COMMENT '用户名称',
`browser` varchar(255) DEFAULT NULL COMMENT '浏览器',
`tenant_id` varchar(32) DEFAULT NULL COMMENT '租户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.调用operLogService的insert方法
@Autowired
private OperLogService operLogService;
operLogService.insert(operLogEntity);
4.operLogEntity参数介绍
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
type | String | 是 | 类型(1为操作日志) |
title | String | 是 | 模块标题 |
content | String | 是 | 日志内容 |
userId | String | 是 | 用户id |
userName | String | 是 | 用户名称 |
method | String | 否 | 方法名称(注解方式时值为所在的方法名) |
requestMethod | String | 否 | 请求方式(GET、POST) |
requestUrl | String | 否 | 请求URL |
ip | String | 是 | 请求IP地址(可以通过IpUtil工具类获取) |
clientIp | String | 是 | 内网ip地址(需要前端放在header上可以通过IpUtil工具类获取) |
clientMac | String | 是 | 内网MAC地址(需要前端放在header上可以通过IpUtil工具类获取) |
server_ip | String | 是 | 服务器ip |
requestParam | String | 是 | 请求参数 |
responseResult | String | 否 | 方法响应参数(该参数暂时不赋值) |
status | Integer | 是 | 操作状态(1成功 0失败) |
errorMsg | String | 否 | 错误消息(状态为1是必填) |
operTime | Date | 是 | 操作时间 |
errorMsg | String | 否 | 错误消息(状态为1是必填) |
takeTime | Long | 否 | 方法执行耗时(单位:毫秒) |
# 四、@OperLog注解
# 介绍
@OperLog注解方式是在OperLog操作日志接口的基础上进行了进一步的封装,通过AOP机制自动填充OperLog实体信息,在controller中的方法添加注解@OperLog
。
# @OperLog注解参数介绍
/**
* 自定义注解记录系统操作日志
*/
@Target({ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface OperLog
{
/**
* 模块标题
*/
String title() default "";
/**
* 日志内容
*/
String content() default "";
/**
* 操作日志输出位置:logFile,database 二者可以同时存在,默认为logFile
* 不填的话根据配置文件framework.oper-log.out项决定
* @return
*/
OperLogOut[] operLogOut() default {};
}
@OperLog
含有三个属性,分别为
title
用来记录方法所在模块名称content
用来记录方法内容operLogOut
用来设置日志输出位置,需要注意的是配置文件中的配置framework.oper-log.out
也可设置该属性,不同之处在于配置文件中的为该属性的全局配置,比注解上直接设置优先级低;只有在注解上没有配置该属性时,配置文件中的设置才会生效;配置文件中默认值为logFile
。
# 使用说明
1.导入依赖
<dependency>
<groupId>com.mediway.hos</groupId>
<artifactId>hos-framework-log-starter</artifactId>
</dependency>
2.配置文件
framework:
# 操作日志输出位置:logFile,database 二者可以同时存在,默认为logFile
# logFile表示输出到log文件,database表示输出数据库中的sys_oper_log表中
# 该配置只对OperLog注解生效,与其他日志输出无关
oper-log:
out: database,logFile
3.在方法上使用注解@OperLog
/**
* 测试自定义异常
*
* @return
*/
@OperLog(title = "合同管理模块", content = "测试自定义异常")
@GetMapping("/testException")
public BaseResponse testException() {
contractService.testException();
return BaseResponse.success();
}
4.请求该接口查看效果