作者:兔子狗530_772 | 来源:互联网 | 2023-07-04 13:30
传送唯一requestid在项目或者Microservices之间背景介绍Log4jformat中加requestidRestAPI调用JMSmessage传输之间线程池中的线程H
传送唯一request id在项目或者Micro services之间
- 背景介绍
- Log4j format 中加requestid
- Rest API 调用
- JMS message 传输之间
- 线程池中的线程
- Hessian call 之间
背景介绍
随着项目中要把原来的几个项目变成许多 MircoServices. 所谓的 Micro services化把一个大的项目。
带来一些其它的问题。比如怎么样更方便的查看log,因为项目都变成了一个一个小的项目了。
每个项目的log 文件都在它们怎么自己的项目里。尽管我们用了ELK把所有的log 到发到Elastic search 通过 logstash. 但是没有办法把项目之间的log message 串联在一起显示。只能通过时间的比对或者是其它的信息。
由于这个原因。我引入一个request id 在项目直接调用。这样我们就可以直接用request id 搜索相关的log message 了。再也不用因为很难找到被调用项目的log而发愁了。
‘
Log4j format 中加requestid
log4j.appender.file.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss,SSS}][%X{REQUEST_ID}] %-5p[%t](%C:%L) - %m%n
Rest API 调用
由于我用MDC 还保存我的requestId 的。所有 调用之前要取一下requestId 再MDC里
发送端要加下面的 interceptor
public class RestfulServiceLogInterceptor implements ClientHttpRequestInterceptor{@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {String requestId= (String) MDC.get("REQUEST_ID");if(StringUtils.isEmpty(requestId)) {requestId=UUID.randomUUID().toString();}request.getHeaders().add("REQUEST_ID", requestId);return execution.execute(request, body);}
}
public class RestfulServiceAsyncLogInterceptor implements AsyncClientHttpRequestInterceptor{&#64;Overridepublic ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution) throws IOException{String requestId&#61; (String) MDC.get("REQUEST_ID");if(StringUtils.isEmpty(requestId)) {requestId&#61;UUID.randomUUID().toString();}request.getHeaders().add("REQUEST_ID", requestId);return execution.execute(request, body);}}
被调用端要加下面的Filter。
&#64;Component
public class LogFilter implements Filter {&#64;Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {try {if (servletRequest !&#61; null && servletRequest instanceof HttpServletRequest) {HttpServletRequest req &#61; (HttpServletRequest) servletRequest;String requestId &#61; req.getHeader("REQUEST_ID");MDC.put("REQUEST_ID", requestId );}filterChain.doFilter(servletRequest, servletResponse);} finally {LogUtils.clearHeaderPropertiesInMDC();}}}
JMS message 传输之间
发送JMS 端&#xff0c; 发送的时候要用下面的类包装message.
public abstract class BaseMessageCreator implements MessageCreator{&#64;Overridepublic Message createMessage(Session session) throws JMSException {Message message &#61; this.createInternalMessage(session);this.postProcess(message);return message;}public void postProcess(Message message){String requestId&#61; (String) MDC.get("REQUEST_ID");if(StringUtils.isEmpty(requestId)) {requestId&#61;UUID.randomUUID().toString();}message.setStringProperty("REQUEST_ID", requestId);}protected abstract Message createInternalMessage(Session session) throws JMSException;}
JMS 接受端 &#xff0c; 要用下面类取message header 的requestid.
我用的是AOP. 当然如果你觉得麻烦。也可以直接再你的JMS listner上&#xff0c; 直接取。
&#64;Aspect
public class JmsTransactionAspectLogger {&#64;Pointcut("execution(public * javax.jms.MessageListener.onMessage(..))")private void isMessageListener() {}&#64;Pointcut("execution(public * org.springframework.jms.listener.SessionAwareMessageListener.onMessage(..))")private void isSessionAwareMessageListener() {}&#64;Pointcut("isMessageListener() || isSessionAwareMessageListener()")private void jmsTransactionMethod() {}public void beforeJmsListener(JoinPoint joinPoint) {try {Object[] args &#61; joinPoint.getArgs();for (int i &#61; 0; i < args.length; i&#43;&#43;) {Object arg &#61; args[i];if (arg instanceof Message) {Message message &#61; (Message) arg;String requestId &#61; message.getStringProperty("REQUEST_ID");MDC.put("REQUEST_ID", requestId );}}} catch (Exception ex) {throw e;}}&#64;Around(value &#61; "jmsTransactionMethod()")public Object handleJmsTransaction(final ProceedingJoinPoint joinPoint) throws Throwable {this.beforeJmsListener(joinPoint);try {retValue &#61; joinPoint.proceed(joinPoint.getArgs());} catch (Throwable ex) {throw ex;} finally {this.afterJmsListener(joinPoint, startTime);}return retValue;}public void afterJmsListener(JoinPoint joinPoint, long startTime) {LogUtils.clearHeaderPropertiesInMDC();}
线程池中的线程
线程要用继承下面的抽象类。
如果不是用的线程中的线程&#xff0c;那么没问题
因为 MDC中的值可以从主线程中传过去的。
public class MdcContextHolder {private String requestId;public MdcContextHolder(){this.requestId &#61; (String)MDC.get("REQUEST_ID");}public void setHeaderPropertiesInMDC(){MDC.put("REQUEST_ID", requestId);}
}public abstract class LogCallable<V> implements Callable<V> {MdcContextHolder mdcContextHolder &#61; new MdcContextHolder();;&#64;Overridepublic V call() throws Exception {this.preCall();V v &#61; innerCall();this.afterCall();return v;}private void preCall(){if(mdcContextHolder!&#61;null){mdcContextHolder.setHeaderPropertiesInMDC();}}private void afterCall(){LogUtils.clearHeaderPropertiesInMDC();}public abstract V innerCall() throws Exception;
}public abstract class LogRunnable implements Runnable{MdcContextHolder mdcContextHolder &#61; new MdcContextHolder();;&#64;Overridepublic void run() {this.preRun();this.innerRun();this.afterRun();}private void preRun(){if(mdcContextHolder!&#61;null){mdcContextHolder.setHeaderPropertiesInMDC();}}private void afterRun(){LogUtils.clearHeaderPropertiesInMDC();}public abstract void innerRun();public class MdcTaskDecorator implements TaskDecorator {&#64;Overridepublic Runnable decorate(final Runnable runnable) {LogRunnable mdcRunnable &#61; new LogRunnable() {&#64;Overridepublic void innerRun() {runnable.run();}};return mdcRunnable;}
}
Hessian call 之间
发送端
public class CustomHessianProxyFactory extends HessianProxyFactory {&#64;Overridepublic Object create(Class<?> api, URL url, ClassLoader loader) {if (api &#61;&#61; null)throw new NullPointerException("api must not be null for HessianProxyFactory.create()");InvocationHandler handler &#61; new CustomHessianProxy(url, this, api);return Proxy.newProxyInstance(loader, new Class[] { api, HessianRemoteObject.class }, handler);}private static class CustomHessianProxy extends HessianProxy {protected CustomHessianProxy(URL url, HessianProxyFactory factory) {super(url, factory);}public CustomHessianProxy(URL url, HessianProxyFactory factory, Class<?> type) {super(url, factory, type);}&#64;Overrideprotected void addRequestHeaders(HessianConnection conn) {super.addRequestHeaders(conn);this.addParameterInHeader(conn);}private void addParameterInHeader(HessianConnection conn){String requestId&#61; (String) MDC.get("REQUEST_ID");conn.addHeader("REQUEST_ID", requestId);}}}
被调用端 用上面提到的logfilter 就可以了