实现思路

  1. springboot默认使用slf4j作为日志门面, logback作为其实现

(logback的配置文件加载顺序为logback.xml—->application.properties—->logback-spring.xml, 一般在logback.xml中定义变量, 在logback-spring.xml中实现)

  1. 日志要全局保持统一规范的格式, 以便后续拓展, 比如写入es后供前端展示查阅等
  2. 在service层对mybatisPlus已有的Mapper接口做自定义封装, 添加上日志记录, 这里舍弃了mybatisPlus默认的IService和ServiceImpl, 因为这两者接口都是default, 不便做日志的拓展

配置解析

公共模块core包

base.xml, 基础日志配置

  1. 这里定义了三个appender, 一个控制台输出, 一个文件记录, 一个写入es
  2. 这里使用的滚动策略是SizeAndTimeBasedRollingPolicy, 默认配置为每天生成日志文件, 每个文件最大100M(一天内日志记录大于100M, 则生成多个文件), 保存10个历史记录日志文件, 所有日志文件一起不能超过10G ```java <?xml version=”1.0” encoding=”UTF-8”?>

${log_file:-${log_file_default}} ${log_file_pattern:-${log_file_pattern_default}} ${log_file_max_size:-${log_file_max_size_default}} ${log_file_max_history:-${log_file_max_history_default}} ${log_file_max_total_size:-${log_file_max_total_size_default}} ${log_pattern:-${log_pattern_default}} ${log_pattern:-${log_pattern_default}}

  1. 这里因为保存位置为src/java/xxx/logger下, 因此注意打包时需要一起打入resources
  2. ```java
  3. <resources>
  4. <resource>
  5. <directory>src/main/java</directory>
  6. <includes>
  7. <include>**/*.xml</include>
  8. </includes>
  9. </resource>
  10. </resources>

AbstractLogger

  1. package top.xinzhang0618.oa.logger;
  2. import org.slf4j.Logger;
  3. import org.slf4j.MDC;
  4. import top.xinzhang0618.oa.Assert;
  5. import top.xinzhang0618.oa.util.StringUtils;
  6. /**
  7. * AbstractLogger
  8. *
  9. */
  10. public abstract class AbstractLogger {
  11. protected abstract Logger getLogger();
  12. private String preProcessMessage(String message) {
  13. return StringUtils.isBlank(message) ? "" : message;
  14. }
  15. protected void putMDCItem(String key, Object value) {
  16. if (!Assert.isNull(key) && !Assert.isNull(value)) {
  17. MDC.put(key, String.valueOf(value));
  18. }
  19. }
  20. protected void doInfo(String message, Object... args) {
  21. getLogger().info(preProcessMessage(message), args);
  22. cleanMDC();
  23. }
  24. protected void doWarn(String message, Object... args) {
  25. getLogger().warn(preProcessMessage(message), args);
  26. cleanMDC();
  27. }
  28. protected void doDebug(String message, Object... args) {
  29. getLogger().debug(preProcessMessage(message), args);
  30. cleanMDC();
  31. }
  32. protected void doError(String message, Object... args) {
  33. getLogger().error(preProcessMessage(message), args);
  34. cleanMDC();
  35. }
  36. protected void doError(String message, Throwable throwable) {
  37. getLogger().error(message, throwable);
  38. cleanMDC();
  39. }
  40. protected void doTrace(String message, Object... args) {
  41. getLogger().trace(preProcessMessage(message), args);
  42. cleanMDC();
  43. }
  44. public boolean isTraceEnabled() {
  45. return getLogger().isTraceEnabled();
  46. }
  47. public boolean isDebugEnabled() {
  48. return getLogger().isDebugEnabled();
  49. }
  50. public boolean isInfoEnabled() {
  51. return getLogger().isInfoEnabled();
  52. }
  53. public boolean isWarnEnabled() {
  54. return getLogger().isWarnEnabled();
  55. }
  56. public boolean isErrorEnabled() {
  57. return getLogger().isErrorEnabled();
  58. }
  59. protected void cleanMDC() {
  60. }
  61. }

BizLogger, 业务日志类

  1. 其他业务自行定义日志结构, 比如MallLogger等
  2. 此处采用通过路径获取Logger, 效果如下, 如果是通过Clazz获取Logger, 路径为全路径类名
    1. 10:06:52.396 INFO [http-nio-40001-exec-1] oa.biz.user - 1334318150742269954, 新增
    ```java package top.xinzhang0618.oa.logger;

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import top.xinzhang0618.oa.BizContext;

/**

  • 业务日志. *
  • @author buer */ public class BizLogger extends AbstractLogger {
  1. private static final String BIZ_ID = "bizId";
  2. private static final String ACTION = "action";
  3. private static final String OPERATOR = "operator";
  4. private static final String TENANT_ID = "tenantId";
  5. private final Logger logger;
  6. public BizLogger(final OaModule module) {
  7. this.logger = getLogger(module.getLogPath());
  8. }
  9. private Logger getLogger(String type) {
  10. return LoggerFactory.getLogger("oa.biz." + type.toLowerCase());
  11. }
  12. public void log(Long bizId, String action) {
  13. log(bizId, action, bizId + ", " + action);
  14. }
  15. public void log(Long bizId, String action, String message, Object... args) {
  16. log(BizContext.getTenantId(), bizId, BizContext.getUserName(), action, message, args);
  17. }
  18. public void log(Long tenantId, Long bizId, String nickname, String action, String message, Object... args) {
  19. putMDC(tenantId, bizId, nickname, action);
  20. this.doInfo(message, args);
  21. }
  22. protected void putMDC(Long tenantId, Long bizId, String nickname, String action) {
  23. putMDCItem(TENANT_ID, tenantId);
  24. putMDCItem(BIZ_ID, bizId);
  25. putMDCItem(OPERATOR, nickname);
  26. putMDCItem(ACTION, action);
  27. }
  28. @Override
  29. protected Logger getLogger() {
  30. return this.logger;
  31. }
  32. @Override
  33. protected void cleanMDC() {
  34. super.cleanMDC();
  35. MDC.remove(TENANT_ID);
  36. MDC.remove(BIZ_ID);
  37. MDC.remove(ACTION);
  38. MDC.remove(OPERATOR);
  39. }

}

  1. **OaLoggerFactory, 日志工厂类**
  2. ```java
  3. package top.xinzhang0618.oa.logger;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. /**
  7. * OaLoggerFactory
  8. */
  9. public class OaLoggerFactory {
  10. /**
  11. * 获取日志记录器
  12. *
  13. * @param clazz 类型
  14. */
  15. public static Logger getLogger(Class<?> clazz) {
  16. return LoggerFactory.getLogger(clazz);
  17. }
  18. /**
  19. * 获取日志记录器
  20. *
  21. * @param logName 日志名称
  22. */
  23. public static Logger getLogger(String logName) {
  24. return LoggerFactory.getLogger(logName);
  25. }
  26. /**
  27. * 获取业务日志记录类
  28. *
  29. * @param module 模块
  30. * @return 日志实例
  31. */
  32. public static BizLogger getBizLogger(final OaModule module) {
  33. return new BizLogger(module);
  34. }
  35. }

其他
ActionConstants

  1. package top.xinzhang0618.oa.logger;
  2. /**
  3. * 操作常量
  4. *
  5. */
  6. public class ActionConstants {
  7. public static final String ADD = "新增";
  8. public static final String AUDIT = "审核";
  9. public static final String REVIEW = "复核";
  10. public static final String MODIFY = "修改";
  11. public static final String DELETE = "删除";
  12. public static final String CANCEL = "取消";
  13. public static final String ENABLE = "启用";
  14. public static final String DISABLE = "禁用";
  15. public static final String INVALID = "作废";
  16. public static final String BEGIN = "开始";
  17. public static final String END = "结束";
  18. }

OaModule

  1. package top.xinzhang0618.oa.logger;
  2. /**
  3. * OAModule
  4. */
  5. public enum OaModule {
  6. /**
  7. * 角色管理
  8. */
  9. ROLE("角色管理", "role"),
  10. /**
  11. * 用户角色
  12. */
  13. USER_ROLE("角色用户", "user.role"),
  14. /**
  15. * 用户管理
  16. */
  17. USER("用户管理", "user"),
  18. /**
  19. * 授权
  20. */
  21. PRIVILEGE("授权","privilege");
  22. private String caption;
  23. private String logPath;
  24. OaModule(String caption, String logPath) {
  25. this.caption = caption;
  26. this.logPath = logPath;
  27. }
  28. public String getCaption() {
  29. return caption;
  30. }
  31. public String getLogPath() {
  32. return logPath;
  33. }
  34. }

服务配置

例如web服务, 需配置resources/config/logback-spring.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <property name="log_file" value="log/web.log"/>
  4. <property name="log_file_pattern" value="log/service.%d{yyyy-MM-dd}.%i.log"/>
  5. <include resource="top/xinzhang0618/oa/logger/base.xml"/>
  6. <springProfile name="dev,test">
  7. <logger name="top.xinzhang0618.oa" level="debug" additivity="false">
  8. <appender-ref ref="file_out"/>
  9. <appender-ref ref="stdout"/>
  10. </logger>
  11. </springProfile>
  12. <springProfile name="prod">
  13. <logger name="top.xinzhang0618.oa" level="info" additivity="false">
  14. <appender-ref ref="file_out"/>
  15. </logger>
  16. </springProfile>
  17. </configuration>

此外在yml中也需指定配置文件位置

  1. server:
  2. port: 40001
  3. servlet:
  4. context-path: /api
  5. logging:
  6. config: classpath:config/logback-spring.xml

BIZ接口处理

BizService, 这里提出了一些通用接口, 可以根据业务情况进一步优化

  1. package top.xinzhang0618.oa.service;
  2. import com.baomidou.mybatisplus.core.conditions.Wrapper;
  3. import com.baomidou.mybatisplus.core.metadata.IPage;
  4. import top.xinzhang0618.oa.BaseDO;
  5. import java.util.List;
  6. /**
  7. * @author xinzhang
  8. * @date 2020/12/2 16:35
  9. */
  10. public interface BizService<T extends BaseDO, W extends Wrapper<T>> {
  11. /**
  12. * 新写入数据库记录.
  13. *
  14. * @return 影响的记录数
  15. */
  16. int create(T entity);
  17. /**
  18. * 根据主键来更新符合条件的数据库记录.
  19. *
  20. * @return 影响的记录数
  21. */
  22. int modify(T entity);
  23. /**
  24. * 根据主键删除记录.
  25. *
  26. * @return 影响的记录数
  27. */
  28. int remove(Long id);
  29. /**
  30. * 根据主键查询数据.
  31. *
  32. * @return 实体对象
  33. */
  34. T get(Long id);
  35. /**
  36. * 根据示例获取单条数据.
  37. *
  38. * @return 实体对象
  39. */
  40. T get(W w);
  41. /**
  42. * 查询列表.
  43. *
  44. * @return 实体对象列表
  45. */
  46. List<T> list();
  47. /**
  48. * 根据条件查询实体对象.
  49. *
  50. * @return 实体对象列表
  51. */
  52. List<T> list(W w);
  53. /**
  54. * 查询分页.
  55. *
  56. * @return 分页的实体对象列表
  57. */
  58. IPage<T> listPage( int page, int pageSize);
  59. /**
  60. * 根据条件查询实体对象返回分页结果.
  61. *
  62. * @return 分页的实体对象列表
  63. */
  64. IPage<T> listPage(W w, int page, int pageSize);
  65. }

AbstractService, 提供了接口的默认实现, 其中添加了日志记录

  1. package top.xinzhang0618.oa.service;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4. import com.baomidou.mybatisplus.core.metadata.IPage;
  5. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import top.xinzhang0618.oa.BaseDO;
  8. import top.xinzhang0618.oa.logger.ActionConstants;
  9. import top.xinzhang0618.oa.logger.BizLogger;
  10. import java.util.List;
  11. /**
  12. * @author xinzhang
  13. * @date 2020/12/2 15:13
  14. */
  15. public abstract class AbstractService<M extends BaseMapper<T>, T extends BaseDO> implements BizService<T,
  16. LambdaQueryWrapper<T>> {
  17. @Autowired
  18. protected M baseMapper;
  19. /**
  20. * 获取业务日志记录器
  21. *
  22. * @return 日志记录器
  23. */
  24. protected BizLogger getBizLogger() {
  25. return null;
  26. }
  27. @Override
  28. public int create(T entity) {
  29. int count = baseMapper.insert(entity);
  30. getBizLogger().log(entity.getPrimaryKey(), ActionConstants.ADD);
  31. return count;
  32. }
  33. @Override
  34. public int modify(T entity) {
  35. int count = baseMapper.updateById(entity);
  36. getBizLogger().log(entity.getPrimaryKey(), ActionConstants.MODIFY);
  37. return count;
  38. }
  39. @Override
  40. public int remove(Long id) {
  41. int count = baseMapper.deleteById(id);
  42. getBizLogger().log(id, ActionConstants.DELETE);
  43. return count;
  44. }
  45. @Override
  46. public T get(Long id) {
  47. return baseMapper.selectById(id);
  48. }
  49. @Override
  50. public T get(LambdaQueryWrapper<T> wrapper) {
  51. return baseMapper.selectOne(wrapper);
  52. }
  53. @Override
  54. public List<T> list() {
  55. return baseMapper.selectList(new LambdaQueryWrapper<>());
  56. }
  57. @Override
  58. public List<T> list(LambdaQueryWrapper<T> wrapper) {
  59. return baseMapper.selectList(wrapper);
  60. }
  61. @Override
  62. public IPage<T> listPage(int page, int pageSize) {
  63. return baseMapper.selectPage(new Page<>(page,pageSize),new LambdaQueryWrapper<>());
  64. }
  65. @Override
  66. public IPage<T> listPage(LambdaQueryWrapper<T> wrapper, int page, int pageSize) {
  67. return baseMapper.selectPage(new Page<>(page,pageSize),wrapper);
  68. }
  69. }

实现示例

  1. /**
  2. * ClientService
  3. *
  4. * @author autoGenerate
  5. * @version 2020-12-02
  6. */
  7. public interface ClientService extends BizService<Client, LambdaQueryWrapper<Client>> {
  8. }
  1. /**
  2. * ClientServiceImpl
  3. *
  4. * @author autoGenerate
  5. * @version 2020-12-02
  6. */
  7. @Service
  8. public class ClientServiceImpl extends AbstractService<ClientMapper, Client> implements ClientService {
  9. }

日志写入ES

待补充