diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9abf65d164b..85ba411a648 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
* Fix #4247: NO_PROXY with invalid entries throws exception
#### Improvements
+* Fix #4254: adding debug logging for exec stream messages
* Fix #4041: adding Quantity.getNumericalAmount with an explanation about bytes and cores.
* Fix #4241: added more context to informer logs with the endpoint path
* Fix #4250: allowing for deserialization of polymorphic unwrapped fields
diff --git a/doc/FAQ.md b/doc/FAQ.md
index 3625af31e68..c07031e0770 100644
--- a/doc/FAQ.md
+++ b/doc/FAQ.md
@@ -30,7 +30,7 @@ If you wish to use another HttpClient implementation typically you will exclude
### What threading concerns are there?
-There has been a lot of changes under the covers with thread utilization in the fabric8 client over the 5.x and 6.x releases. So the exact details of what threads are created / used where will depend on the particular release version.
+There has been a lot of changes under the covers with thread utilization in the Fabric8 client over the 5.x and 6.x releases. So the exact details of what threads are created / used where will depend on the particular release version.
At the core the thread utilization will depend upon the http client implementation. Per client OkHttp maintains a pool of threads for task execution. It will dedicate 2 threads out of that pool per WebSocket connection. If you have a lot of WebSocket usage (Informer or Watches) with OkHttp, you can expect to see a large number of threads in use - which can potentially exhaust the OkHttp defaults.
@@ -40,6 +40,12 @@ With the JDK http client it will only maintain a selector thread and a small wor
For non-ResourceEventHandlers call backs long-running operation can be a problem. When using the OkHttp client and default settings holding a IO thread inhibits websocket processing that can timeout the ping and may prevent additional requests since the okhttp client defaults to only 5 concurrent requests per host. When using the JDK http client the long running task will inhibit the use of that IO thread for ALL http processing. Note that calling other KubernetesClient operations, especially those with waits, can be long-running. We are working towards providing non-blocking mode for many of these operations, but until that is available consider using a separate task queue for such work.
-On top of the http client threads the fabric8 client maintains a task thread pool for scheduled tasks and for potentially long-running tasks that are called from WebSocket operations, such as handling input and output streams and ResourceEventHandler call backs. This thread pool defaults to an unlimited number of cached threads, which will be shutdown when the client is closed - that is a sensible default with either http client as the amount of concurrently running async tasks will typically be low. If you would rather take full control over the threading use KubernetesClientBuilder.withExecutor or KubernetesClientBuilder.withExecutorSupplier - however note that constraining this thread pool too much will result in a build up of event processing queues.
+On top of the http client threads the Fabric8 client maintains a task thread pool for scheduled tasks and for potentially long-running tasks that are called from WebSocket operations, such as handling input and output streams and ResourceEventHandler call backs. This thread pool defaults to an unlimited number of cached threads, which will be shutdown when the client is closed - that is a sensible default with either http client as the amount of concurrently running async tasks will typically be low. If you would rather take full control over the threading use KubernetesClientBuilder.withExecutor or KubernetesClientBuilder.withExecutorSupplier - however note that constraining this thread pool too much will result in a build up of event processing queues.
Finally the fabric8 client will use 1 thread per PortForward and an additional thread per socket connection - this may be improved upon in the future.
+
+### What additional logging is available?
+
+Like many java application the Fabric8 Client utilizes [slf4j](https://www.slf4j.org/). You may configure support for whatever underlying logging framework suits your needs. The logging contexts for the Fabric8 Client follow the standard convention of the package structure - everything from within the client will be rooted at the io.fabric8 context. Third-party dependencies, including the chosen HTTP client implementation, will have different root contexts.
+
+If you are using pod exec, which can occur indirectly via pod operations like copying files, and not seeing the expected behavior - please enable debug logging for the io.fabric8.kubernetes.client.dsl.internal context. That will provide the stdErr and stdOut as debug logs to further diagnose what is occurring.
diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListener.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListener.java
index 7e0d3bdd856..fb127ac8c7f 100644
--- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListener.java
+++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListener.java
@@ -100,17 +100,29 @@ public interface MessageHandler {
private final class ListenerStream {
private MessageHandler handler;
private ExecWatchInputStream inputStream;
+ private String name;
+
+ public ListenerStream(String name) {
+ this.name = name;
+ }
private void handle(ByteBuffer byteString, WebSocket webSocket) throws IOException {
if (handler != null) {
handler.handle(byteString);
} else {
+ if (LOGGER.isDebugEnabled()) {
+ String message = ExecWebSocketListener.toString(byteString);
+ if (message.length() > 200) {
+ message = message.substring(0, 197) + "...";
+ }
+ LOGGER.debug("exec message received on channel {}: {}", name, message);
+ }
webSocket.request();
}
}
}
- private static final Logger LOGGER = LoggerFactory.getLogger(ExecWebSocketListener.class);
+ static final Logger LOGGER = LoggerFactory.getLogger(ExecWebSocketListener.class);
private static final String HEIGHT = "Height";
private static final String WIDTH = "Width";
@@ -152,14 +164,14 @@ public ExecWebSocketListener(PodOperationContext context, Executor executor) {
}
this.terminateOnError = context.isTerminateOnError();
- this.out = createStream(context.getOutput());
- this.error = createStream(context.getError());
- this.errorChannel = createStream(context.getErrorChannel());
+ this.out = createStream("stdOut", context.getOutput());
+ this.error = createStream("stdErr", context.getError());
+ this.errorChannel = createStream("errorChannel", context.getErrorChannel());
this.serialExecutor = new SerialExecutor(executor);
}
- private ListenerStream createStream(StreamContext streamContext) {
- ListenerStream stream = new ListenerStream();
+ private ListenerStream createStream(String name, StreamContext streamContext) {
+ ListenerStream stream = new ListenerStream(name);
if (streamContext == null) {
return stream;
}
diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/PodOperationContext.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/PodOperationContext.java
index 7ec951b78cf..bc4ccfbf08c 100644
--- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/PodOperationContext.java
+++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/PodOperationContext.java
@@ -164,10 +164,11 @@ public void addQueryParameters(URLBuilder httpUrlBuilder) {
if (in != null || redirectingIn) {
httpUrlBuilder.addQueryParameter("stdin", "true");
}
- if (output != null) {
+ boolean debug = ExecWebSocketListener.LOGGER.isDebugEnabled();
+ if (output != null || debug) {
httpUrlBuilder.addQueryParameter("stdout", "true");
}
- if (error != null || terminateOnError) {
+ if (error != null || terminateOnError || debug) {
httpUrlBuilder.addQueryParameter("stderr", "true");
}
}
diff --git a/kubernetes-itests/pom.xml b/kubernetes-itests/pom.xml
index 1f02f289a04..a7bb2a45d5d 100644
--- a/kubernetes-itests/pom.xml
+++ b/kubernetes-itests/pom.xml
@@ -58,6 +58,11 @@
org.awaitility
awaitility
+
+ org.slf4j
+ slf4j-simple
+ test
+