8273471: Add foldmultilines to UL for stdout/err

Reviewed-by: dholmes, iklam
This commit is contained in:
Yasumasa Suenaga 2021-09-14 05:12:36 +00:00
parent c54a918a0e
commit 8d73ee684a
7 changed files with 111 additions and 44 deletions

@ -404,7 +404,40 @@ bool LogConfiguration::parse_command_line_arguments(const char* opts) {
char* output_options = substrings[3];
char errbuf[512];
stringStream ss(errbuf, sizeof(errbuf));
bool success = parse_log_arguments(output, what, decorators, output_options, &ss);
bool success = true;
// output options for stdout/err should be applied just once.
static bool stdout_configured = false;
static bool stderr_configured = false;
// Normally options can't be used to change an existing output
// (parse_log_arguments() will report an error), and
// both StdoutLog and StderrLog are created by static initializers,
// so we have to process their options (e.g. foldmultilines) directly first.
if (output == NULL || strlen(output) == 0 ||
strcmp("stdout", output) == 0 || strcmp("#0", output) == 0) {
if (!stdout_configured) {
success = StdoutLog.parse_options(output_options, &ss);
stdout_configured = true;
// We no longer need to pass output options to parse_log_arguments().
output_options = NULL;
}
// else - fall-through to normal option processing which will be rejected
// with a warning
} else if (strcmp("stderr", output) == 0 || strcmp("#1", output) == 0) {
if (!stderr_configured) {
success = StderrLog.parse_options(output_options, &ss);
stderr_configured = true;
// We no longer need to pass output options to parse_log_arguments().
output_options = NULL;
}
// else - fall-through to normal option processing which will be rejected
// with a warning
}
if (success) {
success = parse_log_arguments(output, what, decorators, output_options, &ss);
}
if (ss.size() > 0) {
// If it failed, log the error. If it didn't fail, but something was written
@ -558,28 +591,34 @@ void LogConfiguration::print_command_line_help(outputStream* out) {
out->cr();
LogTagSet::describe_tagsets(out);
out->cr();
out->print_cr("\nAvailable log outputs:");
out->print_cr("Available log outputs:");
out->print_cr(" stdout/stderr");
out->print_cr(" file=<filename>");
out->print_cr(" If the filename contains %%p and/or %%t, they will expand to the JVM's PID and startup timestamp, respectively.");
out->print_cr(" Additional output-options for file outputs:");
out->print_cr(" filesize=.. - Target byte size for log rotation (supports K/M/G suffix)."
" If set to 0, log rotation will not trigger automatically,"
" but can be performed manually (see the VM.log DCMD).");
out->print_cr(" filecount=.. - Number of files to keep in rotation (not counting the active file)."
" If set to 0, log rotation is disabled."
" This will cause existing log files to be overwritten.");
out->print_cr(" foldmultilines=.. - If set to true, a log event that consists of multiple lines"
" will be folded into a single line by replacing newline characters"
" with the sequence '\\' and 'n' in the output."
" Existing single backslash characters will also be replaced"
" with a sequence of two backslashes so that the conversion can be reversed."
" This option is safe to use with UTF-8 character encodings,"
" but other encodings may not work.");
out->cr();
out->print_cr("\nAsynchronous logging (off by default):");
out->print_cr("Available log output options:");
out->print_cr(" foldmultilines=.. - If set to true, a log event that consists of multiple lines"
" will be folded into a single line by replacing newline characters"
" with the sequence '\\' and 'n' in the output."
" Existing single backslash characters will also be replaced"
" with a sequence of two backslashes so that the conversion can be reversed."
" This option is safe to use with UTF-8 character encodings,"
" but other encodings may not work.");
out->cr();
out->print_cr("Additional file output options:");
out->print_cr(" filesize=.. - Target byte size for log rotation (supports K/M/G suffix)."
" If set to 0, log rotation will not trigger automatically,"
" but can be performed manually (see the VM.log DCMD).");
out->print_cr(" filecount=.. - Number of files to keep in rotation (not counting the active file)."
" If set to 0, log rotation is disabled."
" This will cause existing log files to be overwritten.");
out->cr();
out->print_cr("Asynchronous logging (off by default):");
out->print_cr(" -Xlog:async");
out->print_cr(" All log messages are written to an intermediate buffer first and will then be flushed"
" to the corresponding log outputs by a standalone thread. Write operations at logsites are"

@ -457,10 +457,8 @@ char* LogFileOutput::make_file_name(const char* file_name,
}
void LogFileOutput::describe(outputStream *out) {
LogOutput::describe(out);
out->print(" ");
out->print("filecount=%u,filesize=" SIZE_FORMAT "%s,async=%s", _file_count,
LogFileStreamOutput::describe(out);
out->print(",filecount=%u,filesize=" SIZE_FORMAT "%s,async=%s", _file_count,
byte_size_in_proper_unit(_rotate_size),
proper_unit_for_byte_size(_rotate_size),
LogConfiguration::is_async_mode() ? "true" : "false");

@ -188,3 +188,10 @@ int LogFileStreamOutput::write(LogMessageBuffer::Iterator msg_iterator) {
return flush() ? written : -1;
}
void LogFileStreamOutput::describe(outputStream *out) {
LogOutput::describe(out);
out->print(" ");
out->print("foldmultilines=%s", _fold_multilines ? "true" : "false");
}

@ -64,6 +64,7 @@ class LogFileStreamOutput : public LogOutput {
virtual bool set_option(const char* key, const char* value, outputStream* errstream);
virtual int write(const LogDecorations& decorations, const char* msg);
virtual int write(LogMessageBuffer::Iterator msg_iterator);
virtual void describe(outputStream* out);
};
class LogStdoutOutput : public LogFileStreamOutput {

@ -4433,7 +4433,6 @@ This option is safe to use with UTF\-8 character encodings, but other
encodings may not work.
For example, it may incorrectly convert multi\-byte sequences in Shift
JIS and BIG5.
This option is available only for file outputs.
.RE
.SS Default Configuration
.PP

@ -226,7 +226,7 @@ TEST_VM_F(LogConfigurationTest, reconfigure_decorators) {
// Now reconfigure logging on stderr with no decorators
set_log_config("stderr", "all=off", "none");
EXPECT_TRUE(is_described("#1: stderr all=off none (reconfigured)\n")) << "Expecting no decorators";
EXPECT_TRUE(is_described("#1: stderr all=off none foldmultilines=false (reconfigured)\n")) << "Expecting no decorators";
}
class ConcurrentLogsite : public TestRunnable {

@ -24,38 +24,47 @@
/*
* @test
* @bug 8271186
* @bug 8271186 8273471
* @library /test/lib
* @run driver FoldMultilinesTest
*/
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.regex.Pattern;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
public class FoldMultilinesTest {
private static Path EXCEPTION_LOG_FILE = Path.of("exceptions.log");
private static String XLOG_BASE = "-Xlog:exceptions=info:file=" + EXCEPTION_LOG_FILE.toString();
private static String EXCEPTION_LOG_FILE = "exceptions.log";
private static String XLOG_BASE = "-Xlog:exceptions=info:";
private static String EXCEPTION_MESSAGE = "line 1\nline 2\\nstring";
private static String FOLDED_EXCEPTION_MESSAGE = "line 1\\nline 2\\\\nstring";
private static Pattern NEWLINE_LOG_PATTERN = Pattern.compile("line 1\\Rline 2\\\\nstring", Pattern.MULTILINE);
private static void analyzeFoldMultilinesOn(ProcessBuilder pb) throws Exception {
private static String getLog(String out, OutputAnalyzer output) throws Exception {
return switch (out) {
case "" -> output.getStdout();
case "stdout" -> output.getStdout();
case "stderr" -> output.getStderr();
default -> Files.readString(Path.of(EXCEPTION_LOG_FILE));
};
}
private static void analyzeFoldMultilinesOn(ProcessBuilder pb, String out) throws Exception {
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
String logs = Files.readString(EXCEPTION_LOG_FILE);
if (!logs.contains("line 1\\nline 2\\\\nstring")) {
throw new RuntimeException("foldmultilines=true did not work.");
if (!getLog(out, output).contains(FOLDED_EXCEPTION_MESSAGE)) {
throw new RuntimeException(out + ": foldmultilines=true did not work.");
}
}
private static void analyzeFoldMultilinesOff(ProcessBuilder pb) throws Exception {
private static void analyzeFoldMultilinesOff(ProcessBuilder pb, String out) throws Exception {
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
String logs = Files.readString(EXCEPTION_LOG_FILE);
if (!logs.contains("line 1" + System.lineSeparator() + "line 2\\nstring")) {
throw new RuntimeException("foldmultilines=false did not work.");
if (!NEWLINE_LOG_PATTERN.matcher(getLog(out, output)).find()) {
throw new RuntimeException(out + ": foldmultilines=false did not work.");
}
}
@ -65,27 +74,41 @@ public class FoldMultilinesTest {
output.shouldNotHaveExitValue(0);
}
public static void main(String[] args) throws Exception {
private static void test(String out) throws Exception {
String Xlog;
ProcessBuilder pb;
Xlog = XLOG_BASE + "::foldmultilines=true";
Xlog = XLOG_BASE + out + "::foldmultilines=true";
pb = ProcessTools.createJavaProcessBuilder(Xlog, InternalClass.class.getName());
analyzeFoldMultilinesOn(pb);
analyzeFoldMultilinesOn(pb, out);
Xlog = XLOG_BASE + "::foldmultilines=false";
Xlog = XLOG_BASE + out + "::foldmultilines=false";
pb = ProcessTools.createJavaProcessBuilder(Xlog, InternalClass.class.getName());
analyzeFoldMultilinesOff(pb);
analyzeFoldMultilinesOff(pb, out);
Xlog = XLOG_BASE + "::foldmultilines=invalid";
Xlog = XLOG_BASE + out + "::foldmultilines=invalid";
pb = ProcessTools.createJavaProcessBuilder(Xlog, InternalClass.class.getName());
analyzeFoldMultilinesInvalid(pb);
}
public static void main(String[] args) throws Exception {
// -Xlog:exceptions=info:file=exceptions.log::foldmultilines=[true|false|invalid]
test("file=" + EXCEPTION_LOG_FILE);
// -Xlog:exceptions=info:stdout::foldmultilines=[true|false|invalid]
test("stdout");
// -Xlog:exceptions=info:stderr::foldmultilines=[true|false|invalid]
test("stderr");
// -Xlog:exceptions=info:::foldmultilines=[true|false|invalid]
test("");
}
public static class InternalClass {
public static void main(String[] args) {
try {
throw new RuntimeException("line 1\nline 2\\nstring");
throw new RuntimeException(EXCEPTION_MESSAGE);
} catch (Exception e) {
// Do nothing to return exit code 0
}