Skip to content

Commit

Permalink
Merge pull request wildfly#5789 from yersan/WFCORE-6579
Browse files Browse the repository at this point in the history
[WFCORE-6579] Allow process controller log file capture initial manag…
  • Loading branch information
yersan authored Dec 12, 2023
2 parents c1dd951 + cf05f57 commit f62fbf7
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ public static void main(String[] args) throws IOException {
} catch (Throwable ignored) {
}

// This message is not used to display any information on the stdout and stderr of this process, it signals the
// Process Controller to switch the stdout and stderr of this managed process from the process controller log to
// the process controller stdout and stderr.
// Once the StdioContext gets installed, both the stdout and stderr of this managed process will be captured,
// aggregated and handled by this process logging framework.
STDOUT.println(ProcessController.STDIO_ABOUT_TO_INSTALL_MSG);
STDOUT.flush();
STDERR.println(ProcessController.STDIO_ABOUT_TO_INSTALL_MSG);
STDERR.flush();

// Install JBoss Stdio to avoid any nasty crosstalk.
StdioContext.install();
final StdioContext context = StdioContext.create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ public class CommandLineConstants {
public static final String PREFER_IPV4_STACK = "java.net.preferIPv4Stack";
public static final String PREFER_IPV6_ADDRESSES = "java.net.preferIPv6Addresses";

public static final String MANAGED_PROCESS_SYSTEM_ERROR_TO_LOG = "jboss.domain.managed-process.system.stderr.to.process-controller.log";
public static final String MANAGED_PROCESS_SYSTEM_OUT_TO_LOG = "jboss.domain.managed-process.system.stdout.to.process-controller.log";

/** Additional class path items, used only by app client*/
public static final String APPCLIENT_CONFIG = "--appclient-config";
public static final String SHORT_HOST = "-H";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
package org.jboss.as.process;

import static java.lang.Thread.holdsLock;
import static org.jboss.as.process.CommandLineConstants.MANAGED_PROCESS_SYSTEM_ERROR_TO_LOG;
import static org.jboss.as.process.CommandLineConstants.MANAGED_PROCESS_SYSTEM_OUT_TO_LOG;
import static org.jboss.as.process.protocol.StreamUtils.copyStream;
import static org.jboss.as.process.protocol.StreamUtils.safeClose;
import static org.jboss.as.process.protocol.StreamUtils.writeBoolean;
Expand All @@ -27,11 +29,13 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import org.jboss.as.process.logging.ProcessLogger;
import org.jboss.as.process.stdin.Base64OutputStream;
import org.jboss.logging.Logger;
import org.wildfly.common.Assert;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
* A managed process.
Expand All @@ -41,12 +45,26 @@
* @author Emanuel Muckenhuber
*/
final class ManagedProcess {
// If true, the managed process standard error will be captured in the process controller log file until the managed
// process has installed the JBoss Stdio context, otherwise the output will be redirected to the Process Controller
// standard error.
private static final boolean MANAGED_PROCESS_SYSTEM_ERROR_TO_PROCESS_CONTROLLER_LOG = Boolean.parseBoolean(
WildFlySecurityManager.getPropertyPrivileged(MANAGED_PROCESS_SYSTEM_ERROR_TO_LOG, "true")
);
// If true, the managed process standard output will be captured in the process controller log file until the managed
// process has installed the JBoss Stdio context, otherwise the output will be redirected to the Process Controller
// standard output.
private static final boolean MANAGED_PROCESS_SYSTEM_OUT_TO_PROCESS_CONTROLLER_LOG = Boolean.parseBoolean(
WildFlySecurityManager.getPropertyPrivileged(MANAGED_PROCESS_SYSTEM_OUT_TO_LOG, "true")
);

private final String processName;
private final List<String> command;
private final Map<String, String> env;
private final String workingDirectory;
private final ProcessLogger log;
private final ProcessLogger logStatus;
private final ProcessLogger logSystemErr;
private final ProcessLogger logSystemOut;
private final Object lock;

private final ProcessController processController;
Expand Down Expand Up @@ -107,7 +125,9 @@ enum State {
this.pcAuthKey = pcAuthKey;
isPrivileged = privileged;
respawnPolicy = respawn ? RespawnPolicy.RESPAWN : RespawnPolicy.NONE;
log = Logger.getMessageLogger(ProcessLogger.class, "org.jboss.as.process." + processName + ".status");
logStatus = Logger.getMessageLogger(ProcessLogger.class, "org.jboss.as.process." + processName + ".status");
logSystemErr = Logger.getMessageLogger(ProcessLogger.class, "org.jboss.as.process." + processName + ".system.stderr");
logSystemOut = Logger.getMessageLogger(ProcessLogger.class, "org.jboss.as.process." + processName + ".system.stdout");
}

int incrementAndGetRespawnCount() {
Expand All @@ -125,7 +145,7 @@ public String getProcessName() {
public void start() {
synchronized (lock) {
if (state != State.DOWN) {
log.debugf("Attempted to start already-running process '%s'", processName);
logStatus.debugf("Attempted to start already-running process '%s'", processName);
return;
}
resetRespawnCount();
Expand All @@ -142,7 +162,7 @@ public void sendStdin(final InputStream msg) throws IOException {
base64.close(); // not flush(). close() writes extra data to the stream allowing Base64 input stream
// to distinguish end of message
} catch (IOException e) {
log.failedToSendDataBytes(e, processName);
logStatus.failedToSendDataBytes(e, processName);
throw e;
}
}
Expand All @@ -162,7 +182,7 @@ public void reconnect(String scheme, String hostName, int port, boolean manageme
} catch (IOException e) {
if(state == State.STARTED) {
// Only log in case the process is still running
log.failedToSendReconnect(e, processName);
logStatus.failedToSendReconnect(e, processName);
}
}
}
Expand All @@ -176,8 +196,8 @@ void doStart(boolean restart) {
//Add the restart flag to the HC process if we are respawning it
command.add(CommandLineConstants.PROCESS_RESTARTED);
}
log.startingProcess(processName);
log.debugf("Process name='%s' command='%s' workingDirectory='%s'", processName, command, workingDirectory);
logStatus.startingProcess(processName);
logStatus.debugf("Process name='%s' command='%s' workingDirectory='%s'", processName, command, workingDirectory);
List<String> list = new ArrayList<>();
for (String c : command) {
String trim = c.trim();
Expand All @@ -191,17 +211,23 @@ void doStart(boolean restart) {
process = builder.start();
} catch (IOException e) {
processController.operationFailed(processName, ProcessMessageHandler.OperationType.START);
log.failedToStartProcess(e,processName);
logStatus.failedToStartProcess(e,processName);
return;
}
final long startTime = System.currentTimeMillis();
final OutputStream stdin = process.getOutputStream();
final InputStream stderr = process.getErrorStream();
final InputStream stdout = process.getInputStream();
final Thread stderrThread = new Thread(new ReadTask(stderr, processController.getStderr()));
final Thread stderrThread = new Thread( new ReadTask(stderr, processController.getStderr(),
MANAGED_PROCESS_SYSTEM_ERROR_TO_PROCESS_CONTROLLER_LOG,
logSystemErr::error)
);
stderrThread.setName(String.format("stderr for %s", processName));
stderrThread.start();
final Thread stdoutThread = new Thread(new ReadTask(stdout, processController.getStdout()));
final Thread stdoutThread = new Thread(new ReadTask(stdout, processController.getStdout(),
MANAGED_PROCESS_SYSTEM_OUT_TO_PROCESS_CONTROLLER_LOG,
logSystemOut::info)
);
stdoutThread.setName(String.format("stdout for %s", processName));
stdoutThread.start();

Expand All @@ -217,7 +243,7 @@ void doStart(boolean restart) {
// to distinguish end of message
ok = true;
} catch (Exception e) {
log.failedToSendAuthKey(processName, e);
logStatus.failedToSendAuthKey(processName, e);
}

this.process = process;
Expand All @@ -229,16 +255,15 @@ void doStart(boolean restart) {
} else {
processController.operationFailed(processName, ProcessMessageHandler.OperationType.START);
}
return;
}

public void stop() {
synchronized (lock) {
if (state != State.STARTED) {
log.debugf("Attempted to stop already-stopping or down process '%s'", processName);
logStatus.debugf("Attempted to stop already-stopping or down process '%s'", processName);
return;
}
log.stoppingProcess(processName);
logStatus.stoppingProcess(processName);
stopRequested = true;
safeClose(stdin);
state = State.STOPPING;
Expand All @@ -265,7 +290,7 @@ public void destroy() {
}
}
if (state != State.DOWN || jt == null || jt.isAlive()) { // Cover all bases just to be robust
log.destroyingProcess(processName, timeout);
logStatus.destroyingProcess(processName, timeout);
process.destroyForcibly();
}
}
Expand All @@ -292,10 +317,10 @@ public void kill() {
}

if (state != State.DOWN || jt == null || jt.isAlive()) { // Cover all bases just to be robust
log.attemptingToKillProcess(processName, timeout);
logStatus.attemptingToKillProcess(processName, timeout);
if (!ProcessUtils.killProcess(processName, id)) {
// Fallback to destroy if kill is not available
log.failedToKillProcess(processName);
logStatus.failedToKillProcess(processName);
process.destroyForcibly();
}
}
Expand All @@ -309,7 +334,7 @@ public void shutdown() {
}
shutdown = true;
if (state == State.STARTED) {
log.stoppingProcess(processName);
logStatus.stoppingProcess(processName);
stopRequested = true;
safeClose(stdin);
state = State.STOPPING;
Expand All @@ -329,7 +354,7 @@ public void run() {
void respawn() {
synchronized (lock) {
if (state != State.DOWN) {
log.debugf("Attempted to respawn already-running process '%s'", processName);
logStatus.debugf("Attempted to respawn already-running process '%s'", processName);
return;
}
doStart(true);
Expand Down Expand Up @@ -363,7 +388,7 @@ public void run() {
int exitCode;
for (;;) try {
exitCode = process.waitFor();
log.processFinished(processName, exitCode);
logStatus.processFinished(processName, exitCode);
break;
} catch (InterruptedException e) {
// ignore
Expand Down Expand Up @@ -433,10 +458,14 @@ public void run() {
private final class ReadTask implements Runnable {
private final InputStream source;
private final PrintStream target;
private boolean useLog;
private final Consumer<String> logConsumer;

private ReadTask(final InputStream source, final PrintStream target) {
private ReadTask(final InputStream source, final PrintStream target, boolean useLog, Consumer<String> logConsumer) {
this.source = source;
this.target = target;
this.useLog = useLog;
this.logConsumer = logConsumer;
}

public void run() {
Expand All @@ -448,25 +477,46 @@ public void run() {
String s;
String prevEscape = "";
while ((s = reader.readLine()) != null) {

if (s.contains(ProcessController.STDIO_ABOUT_TO_INSTALL_MSG)) {
useLog = false;
continue;
}

// Has ANSI?
int i = s.lastIndexOf('\033');
int j = i != -1 ? s.indexOf('m', i) : 0;

synchronized (target) {
writer.write('[');
writer.write(processName);
writer.write("] ");
writer.write(prevEscape);
writer.write(s);
if (useLog) {
StringBuilder sp = new StringBuilder();
sp.append("[");
sp.append(processName);
sp.append("] ");
sp.append(prevEscape);
sp.append(s);

// Reset if there was ANSI
if (j != 0 || prevEscape != "") {
writer.write("\033[0m");
if (j != 0 || !prevEscape.isEmpty()) {
sp.append("\033[0m");
}
writer.write('\n');
writer.flush();
}

logConsumer.accept(sp.toString());
} else {
synchronized (target) {
writer.write('[');
writer.write(processName);
writer.write("] ");
writer.write(prevEscape);
writer.write(s);

// Reset if there was ANSI
if (j != 0 || !prevEscape.isEmpty()) {
writer.write("\033[0m");
}
writer.write('\n');
writer.flush();
}
}

// Remember escape code for the next line
if (j != 0) {
Expand All @@ -480,7 +530,7 @@ public void run() {
}
source.close();
} catch (IOException e) {
log.streamProcessingFailed(processName, e);
logStatus.streamProcessingFailed(processName, e);
} finally {
safeClose(source);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public final class ProcessController {
public static final String STDIO_ABOUT_TO_INSTALL_MSG = "--Installing Stdio Context--";

/**
* Main lock - anything which opens a file descriptor or spawns a process must
Expand Down
16 changes: 14 additions & 2 deletions server/src/main/java/org/jboss/as/server/DomainServerMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@

package org.jboss.as.server;

import static org.jboss.as.server.DomainServerCommunicationServices.createAuthenticationContext;

import static org.jboss.as.process.protocol.StreamUtils.readBoolean;
import static org.jboss.as.process.protocol.StreamUtils.readFully;
import static org.jboss.as.process.protocol.StreamUtils.readInt;
import static org.jboss.as.process.protocol.StreamUtils.readUTFZBytes;
import static org.jboss.as.server.DomainServerCommunicationServices.createAuthenticationContext;

import java.io.EOFException;
import java.io.IOException;
Expand Down Expand Up @@ -55,6 +54,9 @@
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public final class DomainServerMain {
// Capture System.out and System.err before they are redirected by STDIO
private static final PrintStream STDOUT = System.out;
private static final PrintStream STDERR = System.err;

/**
* Cache of the latest {@code AuthenticationContext} based on updates received
Expand All @@ -81,6 +83,16 @@ public static void main(String[] args) {
} catch (Throwable ignored) {
}

// This message is not used to display any information on the stdout and stderr of this process, it signals the
// Process Controller to switch the stdout and stderr of this managed process from the process controller log to
// the process controller stdout and stderr.
// Once the StdioContext gets installed, both the stdout and stderr of this managed process will be captured,
// aggregated and handled by this process logging framework.
STDOUT.println(ProcessController.STDIO_ABOUT_TO_INSTALL_MSG);
STDOUT.flush();
STDERR.println(ProcessController.STDIO_ABOUT_TO_INSTALL_MSG);
STDERR.flush();

// Install JBoss Stdio to avoid any nasty crosstalk.
StdioContext.install();
final StdioContext context = StdioContext.create(
Expand Down

0 comments on commit f62fbf7

Please sign in to comment.