日志规范
日志位置(什么时候进行日志记录)
- 【强制-ERROR/WARN】操作失败必须记录日志
- 【强制-ERROR】抛出异常必须记录日志(throw/try-catch)
- 【强制-INFO】敏感操作必须记录日志(数据库和redis的增、删、改;)
- 【推荐-INFO】查询接口推荐进行日志记录
- 【自定义-DEBUG】开发环境下可记录自定义的日志
PS:【】解释——【规范等级-日志等级】
日志等级
框架中常用日志等级(从低到高): DEBUG->INFO->WARN->ERROR
各日志等级使用:
- DEBUG: 在本地开发中使用,可打印任何打印信息
- INFO:打印业务日志
- WARN:特殊场景下敏感操作但又不引起异常的日志(非数据库、redis日志记录;使用频率较少)
- ERROR:报错,业务预期/非预期错误,站在业务角度:需要关注的程序异常都用此等级打印
日志使用
业务日志使用
推荐使用Lombok提供的快速日志记录方式。
使用:
类添加@Slf4j
注解。
直接使用log
调用对应级别的方法即可。
举例:
@Slf4j
public class Test {
public void testLog(){
log.debug("");
log.info("");
log.warn("");
log.error("");
}
}
注解@Log
接口切面日志使用
SMPE-ADMIN框架默认提供接口日志记录的注解@Log
(在smpe-log子工程下实现)
使用:
controller层接口添加注解@Log
,访问接口即可持久化到数据库进行缓存
举例:
@Log("新增用户")
@ApiOperation("新增用户")
@PostMapping
@ApiParam(name = "userInsertOrUpdateDTO", value = "新增用户参数列表")
@PreAuthorize("@smpe.check('user:add')")
public Result<Void> insertUserWithDetail(@RequestBody UserInsertOrUpdateDTO userInsertOrUpdateDTO) {
return Result.success();
}
注意事项:
- 前台接口暂时不添加
@Log
(持久化到数据库影响一定的mysql性能) - 数据库记录接口参数信息暂时只记录添加
@RequestBody
或@RequestParam
注解的参数 - 此注解主要记录增删改接口的日志
日志格式
格式:【业务操作+成功/失败】具体信息。操作人ID:,操作记录唯一标识:,(操作记录重要信息:)。
推荐使用优雅的字符串拼接:使用hutool的StrUtil.format()
方法
举例:
int userId = 1;
String username = "test";
log.error("【新增用户失败】用户名已存在。操作人id:{},新增用户用户名:{}", userId, username);
结果:
SMPE-ADMIN- 2020-12-24 21:54:26 [main] ERROR marchsoft.log.LogTest - 【新增用户失败】用户名已存在。操作人id:1,新增用户用户名:test
以下方式不推荐(字符串"+"拼接):
int userId = 1;
String username = "test";
log.error("【新增用户失败】用户名已存在。操作人id:" + userId + "新增用户用户名:" + username);
日志使用场景举例
/**
* description:新增用户(维护岗位、角色表)
*
* @param userInsertOrUpdateDTO /
* @author RenShiWei
* Date: 2020/11/24 20:52
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void insertUserWithDetail(UserInsertOrUpdateDTO userInsertOrUpdateDTO) {
userInsertOrUpdateDTO.setEnabled(true);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
//判断用户名不能重复
queryWrapper.eq(User::getUsername, userInsertOrUpdateDTO.getUsername());
// modify @RenShiWei 2020/11/24 description:list() ——> count()
if (this.count(queryWrapper) > 0) {
log.error("【新增用户失败】新增用户用户名已存在。操作人id:{},用户名:{}", SecurityUtils.getCurrentUserId(),
userInsertOrUpdateDTO.getUsername());
throw new BadRequestException(ResultEnum.USER_USERNAME_EXIST);
}
//判断邮箱不能重复
queryWrapper.clear();
queryWrapper.eq(User::getEmail, userInsertOrUpdateDTO.getEmail());
// modify @RenShiWei 2020/11/24 description:增加邮箱判断
if (this.count(queryWrapper) > 0) {
log.error("【新增用户失败】新增用户邮箱已存在。操作人id:{},邮箱:{}", SecurityUtils.getCurrentUserId(),
userInsertOrUpdateDTO.getEmail());
throw new BadRequestException(ResultEnum.USER_EMAIL_EXIST);
}
//属性拷贝
User user = new User();
BeanUtil.copyProperties(userInsertOrUpdateDTO, user);
//新增用户
boolean save = save(user);
if (! save) {
log.error("【新增用户失败】操作人id:{},用户名:{}", SecurityUtils.getCurrentUserId(),
userInsertOrUpdateDTO.getUsername());
throw new BadRequestException(ResultEnum.INSERT_OPERATION_FAIL);
}
//维护中间表
int count = userMapper.saveUserAtRole(user.getId(), userInsertOrUpdateDTO.getRoles());
if (count <= 0) {
log.error("【新增用户失败】维护角色中间表失败。操作人id:{}", SecurityUtils.getCurrentUserId());
throw new BadRequestException(ResultEnum.OPERATION_MIDDLE_FAIL);
}
count = userMapper.saveUserAtJob(user.getId(), userInsertOrUpdateDTO.getJobs());
if (count <= 0) {
log.error("【新增用户失败】维护岗位中间表失败。操作人id:{}", SecurityUtils.getCurrentUserId());
throw new BadRequestException(ResultEnum.OPERATION_MIDDLE_FAIL);
}
log.info("【新增用户成功】操作人id:{},用户名:{}", SecurityUtils.getCurrentUserId(),
userInsertOrUpdateDTO.getUsername());
}
日志文件相关
- 日志文件在项目根目录下生成,跟项目走,以logs命名;以年月命名文件夹,每月的日志文件统一放置。
- 以每天每小时生成一个日志
- 日志文件命名
- 业务日志:server.%d{yyyy-MM-dd-HH}.log
- redis日志:redis.%d{yyyy-MM-dd-HH}.log
- 日志文件保存周期:最少一个月
- 定期清理服务器不必要日志