Correlated WebFlux server log messages
Issue: SPR-16966
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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() + "]";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user