在我的spring rest应用中,我记录了方面的每个api端点参数。
@Aspect
@Component
public class EndpointsAspect {
@Around("execution(@org.springframework.web.bind.annotation.RequestMapping * *(..))")
public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
Map<String, Object> log = new HashMap<>();
String[] parameterNames = methodSignature.getParameterNames();
Object[] parameterValues = joinPoint.getArgs();
Map<String, Object> arguments = new HashMap<>();
for (int i = 0; i < parameterNames.length; i++) {
arguments.put(parameterNames[i], parameterValues[i]);
}
log.put("Method arguments", arguments);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(log);
...
Object retVal = joinPoint.proceed();
}
}
它工作正常,直到建议方法的参数之一具有类型为HttpServletRequest的参数为止
@RequestMapping("/info")
public String index(HttpServletRequest request) {
return "Info";
}
在这种情况下,将引发java.lang.StackOverflowError。
我知道这在某种程度上与HttpServlterRequest变量相关(可能是一些不定式循环),但是如何解决此问题呢?
何限制gson深度?
我看过一些解决方案(使用一些注释将字段或类注释为应该转换为json的方法),但它不适合我,这应该是所有类和案例的通用解决方案(例如,我不注释带有一些注释的HttpServletRequest,或将其包含到gson排除策略中,因为谁的类将被转换为json),我需要将日志数据作为json,但由于序列化问题,记录程序不应成为应用程序的错误点。
谢谢。
最佳答案
我找到了解决方案并回答了我的问题。
对于json序列化程序,我使用flexjson库(http://flexjson.sourceforge.net/)。它支持具有双向关系的类的序列化,而无需任何注释和其他额外工作。
对于HttpServletRequest,我将序列化包装在try catch块中并记录日志,例如,参数“ request”无法序列化。
@Around("execution(@org.springframework.web.bind.annotation.RequestMapping * *(..))")
public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Map<String, Object> log = new HashMap<>();
log.put("Method", joinPoint.getSignature().toString());
String[] parameterNames = methodSignature.getParameterNames();
Object[] parameterValues = joinPoint.getArgs();
Map<String, Object> arguments = new HashMap<>();
for (int i = 0; i < parameterNames.length; i++) {
// Check if argument can be serialized and put arguments to argument list if possible. For example, HttpServletRequest cannot be serialized
try {
JSONSerializer serializer = new JSONSerializer();
String json = serializer.prettyPrint(true).deepSerialize(parameterValues[i]);
arguments.put(parameterNames[i], parameterValues[i]);
}
catch (Exception serializerException) {
arguments.put(parameterNames[i], "Couldn't serialize argument. "+serializerException.getMessage());
}
}
log.put("Method arguments", arguments);
...
try {
JSONSerializer serializer = new JSONSerializer();
String json = serializer.prettyPrint(true).deepSerialize(log);
logger.info(json);
}
catch (Exception e) {
logger.error("Could not serialize data. "+e.getMessage());
}
...
}
日志看起来像这样:
{
"Path": "/v1/users/1",
"Http Status": "200 OK",
"Method arguments": {
"request": "Couldn't serialize argument. Error trying to deepSerialize",
"id": 1
},
"Headers": {
"accept-language": "en-US,en;q=0.8",
"cookie": "lbannounce=; ServiceTransactionSuccess=false; ServiceTransactionAmount=false; ServiceTransactionWidgetType=false; ServiceTransactionDomain=false; _ga=GA1.1.1011235846.1448355706",
"host": "localhost:8080",
"connection": "keep-alive",
"accept-encoding": "gzip, deflate, sdch",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"user-agent": "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36"
},
"Return Value": {
"class": "api.domain.User",
"email": "[email protected]",
"id": 1,
"username": "user111"
},
"Ip": "0:0:0:0:0:0:0:1",
"Http Method": "GET",
"Method": "User api.web.UserController.user(int,HttpServletRequest)"
}