使用JAX-RS在一个位置记录请求和响应

Edg*_*rka 27 java rest jax-rs resteasy

我有一个RESTEasy Web服务器,有很多方法.我希望实现logback来跟踪所有请求和响应,但我不想添加log.info()到每个方法.

也许有办法在一个地方捕获请求和响应并记录它.也许类似于RESTEasy上的HTTP请求流程链上的过滤器.

@Path("/rest")
@Produces("application/json")
public class CounterRestService {

    //Don't want use log in controler every method to track requests and responces
    static final Logger log = LoggerFactory.getLogger(CounterRestService.class); 

    @POST
    @Path("/create")
    public CounterResponce create(@QueryParam("name") String name) {
        log.info("create "+name)
        try {
            CounterService.getInstance().put(name);
            log.info("responce data"); // <- :((
            return new CounterResponce();
        } catch (Exception e){
            log.info("responce error data"); // <- :((
            return new CounterResponce("error", e.getMessage());
        }    
    }

    @POST
    @Path("/insert")
    public CounterResponce create(Counter counter) {
        try {
            CounterService.getInstance().put(counter);
            return new CounterResponce();
        } catch (Exception e){
            return new CounterResponce("error", e.getMessage());
        }
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

cas*_*lin 72

您可以创建过滤器并轻松将它们绑定到您需要记录的端点,从而使您的端点保持精简并专注于业务逻辑.

定义名称绑定注释

要将过滤器绑定到REST端点,JAX-RS提供了元注释@NameBinding,可以按如下方式使用:

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Logged { }
Run Code Online (Sandbox Code Playgroud)

记录HTTP请求

@Logged注释将被用来装饰一个过滤器类,它实现ContainerRequestFilter,让您在处理请求:

@Logged
@Provider
public class RequestLoggingFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        // Use the ContainerRequestContext to extract information from the HTTP request
        // Information such as the URI, headers and HTTP entity are available
    }
}
Run Code Online (Sandbox Code Playgroud)

@Provider注释标记的扩展接口期间提供扫描阶段,应该是由JAX-RS运行时发现的实现.

ContainerRequestContext可以帮助您从HTTP请求中提取信息.

以下是ContainerRequestContextAPI中用于从HTTP请求获取可用于日志的信息的方法:

记录HTTP响应

要记录响应,请考虑实现ContainerResponseFilter:

@Logged
@Provider
public class ResponseLoggingFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext, 
                       ContainerResponseContext responseContext) throws IOException {
        // Use the ContainerRequestContext to extract information from the HTTP request
        // Use the ContainerResponseContext to extract information from the HTTP response
    }
}
Run Code Online (Sandbox Code Playgroud)

ContainerResponseContext可以帮助您从HTTP响应中提取信息.

以下是ContainerResponseContextAPI中的一些方法,用于从HTTP响应中获取对日志有用的信息:

将过滤器绑定到端点

要将过滤器绑定到端点方法或类,请使用@Logged上面定义的注释对其进行 注释.对于注释的方法和/或类,将执行过滤器:

@Path("/")
public class MyEndpoint {

    @GET
    @Path("{id}")
    @Produces("application/json")
    public Response myMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Logged
        // The logging filters won't be executed when invoking this method
        ...
    }

    @DELETE
    @Logged
    @Path("{id}")
    @Produces("application/json")
    public Response myLoggedMethod(@PathParam("id") Long id) {
        // This method is annotated with @Logged
        // The request logging filter will be executed before invoking this method
        // The response logging filter will be executed before invoking this method
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,日志记录过滤器将仅执行,myLoggedMethod(Long)因为它带有注释@Logged.

附加信息

除了ContainerRequestContextContainerResponseFilter接口中可用的方法,您可以ResourceInfo使用@Context以下方法注入过滤器:

@Context
ResourceInfo resourceInfo;
Run Code Online (Sandbox Code Playgroud)

它可以用来获取MethodClass匹配请求的URL其中:

Class<?> resourceClass = resourceInfo.getResourceClass();
Method resourceMethod = resourceInfo.getResourceMethod();
Run Code Online (Sandbox Code Playgroud)

HttpServletRequest并且HttpServletResponse也可用于注射:

@Context
HttpServletRequest httpServletRequest;

@Context
HttpServletResponse httpServletResponse;
Run Code Online (Sandbox Code Playgroud)

有关可注入的类型,请参阅此答案@Context.

  • 你可以用这种方式打印它:`BufferedInputStream stream = new BufferedInputStream(requestContext.getEntityStream()); String payload = IOUtils.toString(stream,"UTF-8"); logger.debug("有效载荷:"+有效载荷); requestContext.setEntityStream(IOUtils.toInputStream(payload,"UTF-8"));` (2认同)
  • 这适用于 requestContext.getEntityStream (InputStream)。如何为 responseContext.getEntityStream (OutputStream) 做到这一点? (2认同)
  • 有时你必须热爱Java。使用其他语言执行此操作通常只需一行更改即可添加中间件模块。 (2认同)