Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
.classpath
.project
.settings/
.vscode/

#intellij
.idea
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ public class LogRecordOps {
private String extra;
private String condition;
private String isSuccess;
private String list;
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,17 @@
/**
* @return 是否记录日志
*/
String condition() default "";
boolean condition() default true;

/**
* 记录成功日志的条件
*
* @return 表示成功的表达式,默认为空,代表不抛异常为成功
*/
String successCondition() default "";

/**
* @return 批量操作时的列表变量名称
*/
String list() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
Expand All @@ -36,6 +38,8 @@
@Slf4j
public class LogRecordInterceptor extends LogRecordValueParser implements MethodInterceptor, Serializable, SmartInitializingSingleton {

private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();

private LogRecordOperationSource logRecordOperationSource;

private String tenantId;
Expand Down Expand Up @@ -89,7 +93,26 @@ private Object execute(MethodInvocation invoker, Object target, Method method, O
stopWatch.start(MONITOR_TASK_AFTER_EXECUTE);
try {
if (!CollectionUtils.isEmpty(operations)) {
recordExecute(methodExecuteResult, functionNameAndReturnMap, operations);
// Handle list processing for each operation
for (LogRecordOps operation : operations) {
if (!StringUtils.isEmpty(operation.getList())) {
// Get the list variable from method arguments
Object listArg = getListArgument(args, method, operation.getList());
if (listArg instanceof List) {
List<?> listVariable = (List<?>) listArg;
for (Object item : listVariable) {
// Put the current item in context for SpEL evaluation
LogRecordContext.putVariable(operation.getList(), item);
// Record the log for this item
recordExecute(methodExecuteResult, functionNameAndReturnMap, Collections.singleton(operation));
// The context will be cleared in the finally block
}
}
} else {
// Normal single record processing
recordExecute(methodExecuteResult, functionNameAndReturnMap, Collections.singleton(operation));
}
}
}
} catch (Exception t) {
log.error("log record parse exception", t);
Expand All @@ -110,6 +133,24 @@ private Object execute(MethodInvocation invoker, Object target, Method method, O
return ret;
}

private Object getListArgument(Object[] args, Method method, String listName) {
String[] parameterNames = discoverer.getParameterNames(method);
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
if (parameterNames[i].equals(listName) && i < args.length) {
return args[i];
}
}
}
// If parameter name matching fails, try to find first List argument
for (Object arg : args) {
if (arg instanceof List) {
return arg;
}
}
return null;
}

private List<String> getBeforeExecuteFunctionTemplate(Collection<LogRecordOps> operations) {
List<String> spElTemplates = new ArrayList<>();
for (LogRecordOps operation : operations) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.mzt.logapi.beans.MethodExecuteResult;
import com.mzt.logapi.service.impl.DiffParseFunction;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
Expand Down Expand Up @@ -84,6 +85,62 @@ public Map<String, String> processTemplate(Collection<String> templates, MethodE
return expressionValues;
}

public List<Map<String, String>> processBatchTemplate(Collection<String> templates, MethodExecuteResult methodExecuteResult,
Map<String, String> beforeFunctionNameAndReturnMap, String listName) {
List<Map<String, String>> result = new ArrayList<>();
if (StringUtils.isEmpty(listName)) {
Map<String, String> singleResult = processTemplate(templates, methodExecuteResult, beforeFunctionNameAndReturnMap);
result.add(singleResult);
return result;
}

EvaluationContext evaluationContext = expressionEvaluator.createEvaluationContext(methodExecuteResult.getMethod(),
methodExecuteResult.getArgs(), methodExecuteResult.getTargetClass(), methodExecuteResult.getResult(),
methodExecuteResult.getErrorMsg(), beanFactory);

AnnotatedElementKey annotatedElementKey = new AnnotatedElementKey(methodExecuteResult.getMethod(), methodExecuteResult.getTargetClass());
Object listValue = expressionEvaluator.parseExpression("#" + listName, annotatedElementKey, evaluationContext);

if (!(listValue instanceof Collection)) {
throw new IllegalArgumentException("List parameter must be a Collection type");
}

Collection<?> collection = (Collection<?>) listValue;
for (Object item : collection) {
evaluationContext.setVariable("item", item);
Map<String, String> itemResult = new HashMap<>();

for (String expressionTemplate : templates) {
if (expressionTemplate.contains("{")) {
Matcher matcher = pattern.matcher(expressionTemplate);
StringBuffer parsedStr = new StringBuffer();
boolean sameDiff = false;

while (matcher.find()) {
String expression = matcher.group(2);
String functionName = matcher.group(1);
if (DiffParseFunction.diffFunctionName.equals(functionName)) {
expression = getDiffFunctionValue(evaluationContext, annotatedElementKey, expression);
sameDiff = Objects.equals("", expression);
} else {
// Replace list variable references in expression
expression = expression.replace("#" + listName + ".", "#item.");
Object value = expressionEvaluator.parseExpression(expression, annotatedElementKey, evaluationContext);
expression = logFunctionParser.getFunctionReturnValue(beforeFunctionNameAndReturnMap, value, expression, functionName);
}
matcher.appendReplacement(parsedStr, Matcher.quoteReplacement(expression == null ? "" : expression));
}
matcher.appendTail(parsedStr);
itemResult.put(expressionTemplate, recordSameDiff(sameDiff, diffSameWhetherSaveLog) ? parsedStr.toString() : expressionTemplate);
} else {
itemResult.put(expressionTemplate, expressionTemplate);
}
}
result.add(itemResult);
}
return result;
}

private boolean recordSameDiff(boolean sameDiff, boolean diffSameWhetherSaveLog) {
if(diffSameWhetherSaveLog == true) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.mzt.logserver.pojo.Result;
import com.mzt.logserver.pojo.User;

import java.util.List;

/**
* @author muzhantong
* create on 2020/6/12 11:07 上午
Expand Down Expand Up @@ -60,4 +62,6 @@ public interface IOrderService {
void fixedCopy(String text);

void fixedCopy2(User user, User oldUser);

boolean saveOrders(List<Order> orders);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
* @author muzhantong
Expand Down Expand Up @@ -89,16 +90,17 @@ public boolean createOrder_fail(Order order) {
}

@Override
@LogRecord(success = "更新了订单{ORDER_BEFORE{#order.orderId}},更新内容为...",
type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}",
extra = "{{#order.toString()}}")
public boolean updateBefore(Long orderId, Order order) {
order.setOrderId(10000L);
@LogRecord(success = "更新了订单{ORDER_BEFORE{#orderId}},更新内容为...",
type = LogRecordType.ORDER, bizNo = "{{#orderNo}}", list = "{{orders}}")
public boolean saveOrders(List<Order> orders) {
for (Order order : orders) {
System.out.println(order);
}
return false;
}

@Override
@LogRecord(success = "更新了订单{ORDER{#order.orderId}},更新内容为...",
@LogRecord(success = "更新了订单{ORDER{#orderId}},更新内容为...",
type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}",
extra = "{{#order.toString()}}")
public boolean updateAfter(Long orderId, Order order) {
Expand Down Expand Up @@ -163,9 +165,9 @@ public boolean diff2(Order newOrder) {
@Override
@LogRecord(success = "更新了订单{ORDER{#orderId}},更新内容为...",
type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}",
condition = "{{#condition == null}}")
condition = "#condition == null")
public boolean testCondition(Long orderId, Order order, String condition) {
return false;
return condition == null;
}

@Override
Expand All @@ -174,28 +176,28 @@ public boolean testCondition(Long orderId, Order order, String condition) {
public boolean testContextCallContext(Long orderId, Order order) {
LogRecordContext.putVariable("title", "外层调用");
userQueryService.getUserList(Lists.newArrayList("mzt"));
return false;
return true;
}

@Override
@LogRecord(success = "更新了订单{ORDER{#orderId}},更新内容为..{{#title}}",
type = LogRecordType.ORDER, subType = "{{#order.orderNo}}", bizNo = "{{#order.orderNo}}")
public boolean testSubTypeSpEl(Long orderId, Order order) {
return false;
return true;
}

@Override
@LogRecord(success = "更新了订单{ORDER{#orderId}},更新内容为..{{#title}}",
type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}")
public boolean testVariableInfo(Long orderId, Order order) {
return false;
return true;
}

@Override
@LogRecord(success = "更新成功了订单{ORDER{#orderId}},更新内容为...",
fail = "更新失败了订单{ORDER{#orderId}},更新内容为...",
type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}",
condition = "{{#condition == null}}", successCondition = "{{#result.code == 200}}")
condition = "#condition == null", successCondition = "#result.code == 200")
public Result<Boolean> testResultOnSuccess(Long orderId, Order order) {
Result<Boolean> result = new Result<>(200, "成功", true);
LogRecordContext.putVariable("result", result);
Expand All @@ -206,7 +208,7 @@ public Result<Boolean> testResultOnSuccess(Long orderId, Order order) {
@LogRecord(success = "更新成功了订单{ORDER{#orderId}},更新内容为...",
fail = "更新失败了订单{ORDER{#orderId}},更新内容为...",
type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}",
condition = "{{#condition == null}}", successCondition = "{{#result.code == 200}}")
condition = "#condition == null", successCondition = "#result.code == 200")
public Result<Boolean> testResultOnFail(Long orderId, Order order) {
Result<Boolean> result = new Result<>(500, "服务错误", false);
LogRecordContext.putVariable("result", result);
Expand All @@ -216,7 +218,7 @@ public Result<Boolean> testResultOnFail(Long orderId, Order order) {
@Override
@LogRecord(success = "更新成功了订单{ORDER{#orderId}},更新内容为...",
type = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}",
condition = "{{#condition == null}}", successCondition = "{{#result.code == 200}}")
condition = "#condition == null", successCondition = "#result.code == 200")
public Result<Boolean> testResultNoLog(Long orderId, Order order) {
Result<Boolean> result = new Result<>(500, "服务错误", false);
LogRecordContext.putVariable("result", result);
Expand Down Expand Up @@ -251,4 +253,10 @@ public void fixedCopy(String text) {
public void fixedCopy2(User user, User oldUser) {

}

@Override
public boolean updateBefore(Long orderId, Order order) {
order.setOrderId(10000L);
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -674,4 +674,40 @@ public void fixedCopy3() {
Assert.assertEquals(0, logRecordList.size());
logRecordService.clean();
}

@Test
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testBatchLoggingWithList() {
List<Order> orders = Lists.newArrayList();

Order order1 = new Order();
order1.setOrderNo("MT0000011");
order1.setProductName("超值优惠红烧肉套餐");
order1.setPurchaseName("张三");
orders.add(order1);

Order order2 = new Order();
order2.setOrderNo("MT0000012");
order2.setProductName("麻辣烫套餐");
order2.setPurchaseName("李四");
orders.add(order2);

orderService.saveOrders(orders);

// Verify first order log
List<LogRecord> logRecordList1 = logRecordService.queryLog(order1.getOrderNo(), LogRecordType.ORDER);
Assert.assertEquals(1, logRecordList1.size());
LogRecord logRecord1 = logRecordList1.get(0);
Assert.assertEquals("更新了订单xxxx,更新内容为...", logRecord1.getAction());
Assert.assertEquals(order1.getOrderNo(), logRecord1.getBizNo());

// Verify second order log
List<LogRecord> logRecordList2 = logRecordService.queryLog(order2.getOrderNo(), LogRecordType.ORDER);
Assert.assertEquals(1, logRecordList2.size());
LogRecord logRecord2 = logRecordList2.get(0);
Assert.assertEquals("更新了订单xxxx,更新内容为...", logRecord2.getAction());
Assert.assertEquals(order2.getOrderNo(), logRecord2.getBizNo());

logRecordService.clean();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,6 @@ public void test_diffLog_false() {
address.setCityName("武汉市");
user.setAddress(address);
userService.diffUser(user, user);

List<LogRecord> logRecordList = logRecordService.queryLog(String.valueOf(user.getId()), LogRecordType.USER);
Assert.assertEquals(0, logRecordList.size());
logRecordService.clean();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.mzt.logserver;

import com.mzt.logapi.starter.annotation.LogRecord;
import com.mzt.logserver.infrastructure.constants.LogRecordType;
import lombok.Data;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Arrays;
import java.util.List;

@SpringBootTest
@RunWith(SpringRunner.class)
public class LogRecordBatchTest {

public static class TreeNode{
int value;
TreeNode left;
TreeNode right;
}

public boolean isChild(TreeNode parent, TreeNode child) {
if (parent == null && child == null) {
return true;
}
if (parent == null || child == null) {
return false;
}
if (parent.value == child.value) {
return isChild(parent.left, child.left) && isChild(parent.right, child.right);
}
return isChild(parent.left, child) || isChild(parent.right, child);
}
}