Merge pull request #461 from mabaiwan/develop

Remove custom log component
pull/467/head
小马哥 2 years ago committed by GitHub
commit fad5985f6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -65,7 +65,7 @@ Hippo-4J 获得了一些宝贵的荣誉,这属于每一位对 Hippo-4J 做出
## 鸣谢
Hippo-4J 项目基于或参考以下项目:[nacos](https://github.com/alibaba/nacos)、[eureka](https://github.com/Netflix/Eureka)、[mzt-biz-log](https://github.com/mouzt/mzt-biz-log)
Hippo-4J 项目基于或参考以下项目:[nacos](https://github.com/alibaba/nacos)、[eureka](https://github.com/Netflix/Eureka)。
感谢 JetBrains 提供的免费开源 License

@ -55,11 +55,6 @@
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>log-record-tool</artifactId>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-adapter-base</artifactId>
@ -69,5 +64,10 @@
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
</dependencies>
</project>

@ -17,16 +17,16 @@
package cn.hippo4j.config.mapper;
import cn.hippo4j.config.model.LogRecordInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.hippo4j.tools.logrecord.model.LogRecordInfo;
import org.apache.ibatis.annotations.Mapper;
/**
* Log record mapper.
* Operation log mapper.
*
* @author chen.ma
* @date 2021/10/24 21:01
*/
@Mapper
public interface LogRecordMapper extends BaseMapper<LogRecordInfo> {
public interface OperationLogMapper extends BaseMapper<LogRecordInfo> {
}

@ -15,53 +15,71 @@
* limitations under the License.
*/
package cn.hippo4j.tools.logrecord.model;
package cn.hippo4j.config.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import java.util.Date;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 17:47
* Log record info.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@AllArgsConstructor
@TableName("log_record_info")
public class LogRecordInfo {
/**
* ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* Tenant
*/
private String tenant;
@NotBlank(message = "bizKey required")
@Length(max = 200, message = "appKey max length is 200")
/**
* Biz key
*/
private String bizKey;
@NotBlank(message = "bizNo required")
@Length(max = 200, message = "bizNo max length is 200")
/**
* Biz no
*/
private String bizNo;
@NotBlank(message = "operator required")
@Length(max = 63, message = "operator max length 63")
/**
* Operator
*/
private String operator;
@NotBlank(message = "opAction required")
@Length(max = 511, message = "operator max length 511")
/**
* Action
*/
private String action;
/**
* Category
*/
private String category;
private Date createTime;
/**
* Detail
*/
private String detail;
/**
* Create time
*/
private Date createTime;
}

@ -17,24 +17,28 @@
package cn.hippo4j.config.service.biz;
import cn.hippo4j.config.model.LogRecordInfo;
import cn.hippo4j.config.model.biz.log.LogRecordQueryReqDTO;
import cn.hippo4j.config.model.biz.log.LogRecordRespDTO;
import com.baomidou.mybatisplus.core.metadata.IPage;
/**
* .
*
* @author chen.ma
* @date 2021/11/17 21:41
* Operation log.
*/
public interface LogRecordBizService {
public interface OperationLogService {
/**
* .
* Query operation log.
*
* @param pageQuery
* @return
*/
IPage<LogRecordRespDTO> queryPage(LogRecordQueryReqDTO pageQuery);
/**
* Record.
*
* @param requestParam
*/
void record(LogRecordInfo requestParam);
}

@ -27,11 +27,12 @@ import cn.hippo4j.config.mapper.ConfigInstanceMapper;
import cn.hippo4j.config.model.ConfigAllInfo;
import cn.hippo4j.config.model.ConfigInfoBase;
import cn.hippo4j.config.model.ConfigInstanceInfo;
import cn.hippo4j.config.model.LogRecordInfo;
import cn.hippo4j.config.service.ConfigCacheService;
import cn.hippo4j.config.service.ConfigChangePublisher;
import cn.hippo4j.config.service.biz.ConfigService;
import cn.hippo4j.config.service.biz.OperationLogService;
import cn.hippo4j.config.toolkit.BeanUtil;
import cn.hippo4j.tools.logrecord.annotation.LogRecord;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -41,9 +42,7 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.stream.Collectors;
@ -53,9 +52,6 @@ import static cn.hippo4j.config.service.ConfigCacheService.getContent;
/**
* Config service impl.
*
* @author chen.ma
* @date 2021/6/20 15:21
*/
@Slf4j
@Service
@ -66,6 +62,8 @@ public class ConfigServiceImpl implements ConfigService {
private final ConfigInstanceMapper configInstanceMapper;
private final OperationLogService operationLogService;
@Override
public ConfigAllInfo findConfigAllInfo(String tpId, String itemId, String tenantId) {
LambdaQueryWrapper<ConfigAllInfo> wrapper = Wrappers.lambdaQuery(ConfigAllInfo.class)
@ -100,7 +98,7 @@ public class ConfigServiceImpl implements ConfigService {
}
ConfigAllInfo configAllInfo = findConfigAllInfo(params[0], params[1], params[2]);
if (configAllInfo == null && configInstance == null) {
throw new ServiceException("Thread pool configuration is not defined.");
throw new ServiceException("Thread pool configuration is not defined");
} else if (configAllInfo != null && configInstance == null) {
resultConfig = configAllInfo;
} else if (configAllInfo == null && configInstance != null) {
@ -138,7 +136,7 @@ public class ConfigServiceImpl implements ConfigService {
private void verification(String identify) {
if (StringUtil.isNotBlank(identify)) {
Map content = getContent(identify);
Assert.isTrue(CollectionUtil.isNotEmpty(content), "线程池实例不存在, 请尝试页面刷新.");
Assert.isTrue(CollectionUtil.isNotEmpty(content), "线程池实例不存在, 请尝试页面刷新");
}
}
@ -146,13 +144,13 @@ public class ConfigServiceImpl implements ConfigService {
config.setContent(ContentUtil.getPoolContent(config));
config.setMd5(Md5Util.getTpContentMd5(config));
try {
// 当前为单体应用, 后续支持集群部署时切换分布式锁.
// Currently it is a single application, and it supports switching distributed locks during cluster deployment in the future.
synchronized (ConfigService.class) {
ConfigAllInfo configAllInfo = configInfoMapper.selectOne(
Wrappers.lambdaQuery(ConfigAllInfo.class)
.eq(ConfigAllInfo::getTpId, config.getTpId())
.eq(ConfigAllInfo::getDelFlag, DelEnum.NORMAL.getIntCode()));
Assert.isNull(configAllInfo, "线程池配置已存在.");
Assert.isNull(configAllInfo, "线程池配置已存在");
if (SqlHelper.retBool(configInfoMapper.insert(config))) {
return config.getId();
}
@ -164,7 +162,6 @@ public class ConfigServiceImpl implements ConfigService {
return null;
}
@LogRecord(bizNo = "{{#config.itemId}}_{{#config.tpId}}", category = "THREAD_POOL_UPDATE", success = "核心线程: {{#config.coreSize}}, 最大线程: {{#config.maxSize}}, 队列类型: {{#config.queueType}}, 队列容量: {{#config.capacity}}, 拒绝策略: {{#config.rejectedType}}", detail = "{{#config.toString()}}")
public void updateConfigInfo(String identify, boolean isChangeNotice, ConfigAllInfo config) {
LambdaUpdateWrapper<ConfigAllInfo> wrapper = Wrappers.lambdaUpdate(ConfigAllInfo.class)
.eq(ConfigAllInfo::getTpId, config.getTpId())
@ -173,8 +170,10 @@ public class ConfigServiceImpl implements ConfigService {
config.setGmtCreate(null);
config.setContent(ContentUtil.getPoolContent(config));
config.setMd5(Md5Util.getTpContentMd5(config));
recordOperationLog(config);
try {
// 创建线程池配置实例临时配置, 也可以当作历史配置, 不过针对的是单节点
// Create a temporary configuration of a thread pool configuration instance,
// which can also be used as a historical configuration, but it is aimed at a single node.
if (StringUtil.isNotBlank(identify)) {
ConfigInstanceInfo instanceInfo = BeanUtil.convert(config, ConfigInstanceInfo.class);
instanceInfo.setInstanceId(identify);
@ -198,10 +197,23 @@ public class ConfigServiceImpl implements ConfigService {
}
}
private void recordOperationLog(ConfigAllInfo requestParam) {
LogRecordInfo logRecordInfo = LogRecordInfo.builder()
.bizKey(requestParam.getItemId() + "_" + requestParam.getTpId())
.bizNo(requestParam.getItemId() + "_" + requestParam.getTpId())
.operator(Optional.ofNullable(UserContext.getUserName()).orElse("-"))
.action(String.format("核心线程: %d, 最大线程: %d, 队列类型: %d, 队列容量: %d, 拒绝策略: %d", requestParam.getCoreSize(), requestParam.getMaxSize(), requestParam.getQueueType(), requestParam.getCapacity(), requestParam.getRejectedType()))
.category("THREAD_POOL_UPDATE")
.detail(JSONUtil.toJSONString(requestParam))
.createTime(new Date())
.build();
operationLogService.record(logRecordInfo);
}
/**
* .
* <p>
* {@link SynchronousQueue} {@link LinkedTransferQueue}
* Get queue size based on queue type.
*
* <p> {@link SynchronousQueue} {@link LinkedTransferQueue}
*
* @param config
* @return

@ -1,43 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.config.service.biz.impl;
import cn.hippo4j.config.mapper.LogRecordMapper;
import cn.hippo4j.tools.logrecord.model.LogRecordInfo;
import cn.hippo4j.tools.logrecord.service.LogRecordService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 20:57
*/
@Service
@AllArgsConstructor
public class LogRecordServiceImpl implements LogRecordService {
private final LogRecordMapper logRecordMapper;
@Override
public void record(LogRecordInfo logRecordInfo) {
logRecordMapper.insert(logRecordInfo);
}
}

@ -17,12 +17,12 @@
package cn.hippo4j.config.service.biz.impl;
import cn.hippo4j.config.mapper.LogRecordMapper;
import cn.hippo4j.config.mapper.OperationLogMapper;
import cn.hippo4j.config.model.LogRecordInfo;
import cn.hippo4j.config.model.biz.log.LogRecordQueryReqDTO;
import cn.hippo4j.config.model.biz.log.LogRecordRespDTO;
import cn.hippo4j.config.service.biz.LogRecordBizService;
import cn.hippo4j.config.service.biz.OperationLogService;
import cn.hippo4j.config.toolkit.BeanUtil;
import cn.hippo4j.tools.logrecord.model.LogRecordInfo;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -31,16 +31,16 @@ import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
/**
* .
* Operation log service impl.
*
* @author chen.ma
* @date 2021/11/17 21:50
*/
@Service
@AllArgsConstructor
public class LogRecordBizServiceImpl implements LogRecordBizService {
public class OperationLogServiceImpl implements OperationLogService {
private final LogRecordMapper logRecordMapper;
private final OperationLogMapper operationLogMapper;
@Override
public IPage<LogRecordRespDTO> queryPage(LogRecordQueryReqDTO pageQuery) {
@ -49,8 +49,12 @@ public class LogRecordBizServiceImpl implements LogRecordBizService {
.eq(StrUtil.isNotBlank(pageQuery.getCategory()), LogRecordInfo::getCategory, pageQuery.getCategory())
.eq(StrUtil.isNotBlank(pageQuery.getOperator()), LogRecordInfo::getOperator, pageQuery.getOperator())
.orderByDesc(LogRecordInfo::getCreateTime);
IPage<LogRecordInfo> selectPage = logRecordMapper.selectPage(pageQuery, queryWrapper);
IPage<LogRecordInfo> selectPage = operationLogMapper.selectPage(pageQuery, queryWrapper);
return selectPage.convert(each -> BeanUtil.convert(each, LogRecordRespDTO.class));
}
@Override
public void record(LogRecordInfo requestParam) {
operationLogMapper.insert(requestParam);
}
}

@ -17,8 +17,8 @@
package cn.hippo4j.config.service.biz.impl;
import cn.hippo4j.common.toolkit.Assert;
import cn.hippo4j.common.enums.DelEnum;
import cn.hippo4j.common.toolkit.Assert;
import cn.hippo4j.config.mapper.TenantInfoMapper;
import cn.hippo4j.config.model.TenantInfo;
import cn.hippo4j.config.model.biz.item.ItemQueryReqDTO;
@ -30,7 +30,6 @@ import cn.hippo4j.config.model.biz.tenant.TenantUpdateReqDTO;
import cn.hippo4j.config.service.biz.ItemService;
import cn.hippo4j.config.service.biz.TenantService;
import cn.hippo4j.config.toolkit.BeanUtil;
import cn.hippo4j.tools.logrecord.annotation.LogRecord;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
@ -102,7 +101,6 @@ public class TenantServiceImpl implements TenantService {
}
@Override
@LogRecord(prefix = "item", bizNo = "{{#reqDTO.tenantId}}_{{#reqDTO.tenantName}}", category = "TENANT_UPDATE", success = "更新租户, ID :: {{#reqDTO.id}}, 租户名称由 :: {TENANT{#reqDTO.id}} -> {{#reqDTO.tenantName}}", detail = "{{#reqDTO.toString()}}")
public void updateTenant(TenantUpdateReqDTO reqDTO) {
TenantInfo tenantInfo = BeanUtil.convert(reqDTO, TenantInfo.class);
int updateResult = tenantInfoMapper.update(tenantInfo, Wrappers

@ -18,16 +18,19 @@
package cn.hippo4j.config.service.biz.impl;
import cn.hippo4j.common.enums.DelEnum;
import cn.hippo4j.common.toolkit.JSONUtil;
import cn.hippo4j.common.toolkit.UserContext;
import cn.hippo4j.config.mapper.ConfigInfoMapper;
import cn.hippo4j.config.model.ConfigAllInfo;
import cn.hippo4j.config.model.LogRecordInfo;
import cn.hippo4j.config.model.biz.threadpool.ThreadPoolDelReqDTO;
import cn.hippo4j.config.model.biz.threadpool.ThreadPoolQueryReqDTO;
import cn.hippo4j.config.model.biz.threadpool.ThreadPoolRespDTO;
import cn.hippo4j.config.model.biz.threadpool.ThreadPoolSaveOrUpdateReqDTO;
import cn.hippo4j.config.service.biz.ConfigService;
import cn.hippo4j.config.service.biz.OperationLogService;
import cn.hippo4j.config.service.biz.ThreadPoolService;
import cn.hippo4j.config.toolkit.BeanUtil;
import cn.hippo4j.tools.logrecord.annotation.LogRecord;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
@ -35,13 +38,12 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
* Thread pool service impl.
*
* @author chen.ma
* @date 2021/6/30 21:26
*/
@Service
@AllArgsConstructor
@ -51,6 +53,8 @@ public class ThreadPoolServiceImpl implements ThreadPoolService {
private final ConfigInfoMapper configInfoMapper;
private final OperationLogService operationLogService;
@Override
public IPage<ThreadPoolRespDTO> queryThreadPoolPage(ThreadPoolQueryReqDTO reqDTO) {
LambdaQueryWrapper<ConfigAllInfo> wrapper = Wrappers.lambdaQuery(ConfigAllInfo.class)
@ -59,7 +63,6 @@ public class ThreadPoolServiceImpl implements ThreadPoolService {
.eq(!StringUtils.isBlank(reqDTO.getTpId()), ConfigAllInfo::getTpId, reqDTO.getTpId())
.eq(ConfigAllInfo::getDelFlag, DelEnum.NORMAL)
.orderByDesc(ConfigAllInfo::getGmtCreate);
return configInfoMapper.selectPage(reqDTO, wrapper).convert(each -> BeanUtil.convert(each, ThreadPoolRespDTO.class));
}
@ -73,7 +76,6 @@ public class ThreadPoolServiceImpl implements ThreadPoolService {
public List<ThreadPoolRespDTO> getThreadPoolByItemId(String itemId) {
LambdaQueryWrapper<ConfigAllInfo> queryWrapper = Wrappers.lambdaQuery(ConfigAllInfo.class)
.eq(ConfigAllInfo::getItemId, itemId);
List<ConfigAllInfo> selectList = configInfoMapper.selectList(queryWrapper);
return BeanUtil.convert(selectList, ThreadPoolRespDTO.class);
}
@ -83,14 +85,27 @@ public class ThreadPoolServiceImpl implements ThreadPoolService {
configService.insertOrUpdate(identify, false, BeanUtil.convert(reqDTO, ConfigAllInfo.class));
}
@LogRecord(bizNo = "{{#reqDTO.itemId}}_{{#reqDTO.tpId}}", category = "THREAD_POOL_DELETE", success = "删除线程池: {{#reqDTO.tpId}}", detail = "{{#reqDTO.toString()}}")
@Override
public void deletePool(ThreadPoolDelReqDTO reqDTO) {
public void deletePool(ThreadPoolDelReqDTO requestParam) {
configInfoMapper.delete(
Wrappers.lambdaUpdate(ConfigAllInfo.class)
.eq(ConfigAllInfo::getTenantId, reqDTO.getTenantId())
.eq(ConfigAllInfo::getItemId, reqDTO.getItemId())
.eq(ConfigAllInfo::getTpId, reqDTO.getTpId()));
.eq(ConfigAllInfo::getTenantId, requestParam.getTenantId())
.eq(ConfigAllInfo::getItemId, requestParam.getItemId())
.eq(ConfigAllInfo::getTpId, requestParam.getTpId()));
recordOperationLog(requestParam);
}
private void recordOperationLog(ThreadPoolDelReqDTO requestParam) {
LogRecordInfo logRecordInfo = LogRecordInfo.builder()
.bizKey(requestParam.getItemId() + "_" + requestParam.getTpId())
.bizNo(requestParam.getItemId() + "_" + requestParam.getTpId())
.operator(Optional.ofNullable(UserContext.getUserName()).orElse("-"))
.action("删除线程池: " + requestParam.getTpId())
.category("THREAD_POOL_DELETE")
.detail(JSONUtil.toJSONString(requestParam))
.createTime(new Date())
.build();
operationLogService.record(logRecordInfo);
}
@Override

@ -1,42 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.config.service.handler;
import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.toolkit.UserContext;
import cn.hippo4j.tools.logrecord.model.Operator;
import cn.hippo4j.tools.logrecord.service.OperatorGetService;
import org.springframework.stereotype.Component;
/**
* Custom operator get service.
*
* @author chen.ma
* @date 2021/11/28 17:57
*/
@Component
public class CustomOperatorGetServiceImpl implements OperatorGetService {
@Override
public Operator getUser() {
String userName = UserContext.getUserName();
userName = StringUtil.isEmpty(userName) ? "-" : userName;
return new Operator(userName);
}
}

@ -1,56 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.config.service.handler;
import cn.hippo4j.config.model.biz.tenant.TenantRespDTO;
import cn.hippo4j.config.service.biz.TenantService;
import cn.hippo4j.tools.logrecord.service.ParseFunction;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 22:07
*/
@Component
@AllArgsConstructor
public class TenantIdFunctionServiceImpl implements ParseFunction {
private final TenantService tenantService;
@Override
public boolean executeBefore() {
return true;
}
@Override
public String functionName() {
return "TENANT";
}
@Override
public String apply(String tenantId) {
TenantRespDTO tenant = tenantService.getTenantById(tenantId);
return Optional.ofNullable(tenant).map(TenantRespDTO::getTenantName).orElse("");
}
}

@ -22,7 +22,7 @@ import cn.hippo4j.common.web.base.Result;
import cn.hippo4j.common.web.base.Results;
import cn.hippo4j.config.model.biz.log.LogRecordQueryReqDTO;
import cn.hippo4j.config.model.biz.log.LogRecordRespDTO;
import cn.hippo4j.config.service.biz.LogRecordBizService;
import cn.hippo4j.config.service.biz.OperationLogService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
@ -38,10 +38,10 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping(Constants.BASE_PATH + "/log")
public class LogRecordController {
private final LogRecordBizService logRecordBizService;
private final OperationLogService operationLogService;
@PostMapping("/query/page")
public Result<IPage<LogRecordRespDTO>> queryPage(@RequestBody LogRecordQueryReqDTO queryReqDTO) {
return Results.success(logRecordBizService.queryPage(queryReqDTO));
return Results.success(operationLogService.queryPage(queryReqDTO));
}
}

@ -22,12 +22,6 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Example application.
*
* @author chen.ma
* @date 2022/01/23 21:06
*/
@EnableTransactionManagement
@SpringBootApplication(scanBasePackages = "cn.hippo4j")
@MapperScan(basePackages = {"cn.hippo4j.config.mapper", "cn.hippo4j.auth.mapper"})

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-tool</artifactId>
<version>${revision}</version>
</parent>
<artifactId>log-record-tool</artifactId>
<packaging>jar</packaging>
<name>log-record-tool</name>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
</dependencies>
</project>

@ -1,29 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.annotation;
/**
* Log field, used to mark entity properties that need to be compared.
*
* @author chen.ma
* @date 2021/10/23 21:29
*/
public @interface LogField {
String name();
}

@ -1,98 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.annotation;
import cn.hippo4j.tools.logrecord.enums.LogRecordTypeEnum;
import java.lang.annotation.*;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 21:29
*/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogRecord {
/**
*
*
* @return
*/
String prefix() default "";
/**
*
*
* @return
*/
String success();
/**
*
*
* @return
*/
String fail() default "";
/**
*
*
* @return
*/
String operator() default "";
/**
*
*
* @return
*/
String bizNo();
/**
*
*
* @return
*/
String detail() default "";
/**
*
*
* @return
*/
String category();
/**
*
*
* @return
*/
LogRecordTypeEnum recordType() default LogRecordTypeEnum.COMPLETE;
/**
*
*
* @return
*/
String condition() default "";
}

@ -1,224 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.aop;
import cn.hippo4j.tools.logrecord.annotation.LogRecord;
import cn.hippo4j.tools.logrecord.context.LogRecordContext;
import cn.hippo4j.tools.logrecord.model.LogRecordInfo;
import cn.hippo4j.tools.logrecord.model.LogRecordOps;
import cn.hippo4j.tools.logrecord.model.MethodExecuteResult;
import cn.hippo4j.tools.logrecord.parse.LogRecordOperationSource;
import cn.hippo4j.tools.logrecord.parse.LogRecordValueParser;
import cn.hippo4j.tools.logrecord.service.LogRecordService;
import cn.hippo4j.tools.logrecord.service.OperatorGetService;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
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.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.*;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 22:00
*/
@Slf4j
@Aspect
@Component
@AllArgsConstructor
public class LogRecordAspect {
private final LogRecordService bizLogService;
private final LogRecordValueParser logRecordValueParser;
private final OperatorGetService operatorGetService;
private final LogRecordOperationSource logRecordOperationSource;
private final ConfigurableEnvironment environment;
@Around("@annotation(logRecord)")
public Object logRecord(ProceedingJoinPoint joinPoint, LogRecord logRecord) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
Object target = joinPoint.getTarget();
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
Object[] args = joinPoint.getArgs();
LogRecordContext.putEmptySpan();
Object result = null;
Collection<LogRecordOps> operations = Lists.newArrayList();
Map<String, String> functionNameAndReturnMap = Maps.newHashMap();
MethodExecuteResult methodExecuteResult = new MethodExecuteResult(true);
try {
operations = logRecordOperationSource.computeLogRecordOperations(method, targetClass);
List<String> spElTemplates = getBeforeExecuteFunctionTemplate(operations);
functionNameAndReturnMap = logRecordValueParser.processBeforeExecuteFunctionTemplate(spElTemplates, targetClass, method, args);
} catch (Exception ex) {
log.error("Log record parse before function exception.", ex);
}
try {
result = joinPoint.proceed();
} catch (Exception ex) {
methodExecuteResult = new MethodExecuteResult(false, ex, ex.getMessage());
}
try {
if (!CollectionUtils.isEmpty(operations)) {
recordExecute(result, method, args, operations, targetClass,
methodExecuteResult.isSuccess(), methodExecuteResult.getErrorMsg(), functionNameAndReturnMap);
}
} catch (Exception ex) {
log.error("Log record parse exception.", ex);
} finally {
LogRecordContext.clear();
}
if (methodExecuteResult.getThrowable() != null) {
throw methodExecuteResult.getThrowable();
}
return result;
}
private List<String> getBeforeExecuteFunctionTemplate(Collection<LogRecordOps> operations) {
List<String> spElTemplates = new ArrayList();
for (LogRecordOps operation : operations) {
// 执行之前的函数
List<String> templates = getSpElTemplates(operation, operation.getSuccessLogTemplate());
if (!CollectionUtils.isEmpty(templates)) {
spElTemplates.addAll(templates);
}
}
return spElTemplates;
}
private List<String> getSpElTemplates(LogRecordOps operation, String action) {
List<String> spElTemplates = Lists.newArrayList(operation.getBizKey(), operation.getBizNo(), action, operation.getDetail());
if (!StringUtils.isEmpty(operation.getCondition())) {
spElTemplates.add(operation.getCondition());
}
return spElTemplates;
}
/**
* .
*
* @param ret
* @param method
* @param args
* @param operations
* @param targetClass
* @param success
* @param errorMsg
* @param functionNameAndReturnMap
*/
private void recordExecute(Object ret, Method method, Object[] args, Collection<LogRecordOps> operations,
Class<?> targetClass, boolean success, String errorMsg, Map<String, String> functionNameAndReturnMap) {
for (LogRecordOps operation : operations) {
try {
String action = getActionContent(success, operation);
if (StringUtils.isEmpty(action)) {
// 没有日志内容则忽略
continue;
}
// 获取需要解析的表达式
List<String> spElTemplates = getSpElTemplates(operation, action);
String operatorIdFromService = getOperatorIdFromServiceAndPutTemplate(operation, spElTemplates);
Map<String, String> expressionValues = logRecordValueParser.processTemplate(spElTemplates, ret, targetClass, method, args, errorMsg, functionNameAndReturnMap);
if (logConditionPassed(operation.getCondition(), expressionValues)) {
String tenant = environment.getProperty("tenant");
LogRecordInfo logRecordInfo = LogRecordInfo.builder()
.tenant(StringUtils.isEmpty(tenant) ? "hippo4j" : tenant)
.bizKey(expressionValues.get(operation.getBizKey()))
.bizNo(expressionValues.get(operation.getBizNo()))
.operator(getRealOperatorId(operation, operatorIdFromService, expressionValues))
.category(operation.getCategory())
.detail(expressionValues.get(operation.getDetail()))
.action(expressionValues.get(action))
.createTime(new Date())
.build();
// 如果 action 为空, 不记录日志
if (StringUtils.isEmpty(logRecordInfo.getAction())) {
continue;
}
// save log 需要新开事务, 失败日志不能因为事务回滚而丢失
Preconditions.checkNotNull(bizLogService, "bizLogService not init");
bizLogService.record(logRecordInfo);
}
} catch (Exception t) {
log.error("log record execute exception", t);
}
}
}
private String getActionContent(boolean success, LogRecordOps operation) {
if (success) {
return operation.getSuccessLogTemplate();
}
return operation.getFailLogTemplate();
}
private String getOperatorIdFromServiceAndPutTemplate(LogRecordOps operation, List<String> spElTemplates) {
String realOperatorId = "";
if (StringUtils.isEmpty(operation.getOperatorId())) {
realOperatorId = operatorGetService.getUser().getOperatorId();
if (StringUtils.isEmpty(realOperatorId)) {
throw new IllegalArgumentException("LogRecord operator is null");
}
} else {
spElTemplates.add(operation.getOperatorId());
}
return realOperatorId;
}
private boolean logConditionPassed(String condition, Map<String, String> expressionValues) {
return StringUtils.isEmpty(condition) || StringUtils.endsWithIgnoreCase(expressionValues.get(condition), "true");
}
private String getRealOperatorId(LogRecordOps operation, String operatorIdFromService, Map<String, String> expressionValues) {
return !StringUtils.isEmpty(operatorIdFromService) ? operatorIdFromService : expressionValues.get(operation.getOperatorId());
}
}

@ -1,86 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.config;
import cn.hippo4j.tools.logrecord.parse.LogRecordOperationSource;
import cn.hippo4j.tools.logrecord.parse.LogRecordValueParser;
import cn.hippo4j.tools.logrecord.service.FunctionService;
import cn.hippo4j.tools.logrecord.service.LogRecordService;
import cn.hippo4j.tools.logrecord.service.OperatorGetService;
import cn.hippo4j.tools.logrecord.service.ParseFunction;
import cn.hippo4j.tools.logrecord.service.impl.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import java.util.List;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 22:49
*/
@Configuration
public class LogRecordConfig {
@Bean
@ConditionalOnMissingBean(ParseFunction.class)
public DefaultParseFunction parseFunction() {
return new DefaultParseFunction();
}
@Bean
public ParseFunctionFactory parseFunctionFactory(@Autowired List<ParseFunction> parseFunctions) {
return new ParseFunctionFactory(parseFunctions);
}
@Bean
@ConditionalOnMissingBean(FunctionService.class)
public FunctionService functionService(ParseFunctionFactory parseFunctionFactory) {
return new DefaultFunctionServiceImpl(parseFunctionFactory);
}
@Bean
@Role(BeanDefinition.ROLE_APPLICATION)
@ConditionalOnMissingBean(OperatorGetService.class)
public OperatorGetService operatorGetService() {
return new DefaultOperatorGetServiceImpl();
}
@Bean
@ConditionalOnMissingBean(LogRecordService.class)
@Role(BeanDefinition.ROLE_APPLICATION)
public LogRecordService recordService() {
return new DefaultLogRecordServiceImpl();
}
@Bean
public LogRecordValueParser logRecordValueParser() {
return new LogRecordValueParser();
}
@Bean
public LogRecordOperationSource logRecordOperationSource() {
return new LogRecordOperationSource();
}
}

@ -1,63 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.context;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.Stack;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 21:47
*/
public class LogRecordContext {
private static final ThreadLocal<Stack<Map<String, Object>>> VARIABLE_MAP_STACK = new TransmittableThreadLocal();
/**
* .
*/
public static void clear() {
if (VARIABLE_MAP_STACK.get() != null) {
VARIABLE_MAP_STACK.get().pop();
}
}
/**
* .
*/
public static void putEmptySpan() {
Stack<Map<String, Object>> mapStack = VARIABLE_MAP_STACK.get();
if (mapStack == null) {
Stack<Map<String, Object>> stack = new Stack<>();
VARIABLE_MAP_STACK.set(stack);
}
VARIABLE_MAP_STACK.get().push(Maps.newHashMap());
}
public static Map<String, Object> getVariables() {
Stack<Map<String, Object>> mapStack = VARIABLE_MAP_STACK.get();
return mapStack.peek();
}
}

@ -1,38 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.enums;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 21:54
*/
public enum LogRecordTypeEnum {
/**
*
*/
TEMPLATE,
/**
*
*/
COMPLETE
}

@ -1,49 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.model;
import lombok.Builder;
import lombok.Data;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 21:07
*/
@Data
@Builder
public class LogRecordOps {
private String successLogTemplate;
private String failLogTemplate;
private String operatorId;
private String bizKey;
private String bizNo;
private String category;
private String detail;
private String condition;
}

@ -1,50 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.model;
import lombok.*;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 21:59
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
public class MethodExecuteResult {
/**
*
*/
@NonNull
private boolean success;
/**
*
*/
private Throwable throwable;
/**
*
*/
private String errorMsg;
}

@ -1,40 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 21:44
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Operator {
/**
* Id
*/
private String operatorId;
}

@ -1,49 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.parse;
import cn.hippo4j.tools.logrecord.context.LogRecordContext;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.ParameterNameDiscoverer;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Log record evaluation context.
*
* @author chen.ma
* @date 2021/10/24 21:25
*/
public class LogRecordEvaluationContext extends MethodBasedEvaluationContext {
public LogRecordEvaluationContext(Object rootObject, Method method, Object[] arguments,
ParameterNameDiscoverer parameterNameDiscoverer, Object ret, String errorMsg) {
super(rootObject, method, arguments, parameterNameDiscoverer);
Map<String, Object> variables = LogRecordContext.getVariables();
if (variables != null && variables.size() > 0) {
for (Map.Entry<String, Object> entry : variables.entrySet()) {
setVariable(entry.getKey(), entry.getValue());
}
}
setVariable("_ret", ret);
setVariable("_errorMsg", errorMsg);
}
}

@ -1,70 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.parse;
import com.google.common.collect.Maps;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Log record expression evaluator.
*
* @author chen.ma
* @date 2021/10/24 22:22
*/
public class LogRecordExpressionEvaluator extends CachedExpressionEvaluator {
private Map<ExpressionKey, Expression> expressionCache = Maps.newConcurrentMap();
private final Map<AnnotatedElementKey, Method> targetMethodCache = Maps.newConcurrentMap();
public String parseExpression(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
Object value = getExpression(this.expressionCache, methodKey, conditionExpression).getValue(evalContext, Object.class);
return value == null ? "" : value.toString();
}
public EvaluationContext createEvaluationContext(Method method, Object[] args, Class<?> targetClass,
Object result, String errorMsg, BeanFactory beanFactory) {
Method targetMethod = getTargetMethod(targetClass, method);
LogRecordEvaluationContext evaluationContext = new LogRecordEvaluationContext(
null, targetMethod, args, getParameterNameDiscoverer(), result, errorMsg);
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}
private Method getTargetMethod(Class<?> targetClass, Method method) {
AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
Method targetMethod = this.targetMethodCache.get(methodKey);
if (targetMethod == null) {
targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
this.targetMethodCache.put(methodKey, targetMethod);
}
return targetMethod;
}
}

@ -1,88 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.parse;
import cn.hippo4j.tools.logrecord.annotation.LogRecord;
import cn.hippo4j.tools.logrecord.model.LogRecordOps;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 22:02
*/
public class LogRecordOperationSource {
public Collection<LogRecordOps> computeLogRecordOperations(Method method, Class<?> targetClass) {
if (!Modifier.isPublic(method.getModifiers())) {
return null;
}
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
return parseLogRecordAnnotations(specificMethod);
}
private Collection<LogRecordOps> parseLogRecordAnnotations(AnnotatedElement ae) {
Collection<LogRecord> logRecordAnnotations = AnnotatedElementUtils.getAllMergedAnnotations(ae, LogRecord.class);
Collection<LogRecordOps> ret = null;
if (!logRecordAnnotations.isEmpty()) {
ret = new ArrayList(1);
for (LogRecord logRecord : logRecordAnnotations) {
ret.add(parseLogRecordAnnotation(ae, logRecord));
}
}
return ret;
}
private LogRecordOps parseLogRecordAnnotation(AnnotatedElement ae, LogRecord logRecord) {
LogRecordOps recordOps = LogRecordOps.builder()
.successLogTemplate(logRecord.success())
.failLogTemplate(logRecord.fail())
.bizKey(logRecord.prefix().concat(logRecord.bizNo()))
.bizNo(logRecord.bizNo())
.operatorId(logRecord.operator())
.category(StringUtils.isEmpty(logRecord.category()) ? logRecord.prefix() : logRecord.category())
.detail(logRecord.detail())
.condition(logRecord.condition())
.build();
validateLogRecordOperation(ae, recordOps);
return recordOps;
}
private void validateLogRecordOperation(AnnotatedElement ae, LogRecordOps recordOps) {
if (!StringUtils.hasText(recordOps.getSuccessLogTemplate()) && !StringUtils.hasText(recordOps.getFailLogTemplate())) {
throw new IllegalStateException("Invalid logRecord annotation configuration on '" +
ae.toString() + "'. 'one of successTemplate and failLogTemplate' attribute must be set.");
}
}
}

@ -1,124 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.parse;
import cn.hippo4j.tools.logrecord.service.FunctionService;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Log record value parser.
*
* @author chen.ma
* @date 2021/10/24 21:27
*/
public class LogRecordValueParser implements BeanFactoryAware {
@Autowired
private FunctionService functionService;
protected BeanFactory beanFactory;
private final LogRecordExpressionEvaluator expressionEvaluator = new LogRecordExpressionEvaluator();
private static final Pattern PATTERN = Pattern.compile("\\{\\s*(\\w*)\\s*\\{(.*?)}}");
public Map<String, String> processTemplate(Collection<String> templates, Object ret,
Class<?> targetClass, Method method, Object[] args, String errorMsg,
Map<String, String> beforeFunctionNameAndReturnMap) {
Map<String, String> expressionValues = Maps.newHashMap();
EvaluationContext evaluationContext = expressionEvaluator.createEvaluationContext(method, args, targetClass, ret, errorMsg, beanFactory);
for (String expressionTemplate : templates) {
if (expressionTemplate.contains("{")) {
Matcher matcher = PATTERN.matcher(expressionTemplate);
StringBuffer parsedStr = new StringBuffer();
while (matcher.find()) {
String expression = matcher.group(2);
AnnotatedElementKey annotatedElementKey = new AnnotatedElementKey(method, targetClass);
String value = expressionEvaluator.parseExpression(expression, annotatedElementKey, evaluationContext);
String functionReturnValue = getFunctionReturnValue(beforeFunctionNameAndReturnMap, value, matcher.group(1));
matcher.appendReplacement(parsedStr, Strings.nullToEmpty(functionReturnValue));
}
matcher.appendTail(parsedStr);
expressionValues.put(expressionTemplate, parsedStr.toString());
} else {
expressionValues.put(expressionTemplate, expressionTemplate);
}
}
return expressionValues;
}
public Map<String, String> processBeforeExecuteFunctionTemplate(Collection<String> templates, Class<?> targetClass, Method method, Object[] args) {
Map<String, String> functionNameAndReturnValueMap = new HashMap(16);
EvaluationContext evaluationContext = expressionEvaluator.createEvaluationContext(method, args, targetClass, null, null, beanFactory);
for (String expressionTemplate : templates) {
if (expressionTemplate.contains("{")) {
Matcher matcher = PATTERN.matcher(expressionTemplate);
while (matcher.find()) {
String expression = matcher.group(2);
if (expression.contains("#_ret") || expression.contains("#_errorMsg")) {
continue;
}
AnnotatedElementKey annotatedElementKey = new AnnotatedElementKey(method, targetClass);
String functionName = matcher.group(1);
if (functionService.beforeFunction(functionName)) {
String value = expressionEvaluator.parseExpression(expression, annotatedElementKey, evaluationContext);
String functionReturnValue = getFunctionReturnValue(null, value, functionName);
functionNameAndReturnValueMap.put(functionName, functionReturnValue);
}
}
}
}
return functionNameAndReturnValueMap;
}
private String getFunctionReturnValue(Map<String, String> beforeFunctionNameAndReturnMap, String value, String functionName) {
String functionReturnValue = "";
if (beforeFunctionNameAndReturnMap != null) {
functionReturnValue = beforeFunctionNameAndReturnMap.get(functionName);
}
if (StringUtils.isEmpty(functionReturnValue)) {
functionReturnValue = functionService.apply(functionName, value);
}
return functionReturnValue;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}

@ -1,45 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 21:30
*/
public interface FunctionService {
/**
* .
*
* @param functionName
* @param value
* @return
*/
String apply(String functionName, String value);
/**
* .
*
* @param functionName
* @return
*/
boolean beforeFunction(String functionName);
}

@ -1,37 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service;
import cn.hippo4j.tools.logrecord.model.LogRecordInfo;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 22:43
*/
public interface LogRecordService {
/**
* .
*
* @param logRecordInfo
*/
void record(LogRecordInfo logRecordInfo);
}

@ -1,37 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service;
import cn.hippo4j.tools.logrecord.model.Operator;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 22:46
*/
public interface OperatorGetService {
/**
* .
*
* @return
*/
Operator getUser();
}

@ -1,52 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 22:40
*/
public interface ParseFunction {
/**
* .
*
* @return
*/
default boolean executeBefore() {
return false;
}
/**
* .
*
* @return
*/
String functionName();
/**
* .
*
* @param value
* @return
*/
String apply(String value);
}

@ -1,49 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service.impl;
import cn.hippo4j.tools.logrecord.service.FunctionService;
import cn.hippo4j.tools.logrecord.service.ParseFunction;
import lombok.AllArgsConstructor;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 21:54
*/
@AllArgsConstructor
public class DefaultFunctionServiceImpl implements FunctionService {
private final ParseFunctionFactory parseFunctionFactory;
@Override
public String apply(String functionName, String value) {
ParseFunction function = parseFunctionFactory.getFunction(functionName);
if (function == null) {
return value;
}
return function.apply(value);
}
@Override
public boolean beforeFunction(String functionName) {
return parseFunctionFactory.isBeforeFunction(functionName);
}
}

@ -1,38 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service.impl;
import cn.hippo4j.tools.logrecord.model.LogRecordInfo;
import cn.hippo4j.tools.logrecord.service.LogRecordService;
import lombok.extern.slf4j.Slf4j;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 17:59
*/
@Slf4j
public class DefaultLogRecordServiceImpl implements LogRecordService {
@Override
public void record(LogRecordInfo logRecordInfo) {
log.info("Log print :: {}", logRecordInfo);
}
}

@ -1,36 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service.impl;
import cn.hippo4j.tools.logrecord.model.Operator;
import cn.hippo4j.tools.logrecord.service.OperatorGetService;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 17:58
*/
public class DefaultOperatorGetServiceImpl implements OperatorGetService {
@Override
public Operator getUser() {
return new Operator("-");
}
}

@ -1,45 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service.impl;
import cn.hippo4j.tools.logrecord.service.ParseFunction;
/**
* .
*
* @author chen.ma
* @date 2021/10/24 17:57
*/
public class DefaultParseFunction implements ParseFunction {
@Override
public boolean executeBefore() {
return true;
}
@Override
public String functionName() {
return null;
}
@Override
public String apply(String value) {
return null;
}
}

@ -1,73 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package cn.hippo4j.tools.logrecord.service.impl;
import cn.hippo4j.tools.logrecord.service.ParseFunction;
import com.google.common.collect.Maps;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Map;
/**
* .
*
* @author chen.ma
* @date 2021/10/23 22:39
*/
public class ParseFunctionFactory {
private Map<String, ParseFunction> allFunctionMap;
public ParseFunctionFactory(List<ParseFunction> parseFunctions) {
if (CollectionUtils.isEmpty(parseFunctions)) {
return;
}
allFunctionMap = Maps.newHashMap();
for (ParseFunction parseFunction : parseFunctions) {
if (StringUtils.isEmpty(parseFunction.functionName())) {
continue;
}
allFunctionMap.put(parseFunction.functionName(), parseFunction);
}
}
/**
* .
*
* @param functionName
* @return
*/
public ParseFunction getFunction(String functionName) {
return allFunctionMap.get(functionName);
}
/**
* .
*
* @param functionName
* @return
*/
public boolean isBeforeFunction(String functionName) {
return allFunctionMap.get(functionName) != null && allFunctionMap.get(functionName).executeBefore();
}
}

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-all</artifactId>
<version>${revision}</version>
</parent>
<artifactId>hippo4j-tool</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>Dynamic thread pool tool class module</description>
<modules>
<module>log-record-tool</module>
</modules>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
</project>

@ -7,7 +7,6 @@
<artifactId>hippo4j-all</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>Dynamic observable thread pool framework</description>
<modules>
@ -23,7 +22,6 @@
<module>hippo4j-monitor</module>
<module>hippo4j-server</module>
<module>hippo4j-spring-boot</module>
<module>hippo4j-tool</module>
</modules>
<properties>

Loading…
Cancel
Save