log trace (3)
阿新 • • 發佈:2019-08-23
在bean初始化階段,通過代理方式將其適配為自定義的 IJobHandler 實現物件XxlJobTraceWrapper。
真正 invoke時,從beanFactory中獲取Tracing物件開啟新的Span。
也可以類似於仿@EnableAsync 實現自定義切面日誌 文章中的方案,對IJobHandler.execute()構建StaticMethodMatcherPointcut 的Advisor方式來實現
XxlJobBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware:
import java.lang.reflect.Modifier; import org.aopalliance.aop.Advice; import org.springframework.aop.framework.AopConfigException; import org.springframework.aop.framework.ProxyFactoryBean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; import com.xxl.job.core.handler.IJobHandler; import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** * xxjob入口增加trace日誌 * * @author admin * */ @Component @Slf4j public class XxlJobBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { @Setter private BeanFactory beanFactory; @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { /** * 不能直接用XxjobHnadlerTraceWrapper進行包裝,返出去的型別不匹配。只能通過代理方式 */ return bean instanceof IJobHandler ? wrap(bean) : bean; } private Object wrap(Object bean) { boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers()); boolean cglibProxy = !classFinal; IJobHandler job = (IJobHandler) bean; try { return createProxy(bean, cglibProxy, new XxlJobMethodInterceptor<IJobHandler>(job, this.beanFactory)); } catch (AopConfigException ex) { if (cglibProxy) { if (log.isDebugEnabled()) { log.debug("Exception occurred while trying to create a proxy, falling back to JDK proxy", ex); } return createProxy(bean, false, new XxlJobMethodInterceptor<IJobHandler>(job, this.beanFactory)); } throw ex; } } Object createProxy(Object bean, boolean cglibProxy, Advice advice) { ProxyFactoryBean factory = new ProxyFactoryBean(); factory.setProxyTargetClass(cglibProxy); factory.addAdvice(advice); factory.setTarget(bean); return factory.getObject(); } }
XxlJobMethodInterceptor:
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.beans.factory.BeanFactory; import org.springframework.util.ReflectionUtils; import com.xxl.job.core.handler.IJobHandler; import lombok.AllArgsConstructor; @AllArgsConstructor public class XxlJobMethodInterceptor<T extends IJobHandler> implements MethodInterceptor { private final T delegate; private final BeanFactory beanFactory; @Override public Object invoke(MethodInvocation invocation) throws Throwable { IJobHandler jobHandler = new XxlJobTraceWrapper(this.beanFactory, this.delegate); Method methodOnTracedBean = getMethod(invocation, jobHandler); if (methodOnTracedBean != null) { try { return methodOnTracedBean.invoke(jobHandler, invocation.getArguments()); } catch (InvocationTargetException ex) { Throwable cause = ex.getCause(); throw (cause != null) ? cause : ex; } } return invocation.proceed(); } private Method getMethod(MethodInvocation invocation, Object object) { Method method = invocation.getMethod(); return ReflectionUtils.findMethod(object.getClass(), method.getName(), method.getParameterTypes()); } }
XxlJobTraceWrapper :
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cloud.sleuth.DefaultSpanNamer;
import org.springframework.cloud.sleuth.SpanNamer;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import brave.ScopedSpan;
import brave.Tracing;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
public class XxlJobTraceWrapper extends IJobHandler {
private final BeanFactory beanFactory;
private final IJobHandler delegate;
private Tracing tracing;
private SpanNamer spanNamer;
@Override
public ReturnT<String> execute(String param) throws Exception {
if (this.tracing == null) {
try {
this.tracing = this.beanFactory.getBean(Tracing.class);
} catch (NoSuchBeanDefinitionException e) {
return this.delegate.execute(param);
}
}
return doExecute(param);
}
private ReturnT<String> doExecute(String param) throws Exception {
ScopedSpan span = this.tracing.tracer().startScopedSpanWithParent(spanNamer().name(delegate, "XXJOB"),
this.tracing.currentTraceContext().get());
try {
return this.delegate.execute(param);
} catch (Exception | Error e) {
span.error(e);
throw e;
} finally {
span.finish();
}
}
// due to some race conditions trace keys might not be ready yet
private SpanNamer spanNamer() {
if (this.spanNamer == null) {
try {
this.spanNamer = this.beanFactory.getBean(SpanNamer.class);
} catch (NoSuchBeanDefinitionException e) {
log.warn("SpanNamer bean not found - will provide a manually created instance");
return new DefaultSpanNamer();
}
}
return this.spanNamer;
}
}