使用 JAX-RS 在一处记录请求和响应
Logging request and response in one place with JAX-RS
我有一个带有很多方法的 RESTEasy 网络服务器。我想实现 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());
}
}
...
}
您可以创建过滤器并将它们轻松绑定到您需要记录的端点,从而使您的端点精简并专注于业务逻辑。
定义名称绑定注解
要将过滤器绑定到您的 REST 端点,JAX-RS 提供 meta-annotation @NameBinding
并且可以按如下方式使用:
@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Logged { }
记录 HTTP 请求
@Logged
注解将用于装饰过滤器class,它实现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
}
}
@Provider
注释标记了一个扩展接口的实现,在提供程序扫描阶段,JAX-RS 运行时应该可以发现它。
ContainerRequestContext
帮助您从 HTTP 请求中提取信息。
以下是 ContainerRequestContext
API 中的方法,用于从对您的日志有用的 HTTP 请求中获取信息:
ContainerRequestContext#getMethod()
: 从请求中获取HTTP方法
ContainerRequestContext#getUriInfo()
: 从HTTP请求中获取URI信息。
ContainerRequestContext#getHeaders()
:从HTTP请求中获取headers。
ContainerRequestContext#getMediaType()
:获取实体的媒体类型
ContainerRequestContext#getEntityStream()
:获取实体输入流
记录 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
}
}
ContainerResponseContext
帮助您从 HTTP 响应中提取信息。
以下是 ContainerResponseContext
API 中的一些方法,用于从 HTTP 响应中获取对您的日志有用的信息:
ContainerResponseContext#getStatus()
: 从 HTTP 响应中获取状态码。
ContainerResponseContext#getHeaders()
:从 HTTP 响应中获取 headers。
ContainerResponseContext#getEntityStream()
:获取实体输出流。
将过滤器绑定到您的端点
要将过滤器绑定到您的端点方法或 classes,请使用上面定义的 @Logged
注释对它们进行注释。对于and/or class注解的方法,会执行过滤器:
@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
...
}
}
在上面的示例中,日志过滤器将仅针对 myLoggedMethod(Long)
执行,因为它被注释为 @Logged
。
附加信息
除了 ContainerRequestContext
和 ContainerResponseFilter
接口中可用的方法外,您还可以使用 在过滤器中注入 ResourceInfo
@Context
:
@Context
ResourceInfo resourceInfo;
可用于获取与请求的URL匹配的Method
and the Class
:
Class<?> resourceClass = resourceInfo.getResourceClass();
Method resourceMethod = resourceInfo.getResourceMethod();
HttpServletRequest
and HttpServletResponse
也可以注射:
@Context
HttpServletRequest httpServletRequest;
@Context
HttpServletResponse httpServletResponse;
参考这个answer for the types that can be injected with @Context
.
尝试拦截器(不仅仅是香草 EJB 拦截器,您可以将 CDI 与它一起使用)。
他们在那里用于实施横切关注点(方面)。
对于其他使用 Jersey 并试图解决相同问题的人,org.glassfish.jersey.logging.LoggingFeature 可以在客户端或服务器上使用。它记录对 java.util.logging.Logger.
的请求和响应
如果需要,可以使用 org.slf4j.bridge.SLF4JBridgeHandler 将输出桥接到 slf4j。
我有一个带有很多方法的 RESTEasy 网络服务器。我想实现 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());
}
}
...
}
您可以创建过滤器并将它们轻松绑定到您需要记录的端点,从而使您的端点精简并专注于业务逻辑。
定义名称绑定注解
要将过滤器绑定到您的 REST 端点,JAX-RS 提供 meta-annotation @NameBinding
并且可以按如下方式使用:
@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Logged { }
记录 HTTP 请求
@Logged
注解将用于装饰过滤器class,它实现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
}
}
@Provider
注释标记了一个扩展接口的实现,在提供程序扫描阶段,JAX-RS 运行时应该可以发现它。
ContainerRequestContext
帮助您从 HTTP 请求中提取信息。
以下是 ContainerRequestContext
API 中的方法,用于从对您的日志有用的 HTTP 请求中获取信息:
ContainerRequestContext#getMethod()
: 从请求中获取HTTP方法ContainerRequestContext#getUriInfo()
: 从HTTP请求中获取URI信息。ContainerRequestContext#getHeaders()
:从HTTP请求中获取headers。ContainerRequestContext#getMediaType()
:获取实体的媒体类型ContainerRequestContext#getEntityStream()
:获取实体输入流
记录 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
}
}
ContainerResponseContext
帮助您从 HTTP 响应中提取信息。
以下是 ContainerResponseContext
API 中的一些方法,用于从 HTTP 响应中获取对您的日志有用的信息:
ContainerResponseContext#getStatus()
: 从 HTTP 响应中获取状态码。ContainerResponseContext#getHeaders()
:从 HTTP 响应中获取 headers。ContainerResponseContext#getEntityStream()
:获取实体输出流。
将过滤器绑定到您的端点
要将过滤器绑定到您的端点方法或 classes,请使用上面定义的 @Logged
注释对它们进行注释。对于and/or class注解的方法,会执行过滤器:
@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
...
}
}
在上面的示例中,日志过滤器将仅针对 myLoggedMethod(Long)
执行,因为它被注释为 @Logged
。
附加信息
除了 ContainerRequestContext
和 ContainerResponseFilter
接口中可用的方法外,您还可以使用 在过滤器中注入 ResourceInfo
@Context
:
@Context
ResourceInfo resourceInfo;
可用于获取与请求的URL匹配的Method
and the Class
:
Class<?> resourceClass = resourceInfo.getResourceClass();
Method resourceMethod = resourceInfo.getResourceMethod();
HttpServletRequest
and HttpServletResponse
也可以注射:
@Context
HttpServletRequest httpServletRequest;
@Context
HttpServletResponse httpServletResponse;
参考这个answer for the types that can be injected with @Context
.
尝试拦截器(不仅仅是香草 EJB 拦截器,您可以将 CDI 与它一起使用)。
他们在那里用于实施横切关注点(方面)。
对于其他使用 Jersey 并试图解决相同问题的人,org.glassfish.jersey.logging.LoggingFeature 可以在客户端或服务器上使用。它记录对 java.util.logging.Logger.
的请求和响应如果需要,可以使用 org.slf4j.bridge.SLF4JBridgeHandler 将输出桥接到 slf4j。