【8.0】【log】初始化业务日志记录

pull/57/head
fengshuonan 2023-07-21 18:18:17 +08:00
parent cf616aee24
commit bddd8fa405
14 changed files with 809 additions and 0 deletions

View File

@ -0,0 +1,48 @@
/*
* Copyright [2020-2030] [https://www.stylefeng.cn]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* GunsAPACHE LICENSE 2.0使
*
* 1.LICENSE
* 2.Guns
* 3.
* 4. https://gitee.com/stylefeng/guns
* 5. https://gitee.com/stylefeng/guns
* 6.
*/
package cn.stylefeng.roses.kernel.rule.annotation;
import cn.hutool.core.util.StrUtil;
import java.lang.annotation.*;
/**
*
*
* @author fengshuonan
* @since 2023/7/21 15:37
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BizLog {
/**
*
*/
String logTypeCode() default StrUtil.EMPTY;
}

View File

@ -0,0 +1,37 @@
/*
* Copyright [2020-2030] [https://www.stylefeng.cn]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* GunsAPACHE LICENSE 2.0使
*
* 1.LICENSE
* 2.Guns
* 3.
* 4. https://gitee.com/stylefeng/guns
* 5. https://gitee.com/stylefeng/guns
* 6.
*/
package cn.stylefeng.roses.kernel.log.api;
/**
* service
*
* @author fengshuonan
* @since 2023/7/21 15:42
*/
public interface BizLogServiceApi {
}

View File

@ -37,6 +37,11 @@ public interface LogFileConstants {
*/
Integer DEFAULT_API_LOG_AOP_SORT = 500;
/**
* aop
*/
Integer DEFAULT_BUSINESS_LOG_AOP_SORT = 400;
/**
*
*/

View File

@ -40,6 +40,13 @@
<version>${roses.version}</version>
</dependency>
<!--数据库操作-->
<dependency>
<groupId>cn.stylefeng.roses</groupId>
<artifactId>db-sdk-mp</artifactId>
<version>${roses.version}</version>
</dependency>
<!-- web -->
<!-- 用于本模块中对controller的拦截器 -->
<dependency>

View File

@ -0,0 +1,148 @@
/*
* Copyright [2020-2030] [https://www.stylefeng.cn]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* GunsAPACHE LICENSE 2.0使
*
* 1.LICENSE
* 2.Guns
* 3.
* 4. https://gitee.com/stylefeng/guns
* 5. https://gitee.com/stylefeng/guns
* 6.
*/
package cn.stylefeng.roses.kernel.log.business.aop;
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
import cn.stylefeng.roses.kernel.log.api.constants.LogFileConstants;
import cn.stylefeng.roses.kernel.log.business.context.BusinessLogHolder;
import cn.stylefeng.roses.kernel.log.business.entity.SysLogBusiness;
import cn.stylefeng.roses.kernel.log.business.service.SysLogBusinessService;
import cn.stylefeng.roses.kernel.rule.annotation.BizLog;
import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.List;
/**
* AOP
*
* @author fengshuonan
* @since 2023/7/21 15:06
*/
@Aspect
@Slf4j
public class BusinessLogRecordAop implements Ordered {
@Resource
private SysLogBusinessService sysLogBusinessService;
/**
* BizLog
*
* @author fengshuonan
* @since 2023/7/21 15:49
*/
@Pointcut(value = "@annotation(cn.stylefeng.roses.kernel.rule.annotation.BizLog)")
public void cutService() {
}
@Around("cutService()")
public Object aroundPost(ProceedingJoinPoint joinPoint) throws Throwable {
try {
// 初始化日志的环境信息
initBusinessContext(joinPoint);
// 执行真正业务逻辑这期间会调用BusinessLogHolder类进行业务日志存储
Object result = joinPoint.proceed();
// 业务日志进行保存
SysLogBusiness logContext = BusinessLogHolder.getContext();
List<String> contentList = BusinessLogHolder.getContent();
sysLogBusinessService.saveBatchLogs(logContext, contentList);
return result;
} catch (Exception e) {
log.error("业务日志过程中出现异常!", e);
// 这里不拦截异常,给上级处理异常
throw e;
} finally {
// 清除所有日志缓存记录
BusinessLogHolder.clearContext();
}
}
/**
*
*
* @author fengshuonan
* @since 2023/7/21 15:55
*/
private void initBusinessContext(ProceedingJoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
BizLog bizLog = method.getAnnotation(BizLog.class);
SysLogBusiness sysLogBusiness = new SysLogBusiness();
// 设置业务日志的类型编码
sysLogBusiness.setLogTypeCode(bizLog.logTypeCode());
// 设置调用链id
sysLogBusiness.setTraceId(IdWorker.getId());
// 设置请求的URL
String servletPath = HttpServletUtil.getRequest().getServletPath();
sysLogBusiness.setRequestUrl(servletPath);
// 设置HTTP请求方式
sysLogBusiness.setHttpMethod(HttpServletUtil.getRequest().getMethod());
// 设置客户端IP
String requestClientIp = HttpServletUtil.getRequestClientIp(HttpServletUtil.getRequest());
sysLogBusiness.setClientIp(requestClientIp);
// 设置当前登录用户id
LoginUser loginUserNullable = LoginContext.me().getLoginUserNullable();
if (loginUserNullable != null) {
sysLogBusiness.setUserId(loginUserNullable.getUserId());
}
// 添加到线程环境变量
BusinessLogHolder.setContext(sysLogBusiness);
}
@Override
public int getOrder() {
return LogFileConstants.DEFAULT_BUSINESS_LOG_AOP_SORT;
}
}

View File

@ -0,0 +1,98 @@
package cn.stylefeng.roses.kernel.log.business.context;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.log.business.entity.SysLogBusiness;
import java.util.LinkedList;
import java.util.List;
/**
*
*
* @author fengshuonan
* @since 2023/7/21 16:06
*/
public class BusinessLogHolder {
/**
*
*/
private static final ThreadLocal<SysLogBusiness> BUSINESS_LOG_CONTEXT = new ThreadLocal<>();
/**
*
*/
private static final ThreadLocal<List<String>> CONTENT_LIST = new ThreadLocal<>();
/**
*
*
* @author fengshuonan
* @since 2023/7/21 16:23
*/
public static void setContext(SysLogBusiness sysLogBusiness) {
BUSINESS_LOG_CONTEXT.set(sysLogBusiness);
}
/**
*
*
* @author fengshuonan
* @since 2021/3/23 17:41
*/
public static SysLogBusiness getContext() {
return BUSINESS_LOG_CONTEXT.get();
}
/**
*
*
* @author fengshuonan
* @since 2021/3/23 17:42
*/
public static void clearContext() {
BUSINESS_LOG_CONTEXT.remove();
CONTENT_LIST.remove();
}
/**
*
*
* @author fengshuonan
* @since 2023/7/21 16:53
*/
public static void addContent(String contentStr) {
List<String> contentList = CONTENT_LIST.get();
if (ObjectUtil.isEmpty(contentList)) {
contentList = new LinkedList<>();
}
contentList.add(contentStr);
CONTENT_LIST.set(contentList);
}
/**
*
*
* @author fengshuonan
* @since 2023/7/21 16:53
*/
public static List<String> getContent() {
return CONTENT_LIST.get();
}
/**
* 便
*
* @author fengshuonan
* @since 2023/7/21 17:30
*/
public static void setLogTitle(String logTitle) {
SysLogBusiness sysLogBusiness = BUSINESS_LOG_CONTEXT.get();
if (sysLogBusiness == null) {
return;
}
sysLogBusiness.setLogTitle(logTitle);
BUSINESS_LOG_CONTEXT.set(sysLogBusiness);
}
}

View File

@ -0,0 +1,39 @@
package cn.stylefeng.roses.kernel.log.business.controller;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.log.business.entity.SysLogBusiness;
import cn.stylefeng.roses.kernel.log.business.pojo.request.SysLogBusinessRequest;
import cn.stylefeng.roses.kernel.log.business.service.SysLogBusinessService;
import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData;
import cn.stylefeng.roses.kernel.rule.pojo.response.SuccessResponseData;
import cn.stylefeng.roses.kernel.scanner.api.annotation.ApiResource;
import cn.stylefeng.roses.kernel.scanner.api.annotation.GetResource;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
*
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
@RestController
@ApiResource(name = "业务日志记录")
public class SysLogBusinessController {
@Resource
private SysLogBusinessService sysLogBusinessService;
/**
*
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
@GetResource(name = "查询业务日志列表(带分页)", path = "/sysLogBusiness/page")
public ResponseData<PageResult<SysLogBusiness>> page(SysLogBusinessRequest sysLogBusinessRequest) {
return new SuccessResponseData<>(sysLogBusinessService.findPage(sysLogBusinessRequest));
}
}

View File

@ -0,0 +1,86 @@
package cn.stylefeng.roses.kernel.log.business.entity;
import cn.stylefeng.roses.kernel.db.api.pojo.entity.BaseEntity;
import cn.stylefeng.roses.kernel.rule.annotation.ChineseDescription;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
@TableName("sys_log_business")
@Data
@EqualsAndHashCode(callSuper = true)
public class SysLogBusiness extends BaseEntity {
/**
*
*/
@TableId(value = "business_log_id", type = IdType.ASSIGN_ID)
@ChineseDescription("主键")
private Long businessLogId;
/**
*
*/
@TableField("log_type_code")
@ChineseDescription("日志的业务分类的编码")
private String logTypeCode;
/**
*
*/
@TableField("log_title")
@ChineseDescription("日志的标题,摘要信息")
private String logTitle;
/**
*
*/
@TableField("log_content")
@ChineseDescription("日志记录的内容")
private String logContent;
/**
* idid
*/
@TableField("trace_id")
@ChineseDescription("调用链唯一id一个id代表一次接口调用")
private Long traceId;
/**
* url
*/
@TableField("request_url")
@ChineseDescription("当前用户请求的url")
private String requestUrl;
/**
* http
*/
@TableField("http_method")
@ChineseDescription("请求http方法")
private String httpMethod;
/**
* ip
*/
@TableField("client_ip")
@ChineseDescription("客户端的ip")
private String clientIp;
/**
* id
*/
@TableField("user_id")
@ChineseDescription("业务操作的用户id")
private Long userId;
}

View File

@ -0,0 +1,36 @@
package cn.stylefeng.roses.kernel.log.business.enums;
import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
import cn.stylefeng.roses.kernel.rule.exception.AbstractExceptionEnum;
import lombok.Getter;
/**
*
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
@Getter
public enum SysLogBusinessExceptionEnum implements AbstractExceptionEnum {
/**
*
*/
SYS_LOG_BUSINESS_NOT_EXISTED(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + "10001", "查询结果不存在");
/**
*
*/
private final String errorCode;
/**
*
*/
private final String userTip;
SysLogBusinessExceptionEnum(String errorCode, String userTip) {
this.errorCode = errorCode;
this.userTip = userTip;
}
}

View File

@ -0,0 +1,14 @@
package cn.stylefeng.roses.kernel.log.business.mapper;
import cn.stylefeng.roses.kernel.db.mp.injector.CustomBaseMapper;
import cn.stylefeng.roses.kernel.log.business.entity.SysLogBusiness;
/**
* Mapper
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
public interface SysLogBusinessMapper extends CustomBaseMapper<SysLogBusiness> {
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.stylefeng.roses.kernel.log.business.mapper.SysLogBusinessMapper">
</mapper>

View File

@ -0,0 +1,75 @@
package cn.stylefeng.roses.kernel.log.business.pojo.request;
import cn.stylefeng.roses.kernel.rule.annotation.ChineseDescription;
import cn.stylefeng.roses.kernel.rule.pojo.request.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
/**
*
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class SysLogBusinessRequest extends BaseRequest {
/**
*
*/
@NotNull(message = "主键不能为空", groups = {edit.class, delete.class})
@ChineseDescription("主键")
private Long businessLogId;
/**
*
*/
@ChineseDescription("日志的业务分类的编码")
private String logTypeCode;
/**
*
*/
@ChineseDescription("日志的标题,摘要信息")
private String logTitle;
/**
*
*/
@ChineseDescription("日志记录的内容")
private String logContent;
/**
* idid
*/
@ChineseDescription("调用链唯一id一个id代表一次接口调用")
private Long traceId;
/**
* url
*/
@ChineseDescription("当前用户请求的url")
private String requestUrl;
/**
* http
*/
@ChineseDescription("请求http方法")
private String httpMethod;
/**
* ip
*/
@ChineseDescription("客户端的ip")
private String clientIp;
/**
* id
*/
@ChineseDescription("业务操作的用户id")
private Long userId;
}

View File

@ -0,0 +1,82 @@
package cn.stylefeng.roses.kernel.log.business.service;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.log.business.entity.SysLogBusiness;
import cn.stylefeng.roses.kernel.log.business.pojo.request.SysLogBusinessRequest;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
*
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
public interface SysLogBusinessService extends IService<SysLogBusiness> {
/**
*
*
* @param sysLogBusinessRequest
* @author fengshuonan
* @date 2023/07/21 15:00
*/
void add(SysLogBusinessRequest sysLogBusinessRequest);
/**
*
*
* @param sysLogBusinessRequest
* @author fengshuonan
* @date 2023/07/21 15:00
*/
void del(SysLogBusinessRequest sysLogBusinessRequest);
/**
*
*
* @param sysLogBusinessRequest
* @author fengshuonan
* @date 2023/07/21 15:00
*/
void edit(SysLogBusinessRequest sysLogBusinessRequest);
/**
*
*
* @param sysLogBusinessRequest
* @author fengshuonan
* @date 2023/07/21 15:00
*/
SysLogBusiness detail(SysLogBusinessRequest sysLogBusinessRequest);
/**
*
*
* @param sysLogBusinessRequest
* @return List<SysLogBusiness>
* @author fengshuonan
* @date 2023/07/21 15:00
*/
List<SysLogBusiness> findList(SysLogBusinessRequest sysLogBusinessRequest);
/**
*
*
* @param sysLogBusinessRequest
* @return PageResult<SysLogBusiness>
* @author fengshuonan
* @date 2023/07/21 15:00
*/
PageResult<SysLogBusiness> findPage(SysLogBusinessRequest sysLogBusinessRequest);
/**
*
*
* @author fengshuonan
* @since 2023/7/21 17:01
*/
void saveBatchLogs(SysLogBusiness context, List<String> batchContentList);
}

View File

@ -0,0 +1,129 @@
package cn.stylefeng.roses.kernel.log.business.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.log.business.entity.SysLogBusiness;
import cn.stylefeng.roses.kernel.log.business.enums.SysLogBusinessExceptionEnum;
import cn.stylefeng.roses.kernel.log.business.mapper.SysLogBusinessMapper;
import cn.stylefeng.roses.kernel.log.business.pojo.request.SysLogBusinessRequest;
import cn.stylefeng.roses.kernel.log.business.service.SysLogBusinessService;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
@Service
public class SysLogBusinessServiceImpl extends ServiceImpl<SysLogBusinessMapper, SysLogBusiness> implements SysLogBusinessService {
@Override
public void add(SysLogBusinessRequest sysLogBusinessRequest) {
SysLogBusiness sysLogBusiness = new SysLogBusiness();
BeanUtil.copyProperties(sysLogBusinessRequest, sysLogBusiness);
this.save(sysLogBusiness);
}
@Override
public void del(SysLogBusinessRequest sysLogBusinessRequest) {
SysLogBusiness sysLogBusiness = this.querySysLogBusiness(sysLogBusinessRequest);
this.removeById(sysLogBusiness.getBusinessLogId());
}
@Override
public void edit(SysLogBusinessRequest sysLogBusinessRequest) {
SysLogBusiness sysLogBusiness = this.querySysLogBusiness(sysLogBusinessRequest);
BeanUtil.copyProperties(sysLogBusinessRequest, sysLogBusiness);
this.updateById(sysLogBusiness);
}
@Override
public SysLogBusiness detail(SysLogBusinessRequest sysLogBusinessRequest) {
return this.querySysLogBusiness(sysLogBusinessRequest);
}
@Override
public PageResult<SysLogBusiness> findPage(SysLogBusinessRequest sysLogBusinessRequest) {
LambdaQueryWrapper<SysLogBusiness> wrapper = createWrapper(sysLogBusinessRequest);
Page<SysLogBusiness> sysRolePage = this.page(PageFactory.defaultPage(), wrapper);
return PageResultFactory.createPageResult(sysRolePage);
}
@Override
public void saveBatchLogs(SysLogBusiness context, List<String> batchContentList) {
if (ObjectUtil.isEmpty(batchContentList)) {
return;
}
List<SysLogBusiness> sysLogBusinesses = new ArrayList<>();
for (String content : batchContentList) {
SysLogBusiness sysLogBusiness = new SysLogBusiness();
BeanUtil.copyProperties(context, sysLogBusiness);
sysLogBusiness.setLogContent(content);
sysLogBusinesses.add(sysLogBusiness);
}
this.getBaseMapper().insertBatchSomeColumn(sysLogBusinesses);
}
@Override
public List<SysLogBusiness> findList(SysLogBusinessRequest sysLogBusinessRequest) {
LambdaQueryWrapper<SysLogBusiness> wrapper = this.createWrapper(sysLogBusinessRequest);
return this.list(wrapper);
}
/**
*
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
private SysLogBusiness querySysLogBusiness(SysLogBusinessRequest sysLogBusinessRequest) {
SysLogBusiness sysLogBusiness = this.getById(sysLogBusinessRequest.getBusinessLogId());
if (ObjectUtil.isEmpty(sysLogBusiness)) {
throw new ServiceException(SysLogBusinessExceptionEnum.SYS_LOG_BUSINESS_NOT_EXISTED);
}
return sysLogBusiness;
}
/**
* wrapper
*
* @author fengshuonan
* @date 2023/07/21 15:00
*/
private LambdaQueryWrapper<SysLogBusiness> createWrapper(SysLogBusinessRequest sysLogBusinessRequest) {
LambdaQueryWrapper<SysLogBusiness> queryWrapper = new LambdaQueryWrapper<>();
// 根据日志类型编码查询
String logTypeCode = sysLogBusinessRequest.getLogTypeCode();
queryWrapper.eq(ObjectUtil.isNotEmpty(logTypeCode), SysLogBusiness::getLogTypeCode, logTypeCode);
// 根据调用链日志信息查询
Long traceId = sysLogBusinessRequest.getTraceId();
queryWrapper.eq(ObjectUtil.isNotNull(traceId), SysLogBusiness::getTraceId, traceId);
// 根据文本检索内容查询
String searchText = sysLogBusinessRequest.getSearchText();
if (ObjectUtil.isNotEmpty(searchText)) {
queryWrapper.nested(wrap -> {
wrap.like(SysLogBusiness::getLogTitle, searchText).or().like(SysLogBusiness::getLogContent, searchText);
});
}
return queryWrapper;
}
}