我创建了一个基于Spring的Java Web应用程序,
对于每个请求,将创建一个'controller class'实例来处理请求。
在业务逻辑中,我希望使用自动分配给每个请求的UNIQUE ID进行一些日志记录,以便我可以跟踪程序的确切操作。
日志可能是这样的(同时有2个请求):
[INFO] request #XXX: begin.
[INFO] request #XXX: did step 1
[INFO] request #YYY: begin.
[INFO] request #XXX: did step 2
[INFO] request #YYY: did step 1
[INFO] request #XXX: end.
[INFO] request #YYY: end.
从日志中,我可以意识到: req #XXX:begin-step1-step2-end req #YYY:begin-step1-end
我希望可以在代码中的任何地方轻松调用日志记录, 所以我不想在每个java函数中添加“requestId”参数,
如果可以以静态方式调用日志工具,那就完美了:
LOG.doLog("did step 1");
我怎么能这样做呢?谢谢你:))
答案 0 :(得分:27)
您也可以尝试使用MDC类Log4j。 MDC基于每个线程进行管理。 如果您使用的是ServletRequestListner,则可以在requestInitialized中设置唯一ID。
import org.apache.log4j.MDC;
import java.util.UUID;
public class TestRequestListener implements ServletRequestListener {
protected static final Logger LOGGER = LoggerFactory.getLogger(TestRequestListener.class);
public void requestInitialized(ServletRequestEvent arg0) {
LOGGER.debug("++++++++++++ REQUEST INITIALIZED +++++++++++++++++");
MDC.put("RequestId", UUID.randomUUID());
}
public void requestDestroyed(ServletRequestEvent arg0) {
LOGGER.debug("-------------REQUEST DESTROYED ------------");
MDC.clear();
}
}
如果您执行日志调试,警告或错误,现在代码中的任何位置。无论你输入MDC的是什么,都会打印出来。您需要配置log4j.properties。注意%X {RequestId}。这引用了上面的requestInitialized()中插入的键名。
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSSS} %p %C %X{RequestId} - %m%n
我还发现此链接很有用 - > What is the difference between Log4j's NDC and MDC facilities?
答案 1 :(得分:17)
您有三个不同的问题需要解决:
我会建议这种方法
使用Servlet过滤器或ServletRequestListener(由M. Deinum建议)或Spring Handler Interceptor以一般方式拦截请求,您可以创建一个唯一的ID,也许使用UUID
您可以将id保存为请求的属性,在这种情况下,id将仅在控制器层中传播,而不是在服务中传播。因此,您可以使用ThreadLocal变量解决问题,或者让Spring使用RequestContextHolder执行魔术:RequestContextHolder将允许您访问该特定线程的请求,以及请求属性,在服务层。 RequestContextHolder使用ThreadLocal变量来存储请求。您可以通过以下方式访问请求:
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
// Extract the request
HttpServletRequest request = attr.getRequest();
如果您使用log4j,则有一个有趣的article(2018 alternative),可以自定义记录器的模式布局。但是,您只需创建日志系统接口的代理,并将id手动附加到每个记录的字符串。
答案 2 :(得分:2)
您还可以使用" Fish Tagging"在Log4j中2.与https://logging.apache.org/log4j/2.x/manual/thread-context.html
中描述的MDC和NDC(线程基础)相同的想法在这里,您可以使用线程上下文堆栈或线程上下文映射。 Map的示例如下所示:
//put a unique id to the map
ThreadContext.put("id", UUID.randomUUID().toString()
//clear map
ThreadContext.clearMap();
对于log4j2.xml中的模式,您还可以使用%X {KEY}标记。
要将新ID添加到地图中(例如,对于每个传入的请求),您可以在ServletRequestListener实现中执行该操作,Sharadr如何对其进行描述。
答案 3 :(得分:2)
如果您不介意使用spring 4.1.3或更高版本,可以将请求包装到ContentCachingRequestWrapper类的自定义子类中。
public class MyHTTPServletRequestWrapper extends ContentCachingRequestWrapper {
private UUID uuid;
public MyHTTPServletRequestWrapper (HttpServletRequest request) {
super(request);
uuid = UUID.randomUUID();
}
public UUID getUUID() {
return uuid;
}
}
在spring控制器中,将请求添加到方法的参数中,并将其强制转换为自定义包装器:
@RequestMapping(value="/get", method = RequestMethod.GET, produces="application/json")
public @ResponseBody String find(@RequestParam(value = "id") String id, HttpServletRequest request) {
MyHTTPServletRequestWrapper wrappedRequest = (WGHTTPServletRequestWrapper)request;
System.out.println(wrappedRequest.getUUID());
...
}
您需要使用过滤器来连接点:
public class RequestLoggingFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest requestToCache = new MyHTTPServletRequestWrapper((HttpServletRequest) request);
System.out.println(((WGHTTPServletRequestWrapper)requestToCache).getUUID());
chain.doFilter(requestToCache, response);
}
}
您会发现来自RequestLoggingFilter.doFilter()和控制器getId()的printlin将生成相同的UUID。
您只需要在适当的位置使用UUID,但至少您拥有控制器中的值,即业务逻辑的起点。
答案 4 :(得分:0)
答案 5 :(得分:0)
你必须使用如下的数据库序列(如果你的数据库是ORACLE)
create sequence "sequence_Name";
String uniqueString=sessionFactory.getCurrentSession(). createSQLQuery("SELECT \"sequence_Name\".nextval FROM dual").list().get(0);