这篇文章主要介绍“@EnableAsync如何实现配置化日志输出”,在日常操作中,相信很多人在@EnableAsync如何实现配置化日志输出问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”@EnableAsync如何实现配置化日志输出”的疑惑有所帮助!接下来,请跟着小编一起来学习吧! 声明启动类注解、需要import的配置类。 常规情况会额外指定一下Ordered、proxyTargetClass,本例从简 import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(LogConfigurationImport.class)
@Documented
public @interface EnableLog {
/**
* 指定包路径
*/
String[] basePackages() default {};
}
配置类中需要 advise-> LogPointcutAdvisor : 绑定pointcut与adivce 关系。 adivce -> LogInterceptor: 切面执行处理 import javax.annotation.Resource;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import lombok.Setter;
@Configuration
public class LogConfigurationImport implements ImportAware, EnvironmentAware {
@Nullable
protected AnnotationAttributes enableLogAttributes;
@Setter
private Environment environment;
@Resource
TaskExecutor taskExecutor;
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableLogAttributes = AnnotationAttributes
.fromMap(importMetadata.getAnnotationAttributes(EnableLog.class.getName(), false));
if (this.enableLogAttributes == null) {
throw new IllegalArgumentException(
"@EnableLog is not present on importing class " + importMetadata.getClassName());
}
}
@Bean
public LogInterceptor logInterceptor(TaskExecutor taskExecutor) {
return new LogInterceptor(handler(environment), taskExecutor);
}
@Bean
public ILogHandler handler(Environment environment) {
return new LocalLogHandler(environment);
}
@Bean
public PointcutAdvisor pointcutAdvisor(LogInterceptor logInterceptor) {
LogPointcutAdvisor advisor = new LogPointcutAdvisor(this.enableLogAttributes.getStringArray("basePackages"));
advisor.setAdvice(logInterceptor);
if (enableLogAttributes != null) {
advisor.setOrder(Ordered.LOWEST_PRECEDENCE - 1);
}
return advisor;
}
}
----
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LogPointcutAdvisor extends AbstractBeanFactoryPointcutAdvisor {
private static final long serialVersionUID = 1L;
private ComposablePointcut pointcut; //组合方式的pointcut
public LogPointcutAdvisor(String[] basePackages) {
// 确定切面范围
pointcut = new ComposablePointcut(new AnnotationMatchingPointcut(Controller.class, RequestMapping.class, true));
if (basePackages != null && basePackages.length > 0) {
pointcut.intersection(new LogPackageFilter(Arrays.asList(basePackages)));
}
}
@AllArgsConstructor
static class LogPackageFilter implements ClassFilter {
private List<String> basePackages;
private final ConcurrentHashMap<String, Boolean> classMatchMap = new ConcurrentHashMap<>(50);
@Override
public boolean matches(Class<?> clazz) {
String name = clazz.getName();
boolean match = classMatchMap.computeIfAbsent(name, key -> !CollectionUtils.isEmpty(basePackages)
&& basePackages.stream().anyMatch(t -> key.startsWith(t)));
log.debug("name: {} LogPackageFilter -> {}", name, match);
return match;
}
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
}
----
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodClassKey;
import org.springframework.core.task.TaskExecutor;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestMapping;
import com.google.common.collect.Maps;
public class LogInterceptor implements MethodInterceptor {
private final Map<Object, String> uriCache = new ConcurrentHashMap<>(1024);
private ILogHandler logHandler;
private TaskExecutor taskExecutor;
public LogInterceptor(ILogHandler logHandler, TaskExecutor taskExecutor) {
this.logHandler = logHandler;
this.taskExecutor = taskExecutor;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
String exceptionMsg = null;
String exceptionType = null;
try {
Object result = invocation.proceed();
return result;
} catch (Throwable e) {
exceptionMsg = e.getMessage();
exceptionType = e.getClass().getName();
throw e;
} finally {
final String errorMsg = exceptionMsg;
final String errorType = exceptionType;
long end = System.currentTimeMillis();
taskExecutor.execute(() -> {
handLog(invocation, start, end, errorMsg, errorType);
});
}
}
private void handLog(MethodInvocation invocation, long start, long end, final String errorMsg,
final String errorType) {
Map<String, Object> args = null;
Method method = BridgeMethodResolver.findBridgedMethod(invocation.getMethod());
Class<?> targetClass = getTargetClass(invocation.getThis());
String reqUrl = getRequestUrl(method, targetClass);
Parameter[] parameters = method.getParameters();
Object[] arguments = invocation.getArguments();
if (parameters != null && parameters.length > 1) {
args = Maps.newHashMapWithExpectedSize(15);
for (int i = 0; i < parameters.length; i++) {
args.put(parameters[i].getName(), i < arguments.length ? arguments[i] : null);
}
}
logHandler.handle(new LogInfo(reqUrl, method.getName(), targetClass.getName(), start, end - start, errorMsg,
errorType, args));
}
//获取对象真实的class类型
private Class<?> getTargetClass(Object target) {
return AopProxyUtils.ultimateTargetClass(target);
}
public String getRequestUrl(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
Object cacheKey = getCacheKey(method, targetClass);
String requestUrl = this.uriCache.get(cacheKey);
if (requestUrl == null) {
requestUrl = retrieveUriFromHandlerMethod(method, targetClass);
this.uriCache.put(cacheKey, requestUrl);
}
return requestUrl;
}
//通过方法获取URL
private String retrieveUriFromHandlerMethod(Method method, Class<?> targetClass) {
RequestMapping classRequestMapping = targetClass.getAnnotation(RequestMapping.class);
StringBuilder uriSb = new StringBuilder(256);
if (classRequestMapping != null) {
String[] value = classRequestMapping.value();
if (ArrayUtils.isNotEmpty(value) && StringUtils.isNotBlank(value[0])) {
String classUri = trimFirstSlash(value[0]);
classUri = trimLastSlash(classUri);
uriSb.append(classUri);
}
}
RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
if (methodRequestMapping != null) {
String[] value = methodRequestMapping.value();
if (ArrayUtils.isNotEmpty(value) && StringUtils.isNotBlank(value[0])) {
boolean hasClassUri = uriSb.length() != 0;
String methodUri = trimFirstSlash(value[0]);
if (hasClassUri) {
uriSb.append("/");
}
uriSb.append(methodUri);
}
}
return uriSb.toString().replaceAll("[{}]", "");
}
private String trimFirstSlash(String uri) {
return uri.startsWith("/") ? uri.substring(1) : uri;
}
private String trimLastSlash(String uri) {
return uri.lastIndexOf("/") == uri.length() - 1 ? uri.substring(0, uri.length() - 1) : uri;
}
private Object getCacheKey(Method method, Class<?> targetClass) {
return new MethodClassKey(method, targetClass);
}
}
实际的日志操作处理类 /**
*
* 日志操作
*/
public interface ILogHandler {
String getAppName();
void handle(LogInfo logInfo);
}
import org.springframework.core.env.Environment;
import com.yy.cs.base.json.Json;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
*
* 本地日志输出
*/
@Slf4j
@AllArgsConstructor
public class LocalLogHandler implements ILogHandler {
Environment environment;
@Override
public String getAppName() {
return environment.getProperty("spring.application.name");
}
@Override
public void handle(LogInfo logInfo) {
log.info("request log: {}", Json.ObjToStr(logInfo));
}
}
----
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LogInfo {
private String requestUrl;
private String method;
private String clas;
private long start;
private long cost;
private String errorMsg;
private String exceptionType;
private Map<String, Object> args;
}
到此,关于“@EnableAsync如何实现配置化日志输出”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注天达云网站,小编会继续努力为大家带来更多实用的文章!
|