From 4bd22eeb13987aff934273a60ebc59cb05229bc4 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 6 Jul 2018 18:10:39 -0400 Subject: [PATCH] Fallback logger for logging in http and codec packages Issue: SPR-17012 --- .../core/codec/AbstractDecoder.java | 20 +- .../core/codec/AbstractEncoder.java | 20 +- .../org/springframework/http/HttpLog.java | 195 ++++++++++++++++++ .../http/codec/DecoderHttpMessageReader.java | 13 ++ .../http/codec/EncoderHttpMessageWriter.java | 13 ++ .../http/codec/LoggingCodecSupport.java | 4 +- .../http/codec/ResourceHttpMessageWriter.java | 3 +- .../http/codec/json/Jackson2CodecSupport.java | 3 +- .../AbstractHttpMessageConverter.java | 3 +- .../json/Jackson2ObjectMapperBuilder.java | 3 +- .../reactive/AbstractServerHttpRequest.java | 3 +- .../reactive/AbstractServerHttpResponse.java | 3 +- .../reactive/ReactorHttpHandlerAdapter.java | 3 +- .../reactive/ServletHttpHandlerAdapter.java | 3 +- .../reactive/UndertowHttpHandlerAdapter.java | 3 +- 15 files changed, 280 insertions(+), 12 deletions(-) create mode 100644 spring-web/src/main/java/org/springframework/http/HttpLog.java diff --git a/spring-core/src/main/java/org/springframework/core/codec/AbstractDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/AbstractDecoder.java index b1fb8d8e554..bc5c11efa02 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/AbstractDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/AbstractDecoder.java @@ -40,7 +40,7 @@ import org.springframework.util.MimeType; */ public abstract class AbstractDecoder implements Decoder { - protected final Log logger = LogFactory.getLog(getClass()); + protected Log logger = LogFactory.getLog(getClass()); private final List decodableMimeTypes; @@ -50,6 +50,24 @@ public abstract class AbstractDecoder implements Decoder { } + /** + * Set an alternative logger to use than the one based on the class name. + * @param logger the logger to use + * @since 5.1 + */ + public void setLogger(Log logger) { + this.logger = logger; + } + + /** + * Return the currently configured Logger. + * @since 5.1 + */ + public Log getLogger() { + return logger; + } + + @Override public List getDecodableMimeTypes() { return this.decodableMimeTypes; diff --git a/spring-core/src/main/java/org/springframework/core/codec/AbstractEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/AbstractEncoder.java index 40feff2b173..b1d220616fa 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/AbstractEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/AbstractEncoder.java @@ -36,7 +36,7 @@ import org.springframework.util.MimeType; */ public abstract class AbstractEncoder implements Encoder { - protected final Log logger = LogFactory.getLog(getClass()); + protected Log logger = LogFactory.getLog(getClass()); private final List encodableMimeTypes; @@ -46,6 +46,24 @@ public abstract class AbstractEncoder implements Encoder { } + /** + * Set an alternative logger to use than the one based on the class name. + * @param logger the logger to use + * @since 5.1 + */ + public void setLogger(Log logger) { + this.logger = logger; + } + + /** + * Return the currently configured Logger. + * @since 5.1 + */ + public Log getLogger() { + return logger; + } + + @Override public List getEncodableMimeTypes() { return this.encodableMimeTypes; diff --git a/spring-web/src/main/java/org/springframework/http/HttpLog.java b/spring-web/src/main/java/org/springframework/http/HttpLog.java new file mode 100644 index 00000000000..a24883a7afb --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/HttpLog.java @@ -0,0 +1,195 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.http; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.logging.impl.NoOpLog; + +import org.springframework.util.ObjectUtils; + +/** + * Composite {@link Log} configured with a primary logger and a list of secondary + * ones to fall back on if the main one is not enabled. + * + *

This class also declares {@link #webLogger} for use as fallback when + * logging in the "org.springframework.http" package. + * + * @author Rossen Stoyanchev + * @since 5.1 + */ +public final class HttpLog implements Log { + + /** + * Logger with category "org.springframework.web.HTTP" to use as fallback + * if "org.springframework.web" is on. + */ + public static final Log webLogger = LogFactory.getLog("org.springframework.web.HTTP"); + + private static final Log noOpLog = new NoOpLog(); + + + private final Log fatalLogger; + + private final Log errorLogger; + + private final Log warnLogger; + + private final Log infoLogger; + + private final Log debugLogger; + + private final Log traceLogger; + + + private HttpLog(List loggers) { + this.fatalLogger = initLogger(loggers, Log::isFatalEnabled); + this.errorLogger = initLogger(loggers, Log::isErrorEnabled); + this.warnLogger = initLogger(loggers, Log::isWarnEnabled); + this.infoLogger = initLogger(loggers, Log::isInfoEnabled); + this.debugLogger = initLogger(loggers, Log::isDebugEnabled); + this.traceLogger = initLogger(loggers, Log::isTraceEnabled); + } + + private static Log initLogger(List loggers, Predicate predicate) { + return loggers.stream().filter(predicate).findFirst().orElse(noOpLog); + } + + + @Override + public boolean isFatalEnabled() { + return this.fatalLogger != noOpLog; + } + + @Override + public boolean isErrorEnabled() { + return this.errorLogger != noOpLog; + } + + @Override + public boolean isWarnEnabled() { + return this.warnLogger != noOpLog; + } + + @Override + public boolean isInfoEnabled() { + return this.infoLogger != noOpLog; + } + + @Override + public boolean isDebugEnabled() { + return this.debugLogger != noOpLog; + } + + @Override + public boolean isTraceEnabled() { + return this.traceLogger != noOpLog; + } + + @Override + public void fatal(Object message) { + this.fatalLogger.fatal(message); + } + + @Override + public void fatal(Object message, Throwable ex) { + this.fatalLogger.fatal(message, ex); + } + + @Override + public void error(Object message) { + this.errorLogger.error(message); + } + + @Override + public void error(Object message, Throwable ex) { + this.errorLogger.error(message); + } + + @Override + public void warn(Object message) { + this.warnLogger.warn(message); + } + + @Override + public void warn(Object message, Throwable ex) { + this.warnLogger.warn(message, ex); + } + + @Override + public void info(Object message) { + this.infoLogger.info(message); + } + + @Override + public void info(Object message, Throwable ex) { + this.infoLogger.info(message, ex); + } + + @Override + public void debug(Object message) { + this.debugLogger.debug(message); + } + + @Override + public void debug(Object message, Throwable ex) { + this.debugLogger.debug(message, ex); + } + + @Override + public void trace(Object message) { + this.traceLogger.trace(message); + } + + @Override + public void trace(Object message, Throwable ex) { + this.traceLogger.trace(message, ex); + } + + + /** + * Create a composite logger that uses the given primary logger, if enabled, + * or falls back on {@link #webLogger}. + * @param primaryLogger the primary logger + * @return a composite logger + */ + public static Log create(Log primaryLogger) { + return createWith(primaryLogger, webLogger); + } + + /** + * Create a composite logger that uses the given primary logger, if enabled, + * or falls back on one of the given secondary loggers.. + * @param primaryLogger the primary logger + * @param secondaryLoggers fallback loggers + * @return a composite logger + */ + public static Log createWith(Log primaryLogger, Log... secondaryLoggers) { + if (ObjectUtils.isEmpty(secondaryLoggers)) { + return primaryLogger; + } + List loggers = new ArrayList<>(1 + secondaryLoggers.length); + loggers.add(primaryLogger); + Collections.addAll(loggers, secondaryLoggers); + return new HttpLog(loggers); + } + +} diff --git a/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java index 12bbb746b13..540c7c9fed6 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java @@ -19,12 +19,15 @@ package org.springframework.http.codec; import java.util.List; import java.util.Map; +import org.apache.commons.logging.Log; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; +import org.springframework.core.codec.AbstractDecoder; import org.springframework.core.codec.Decoder; import org.springframework.core.codec.Hints; +import org.springframework.http.HttpLog; import org.springframework.http.HttpMessage; import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpInputMessage; @@ -60,6 +63,16 @@ public class DecoderHttpMessageReader implements HttpMessageReader { Assert.notNull(decoder, "Decoder is required"); this.decoder = decoder; this.mediaTypes = MediaType.asMediaTypes(decoder.getDecodableMimeTypes()); + initLogger(decoder); + } + + private void initLogger(Decoder decoder) { + if (decoder instanceof AbstractDecoder && + decoder.getClass().getPackage().getName().startsWith("org.springframework.core.codec")) { + + Log logger = HttpLog.create(((AbstractDecoder) decoder).getLogger()); + ((AbstractDecoder) decoder).setLogger(logger); + } } diff --git a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java index 7c4860fcffc..16a8d5660e9 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java @@ -19,15 +19,18 @@ package org.springframework.http.codec; import java.util.List; import java.util.Map; +import org.apache.commons.logging.Log; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; +import org.springframework.core.codec.AbstractEncoder; import org.springframework.core.codec.Encoder; import org.springframework.core.codec.Hints; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpLog; import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpOutputMessage; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -66,6 +69,16 @@ public class EncoderHttpMessageWriter implements HttpMessageWriter { this.encoder = encoder; this.mediaTypes = MediaType.asMediaTypes(encoder.getEncodableMimeTypes()); this.defaultMediaType = initDefaultMediaType(this.mediaTypes); + initLogger(encoder); + } + + private void initLogger(Encoder encoder) { + if (encoder instanceof AbstractEncoder && + encoder.getClass().getPackage().getName().startsWith("org.springframework.core.codec")) { + + Log logger = HttpLog.create(((AbstractEncoder) encoder).getLogger()); + ((AbstractEncoder) encoder).setLogger(logger); + } } @Nullable diff --git a/spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java b/spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java index 480e28f9698..77c3dfffeac 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java +++ b/spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java @@ -18,6 +18,8 @@ package org.springframework.http.codec; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.http.HttpLog; + /** * Base class for {@link org.springframework.core.codec.Encoder}, * {@link org.springframework.core.codec.Decoder}, {@link HttpMessageReader}, or @@ -29,7 +31,7 @@ import org.apache.commons.logging.LogFactory; */ public class LoggingCodecSupport { - protected final Log logger = LogFactory.getLog(getClass()); + protected final Log logger = HttpLog.create(LogFactory.getLog(getClass())); /** Do not log potentially sensitive information (params at DEBUG and headers at TRACE). */ private boolean disableLoggingRequestDetails = false; diff --git a/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java index 0b51c153a6d..48bf143a2c4 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java @@ -39,6 +39,7 @@ import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.support.ResourceRegion; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpLog; import org.springframework.http.HttpRange; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -72,7 +73,7 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter { private static final ResolvableType REGION_TYPE = ResolvableType.forClass(ResourceRegion.class); - private static final Log logger = LogFactory.getLog(ResourceHttpMessageWriter.class); + private static final Log logger = HttpLog.create(LogFactory.getLog(ResourceHttpMessageWriter.class)); private final ResourceEncoder encoder; diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java index be3971399b9..3835053add1 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java @@ -35,6 +35,7 @@ import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.codec.Hints; +import org.springframework.http.HttpLog; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.MimeType; @@ -65,7 +66,7 @@ public abstract class Jackson2CodecSupport { new MimeType("application", "*+json", StandardCharsets.UTF_8))); - protected final Log logger = LogFactory.getLog(getClass()); + protected final Log logger = HttpLog.create(LogFactory.getLog(getClass())); private final ObjectMapper objectMapper; diff --git a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java index f9ae2043364..49bc4e1564b 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java @@ -29,6 +29,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpLog; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.StreamingHttpOutputMessage; @@ -51,7 +52,7 @@ import org.springframework.util.Assert; public abstract class AbstractHttpMessageConverter implements HttpMessageConverter { /** Logger available to subclasses. */ - protected final Log logger = LogFactory.getLog(getClass()); + protected final Log logger = HttpLog.create(LogFactory.getLog(getClass())); private List supportedMediaTypes = Collections.emptyList(); diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index f7975bb04b6..0ba3b93519f 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -60,6 +60,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.FatalBeanException; import org.springframework.context.ApplicationContext; import org.springframework.core.KotlinDetector; +import org.springframework.http.HttpLog; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -103,7 +104,7 @@ public class Jackson2ObjectMapperBuilder { private static volatile boolean kotlinWarningLogged = false; - private final Log logger = LogFactory.getLog(getClass()); + private final Log logger = HttpLog.create(LogFactory.getLog(getClass())); private final Map, Class> mixIns = new HashMap<>(); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java index ef9c4431ccb..8fb17053280 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpLog; import org.springframework.http.server.RequestPath; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; @@ -43,7 +44,7 @@ import org.springframework.util.StringUtils; */ public abstract class AbstractServerHttpRequest implements ServerHttpRequest { - protected final Log logger = LogFactory.getLog(getClass()); + protected final Log logger = HttpLog.create(LogFactory.getLog(getClass())); private static final Pattern QUERY_PATTERN = Pattern.compile("([^&=]+)(=?)([^&]+)?"); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpResponse.java b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpResponse.java index b9d0edb1df1..1bc238749cf 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpResponse.java @@ -31,6 +31,7 @@ import reactor.core.publisher.Mono; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpLog; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; import org.springframework.lang.Nullable; @@ -57,7 +58,7 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse { */ private enum State {NEW, COMMITTING, COMMITTED} - protected final Log logger = LogFactory.getLog(getClass()); + protected final Log logger = HttpLog.create(LogFactory.getLog(getClass())); private final DataBufferFactory dataBufferFactory; diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorHttpHandlerAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorHttpHandlerAdapter.java index f9c8eba3c37..2afec328c41 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorHttpHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorHttpHandlerAdapter.java @@ -27,6 +27,7 @@ import reactor.netty.http.server.HttpServerRequest; import reactor.netty.http.server.HttpServerResponse; import org.springframework.core.io.buffer.NettyDataBufferFactory; +import org.springframework.http.HttpLog; import org.springframework.http.HttpMethod; import org.springframework.util.Assert; @@ -39,7 +40,7 @@ import org.springframework.util.Assert; */ public class ReactorHttpHandlerAdapter implements BiFunction> { - private static final Log logger = LogFactory.getLog(ReactorHttpHandlerAdapter.class); + private static final Log logger = HttpLog.create(LogFactory.getLog(ReactorHttpHandlerAdapter.class)); private final HttpHandler httpHandler; diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletHttpHandlerAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletHttpHandlerAdapter.java index e88c819cf39..7a2dc13f9b9 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletHttpHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletHttpHandlerAdapter.java @@ -41,6 +41,7 @@ import org.reactivestreams.Subscription; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DefaultDataBufferFactory; +import org.springframework.http.HttpLog; import org.springframework.http.HttpMethod; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -57,7 +58,7 @@ import org.springframework.util.Assert; @SuppressWarnings("serial") public class ServletHttpHandlerAdapter implements Servlet { - private static final Log logger = LogFactory.getLog(ServletHttpHandlerAdapter.class); + private static final Log logger = HttpLog.create(LogFactory.getLog(ServletHttpHandlerAdapter.class)); private static final int DEFAULT_BUFFER_SIZE = 8192; diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowHttpHandlerAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowHttpHandlerAdapter.java index af04f06169f..12323a280f0 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowHttpHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowHttpHandlerAdapter.java @@ -27,6 +27,7 @@ import org.reactivestreams.Subscription; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DefaultDataBufferFactory; +import org.springframework.http.HttpLog; import org.springframework.http.HttpMethod; import org.springframework.util.Assert; @@ -40,7 +41,7 @@ import org.springframework.util.Assert; */ public class UndertowHttpHandlerAdapter implements io.undertow.server.HttpHandler { - private static final Log logger = LogFactory.getLog(UndertowHttpHandlerAdapter.class); + private static final Log logger = HttpLog.create(LogFactory.getLog(UndertowHttpHandlerAdapter.class)); private final HttpHandler httpHandler;