Correlated WebFlux server log messages

Issue: SPR-16966
This commit is contained in:
Rossen Stoyanchev
2018-07-03 15:54:19 -04:00
parent 010ba33d03
commit fd90b73748
27 changed files with 146 additions and 60 deletions

View File

@@ -43,6 +43,16 @@ import org.springframework.util.MultiValueMap;
*/
public interface ServerWebExchange {
/**
* Name of {@link #getAttributes() attribute} whose value correlates log
* messages for the exchange. Use {@link #getLogPrefix()} to obtain a
* consistently formatted prefix based on this attribute.
* @since 5.1
* @see #getLogPrefix()
*/
String LOG_ID_ATTRIBUTE = ServerWebExchange.class.getName() + ".LOG_ID";
/**
* Return the current HTTP request.
*/
@@ -208,6 +218,16 @@ public interface ServerWebExchange {
*/
void addUrlTransformer(Function<String, String> transformer);
/**
* Return a common prefix to use for log messages related to the exchange.
* The prefix is based on the value of the {@link #LOG_ID_ATTRIBUTE}
* attribute along with some extra formatting.
* @return the log message prefix or an empty String if the
* {@link #LOG_ID_ATTRIBUTE} is not set.
* @since 5.1
*/
String getLogPrefix();
/**
* Return a builder to mutate properties of this exchange by wrapping it
* with {@link ServerWebExchangeDecorator} and returning either mutated

View File

@@ -137,6 +137,11 @@ public class ServerWebExchangeDecorator implements ServerWebExchange {
getDelegate().addUrlTransformer(transformer);
}
@Override
public String getLogPrefix() {
return getDelegate().getLogPrefix();
}
@Override
public String toString() {
return getClass().getSimpleName() + " [delegate=" + getDelegate() + "]";

View File

@@ -98,6 +98,11 @@ public class DefaultServerWebExchange implements ServerWebExchange {
private Function<String, String> urlTransformer = url -> url;
@Nullable
private Object logId;
private String logPrefix = "";
public DefaultServerWebExchange(ServerHttpRequest request, ServerHttpResponse response,
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer,
@@ -355,4 +360,14 @@ public class DefaultServerWebExchange implements ServerWebExchange {
this.urlTransformer = this.urlTransformer.andThen(transformer);
}
@Override
public String getLogPrefix() {
Object value = getAttribute(LOG_ID_ATTRIBUTE);
if (this.logId != value) {
this.logId = value;
this.logPrefix = value != null ? "[" + value + "] " : "";
}
return this.logPrefix;
}
}

View File

@@ -34,6 +34,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
@@ -214,11 +215,15 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
ServerWebExchange exchange = createExchange(request, response);
String logId = ObjectUtils.getIdentityHexString(request);
exchange.getAttributes().put(ServerWebExchange.LOG_ID_ATTRIBUTE, logId);
logExchange(exchange);
return getDelegate().handle(exchange)
.doOnSuccess(aVoid -> logResponse(response))
.onErrorResume(ex -> handleUnresolvedError(request, response, ex))
.doOnSuccess(aVoid -> logResponse(exchange))
.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
.then(Mono.defer(response::setComplete));
}
@@ -229,13 +234,14 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
private void logExchange(ServerWebExchange exchange) {
if (logger.isDebugEnabled()) {
String logPrefix = exchange.getLogPrefix();
ServerHttpRequest request = exchange.getRequest();
if (logger.isTraceEnabled()) {
String headers = this.disableLoggingRequestDetails ? "" : ", headers=" + request.getHeaders();
logger.trace(formatRequest(request) + headers);
logger.trace(logPrefix + formatRequest(request) + headers);
}
else {
logger.debug(formatRequest(request));
logger.debug(logPrefix + formatRequest(request));
}
}
}
@@ -249,40 +255,45 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
return "HTTP " + request.getMethod() + " \"" + request.getPath() + query + "\"";
}
private void logResponse(ServerHttpResponse response) {
private void logResponse(ServerWebExchange exchange) {
if (logger.isDebugEnabled()) {
String logPrefix = exchange.getLogPrefix();
ServerHttpResponse response = exchange.getResponse();
HttpStatus status = response.getStatusCode();
String message = "Completed " + (status != null ? status : "200 OK");
if (logger.isTraceEnabled()) {
String headers = this.disableLoggingRequestDetails ? "" : ", headers=" + response.getHeaders();
logger.trace(message + headers);
logger.trace(logPrefix + message + headers);
}
else {
logger.debug(message);
logger.debug(logPrefix + message);
}
}
}
private Mono<Void> handleUnresolvedError(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) {
private Mono<Void> handleUnresolvedError(ServerWebExchange exchange, Throwable ex) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String logPrefix = exchange.getLogPrefix();
if (isDisconnectedClientError(ex)) {
if (lostClientLogger.isTraceEnabled()) {
lostClientLogger.trace("Client went away", ex);
lostClientLogger.trace(logPrefix + "Client went away", ex);
}
else if (lostClientLogger.isDebugEnabled()) {
lostClientLogger.debug("Client went away: " + ex +
lostClientLogger.debug(logPrefix + "Client went away: " + ex +
" (stacktrace at TRACE level for '" + DISCONNECTED_CLIENT_LOG_CATEGORY + "')");
}
return Mono.empty();
}
else if (response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR)) {
logger.error("500 Server Error for " + formatRequest(request), ex);
logger.error(logPrefix + "500 Server Error for " + formatRequest(request), ex);
return Mono.empty();
}
else {
// After the response is committed, propagate errors to the server..
logger.error("Error [" + ex + "] for " + formatRequest(request) +
logger.error(logPrefix + "Error [" + ex + "] for " + formatRequest(request) +
", but ServerHttpResponse already committed (" + response.getStatusCode() + ")");
return Mono.error(ex);
}

View File

@@ -70,11 +70,12 @@ public class ResponseStatusExceptionHandler implements WebExceptionHandler {
// Mirrors AbstractHandlerExceptionResolver in spring-webmvc..
String logPrefix = exchange.getLogPrefix();
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
this.warnLogger.warn(formatError(ex, exchange.getRequest()), ex);
this.warnLogger.warn(logPrefix + formatError(ex, exchange.getRequest()), ex);
}
else if (logger.isDebugEnabled()) {
logger.debug(formatError(ex, exchange.getRequest()));
logger.debug(logPrefix + formatError(ex, exchange.getRequest()));
}
return exchange.getResponse().setComplete();