在我的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)"
}

10-08 01:48