Skip to content

Commit f8de5b4

Browse files
authored
Log channel ID when logging error (#2892)
* Log channel ID when logging error * Review comments * Review comments * Review comments
1 parent c899417 commit f8de5b4

File tree

4 files changed

+365
-18
lines changed

4 files changed

+365
-18
lines changed

.idea/inspectionProfiles/AWS_Java_SDK_2_0.xml

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutor.java

+14-15
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@
5757
import org.reactivestreams.Publisher;
5858
import org.reactivestreams.Subscriber;
5959
import org.reactivestreams.Subscription;
60-
import org.slf4j.Logger;
61-
import org.slf4j.LoggerFactory;
6260
import software.amazon.awssdk.annotations.SdkInternalApi;
6361
import software.amazon.awssdk.http.Protocol;
6462
import software.amazon.awssdk.http.nio.netty.internal.http2.FlushOnReadHandler;
@@ -68,12 +66,13 @@
6866
import software.amazon.awssdk.http.nio.netty.internal.nrs.HttpStreamsClientHandler;
6967
import software.amazon.awssdk.http.nio.netty.internal.nrs.StreamedHttpRequest;
7068
import software.amazon.awssdk.http.nio.netty.internal.utils.ChannelUtils;
69+
import software.amazon.awssdk.http.nio.netty.internal.utils.NettyClientLogger;
7170
import software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils;
7271
import software.amazon.awssdk.metrics.MetricCollector;
7372

7473
@SdkInternalApi
7574
public final class NettyRequestExecutor {
76-
private static final Logger log = LoggerFactory.getLogger(NettyRequestExecutor.class);
75+
private static final NettyClientLogger log = NettyClientLogger.getLogger(NettyRequestExecutor.class);
7776
private static final RequestAdapter REQUEST_ADAPTER_HTTP2 = new RequestAdapter(Protocol.HTTP2);
7877
private static final RequestAdapter REQUEST_ADAPTER_HTTP1_1 = new RequestAdapter(Protocol.HTTP1_1);
7978
private static final AtomicLong EXECUTION_COUNTER = new AtomicLong(0L);
@@ -129,7 +128,7 @@ private CompletableFuture<Void> createExecutionFuture(Promise<Channel> channelPr
129128
}
130129
});
131130
} catch (Throwable exc) {
132-
log.warn("Unable to add a task to cancel the request to channel's EventLoop", exc);
131+
log.warn(ch, () -> "Unable to add a task to cancel the request to channel's EventLoop", exc);
133132
}
134133
}
135134
});
@@ -151,13 +150,13 @@ private void verifyMetricsWereCollected(CompletableFuture<Void> metricsFuture) {
151150
}
152151

153152
if (!metricsFuture.isDone()) {
154-
log.debug("HTTP request metric collection did not finish in time, so results may be incomplete.");
153+
log.debug(null, () -> "HTTP request metric collection did not finish in time, so results may be incomplete.");
155154
metricsFuture.cancel(false);
156155
return;
157156
}
158157

159158
metricsFuture.exceptionally(t -> {
160-
log.debug("HTTP request metric collection failed, so results may be incomplete.", t);
159+
log.debug(null, () -> "HTTP request metric collection failed, so results may be incomplete.", t);
161160
return null;
162161
});
163162
}
@@ -172,7 +171,7 @@ private void makeRequestListener(Future<Channel> channelFuture) {
172171
}
173172
});
174173
} else {
175-
handleFailure(() -> "Failed to create connection to " + endpoint(), channelFuture.cause());
174+
handleFailure(channel, () -> "Failed to create connection to " + endpoint(), channelFuture.cause());
176175
}
177176
}
178177

@@ -203,7 +202,7 @@ private boolean tryConfigurePipeline() {
203202
default:
204203
String errorMsg = "Unknown protocol: " + protocol;
205204
closeAndRelease(channel);
206-
handleFailure(() -> errorMsg, new RuntimeException(errorMsg));
205+
handleFailure(channel, () -> errorMsg, new RuntimeException(errorMsg));
207206
return false;
208207
}
209208

@@ -220,7 +219,7 @@ private boolean tryConfigurePipeline() {
220219
if (!channel.isActive()) {
221220
String errorMessage = "Channel was closed before it could be written to.";
222221
closeAndRelease(channel);
223-
handleFailure(() -> errorMessage, new IOException(errorMessage));
222+
handleFailure(channel, () -> errorMessage, new IOException(errorMessage));
224223
return false;
225224
}
226225

@@ -254,7 +253,7 @@ private void writeRequest(HttpRequest request) {
254253
} else {
255254
// TODO: Are there cases where we can keep the channel open?
256255
closeAndRelease(channel);
257-
handleFailure(() -> "Failed to make request to " + endpoint(), wireCall.cause());
256+
handleFailure(channel, () -> "Failed to make request to " + endpoint(), wireCall.cause());
258257
}
259258
});
260259

@@ -297,8 +296,8 @@ private URI endpoint() {
297296
return context.executeRequest().request().getUri();
298297
}
299298

300-
private void handleFailure(Supplier<String> msg, Throwable cause) {
301-
log.debug(msg.get(), cause);
299+
private void handleFailure(Channel channel, Supplier<String> msgSupplier, Throwable cause) {
300+
log.debug(channel, msgSupplier, cause);
302301
cause = decorateException(cause);
303302
context.handler().onError(cause);
304303
executeFuture.completeExceptionally(cause);
@@ -379,7 +378,7 @@ private String getMessageForTooManyAcquireOperationsError() {
379378
* @param channel The channel.
380379
*/
381380
private void closeAndRelease(Channel channel) {
382-
log.trace("closing and releasing channel {}", channel.id().asLongText());
381+
log.trace(channel, () -> String.format("closing and releasing channel %s", channel.id().asLongText()));
383382
channel.attr(KEEP_ALIVE).set(false);
384383
channel.close();
385384
context.channelPool().release(channel);
@@ -472,7 +471,7 @@ public String toString() {
472471
/**
473472
* Decorator around {@link StreamedHttpRequest} to adapt a publisher of {@link ByteBuffer} (i.e. {@link
474473
* software.amazon.awssdk.http.async.SdkHttpContentPublisher}) to a publisher of {@link HttpContent}.
475-
* <p />
474+
* <p>
476475
* This publisher also prevents the adapted publisher from publishing more content to the subscriber than
477476
* the specified 'Content-Length' of the request.
478477
*/
@@ -565,7 +564,7 @@ private static Optional<Long> contentLength(HttpRequest request) {
565564
try {
566565
return Optional.of(Long.parseLong(value));
567566
} catch (NumberFormatException e) {
568-
log.warn("Unable to parse 'Content-Length' header. Treating it as non existent.");
567+
log.warn(null, () -> "Unable to parse 'Content-Length' header. Treating it as non existent.");
569568
}
570569
}
571570
return Optional.empty();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http.nio.netty.internal.utils;
17+
18+
import io.netty.channel.Channel;
19+
import java.util.function.Supplier;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
import software.amazon.awssdk.annotations.SdkInternalApi;
23+
import software.amazon.awssdk.annotations.SdkTestInternalApi;
24+
25+
/**
26+
* Logger facade similar to {@link software.amazon.awssdk.utils.Logger}, that also includes channel information in the message
27+
* when provided. When the logger has at least DEBUG level enabled, the logger uses {@link Channel#toString()} to provide the
28+
* complete information about the channel. If only less verbose levels are available, then only the channel's ID is logged.
29+
* <p>
30+
* Having the channel information associated with the log message whenever available makes correlating messages that are all
31+
* logged within the context of that channel possible; this is impossible to do otherwise because there is a 1:M mapping from
32+
* event loops to channels.
33+
* <p>
34+
* <b>NOTE:</b> The absence of overrides that don't take a {@code Channel} parameter is deliberate. This is done to lessen the
35+
* chances that a {code Channel} is omitted from the log by accident.
36+
*/
37+
@SdkInternalApi
38+
public final class NettyClientLogger {
39+
private final Logger delegateLogger;
40+
41+
@SdkTestInternalApi
42+
NettyClientLogger(Logger delegateLogger) {
43+
this.delegateLogger = delegateLogger;
44+
}
45+
46+
public static NettyClientLogger getLogger(Class<?> clzz) {
47+
Logger delegate = LoggerFactory.getLogger(clzz);
48+
return new NettyClientLogger(delegate);
49+
}
50+
51+
/**
52+
* Log a DEBUG level message including the channel information.
53+
*
54+
* @param channel The channel for this message is being logged
55+
* @param msgSupplier Supplier for the log message
56+
*/
57+
public void debug(Channel channel, Supplier<String> msgSupplier) {
58+
debug(channel, msgSupplier, null);
59+
}
60+
61+
/**
62+
* Log a DEBUG level message with the given exception and including the channel information.
63+
*
64+
* @param channel The channel for this message is being logged
65+
* @param msgSupplier Supplier for the log message
66+
* @param t The throwable to log
67+
*/
68+
public void debug(Channel channel, Supplier<String> msgSupplier, Throwable t) {
69+
if (!delegateLogger.isDebugEnabled()) {
70+
return;
71+
}
72+
73+
String finalMessage = prependChannelInfo(msgSupplier, channel);
74+
delegateLogger.debug(finalMessage, t);
75+
}
76+
77+
/**
78+
* Log a WARN level message and including the channel information.
79+
*
80+
* @param channel The channel for this message is being logged
81+
* @param msgSupplier Supplier for the log message
82+
*/
83+
public void warn(Channel channel, Supplier<String> msgSupplier) {
84+
warn(channel, msgSupplier, null);
85+
}
86+
87+
/**
88+
* Log a WARN level message with the given exception and including the channel information.
89+
*
90+
* @param channel The channel for this message is being logged
91+
* @param msgSupplier Supplier for the log message
92+
* @param t The throwable to log
93+
*/
94+
public void warn(Channel channel, Supplier<String> msgSupplier, Throwable t) {
95+
if (!delegateLogger.isWarnEnabled()) {
96+
return;
97+
}
98+
99+
String finalMessage = prependChannelInfo(msgSupplier, channel);
100+
delegateLogger.warn(finalMessage, t);
101+
}
102+
103+
/**
104+
* Log a TRACE level message including the channel information.
105+
*
106+
* @param channel The channel for this message is being logged
107+
* @param msgSupplier Supplier for the log message
108+
*/
109+
public void trace(Channel channel, Supplier<String> msgSupplier) {
110+
if (!delegateLogger.isTraceEnabled()) {
111+
return;
112+
}
113+
114+
String finalMessage = prependChannelInfo(msgSupplier, channel);
115+
delegateLogger.trace(finalMessage);
116+
}
117+
118+
private String prependChannelInfo(Supplier<String> msgSupplier, Channel channel) {
119+
if (channel == null) {
120+
return msgSupplier.get();
121+
}
122+
123+
String id;
124+
if (!delegateLogger.isDebugEnabled()) {
125+
id = channel.id().asShortText();
126+
} else {
127+
id = channel.toString();
128+
}
129+
130+
return String.format("[Channel: %s] %s", id, msgSupplier.get());
131+
}
132+
}

0 commit comments

Comments
 (0)