From 390ac32d77950806576102d079ef127aa661177f Mon Sep 17 00:00:00 2001 From: Prateek Jain Date: Thu, 9 Feb 2023 17:53:15 +0530 Subject: [PATCH 1/6] Capturing startup logs and writing them to the logger incase end of stream reached --- .../testing/BlockingProcessStreamReader.java | 10 +++ .../cloud/testing/StartupLogAggregator.java | 65 ++++++++++++++++++ .../cloud/testing/BaseEmulatorHelperTest.java | 2 +- .../BlockingProcessStreamReaderTest.java | 19 ++++++ .../testing/StartupLogAggregatorTest.java | 68 +++++++++++++++++++ 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java create mode 100644 java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java index 90fbb764d55d..4f8973b2117b 100644 --- a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java @@ -43,6 +43,7 @@ class BlockingProcessStreamReader extends Thread { private boolean collectionMode; private final String emulatorTag; private final Pattern logLinePattern; + private final StartupLogAggregator logAggregator; private BlockingProcessStreamReader( String emulator, InputStream stream, String blockUntil, Logger logger) throws IOException { @@ -52,12 +53,21 @@ private BlockingProcessStreamReader( this.logger = logger; this.emulatorTag = "[" + emulator + "]"; this.logLinePattern = Pattern.compile("(\\[" + emulator + "\\]\\s)?(\\w+):.*"); + this.logAggregator = new StartupLogAggregator(logger); if (!Strings.isNullOrEmpty(blockUntil)) { String line; do { line = errorReader.readLine(); + if (line != null) { + logAggregator.process(line); + } } while (line != null && !line.contains(blockUntil)); } + + boolean streamClosed = errorReader.read() == -1; + if (streamClosed) { + logAggregator.writeLog(); + } } @Override diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java new file mode 100644 index 000000000000..d290b99d649d --- /dev/null +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java @@ -0,0 +1,65 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.testing; + +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StartupLogAggregator { + + private final Pattern logLinePattern = Pattern.compile("([A-Z]+):.*"); + private final Logger logger; + private final StringBuilder logs; + + public StartupLogAggregator(Logger logger) { + this.logger = logger; + this.logs = new StringBuilder(); + } + + public void process(String logLine) { + if (hasLevel(logLine)) { + String stripLevel = logLine.split(":")[1].trim(); + this.logs.append(stripLevel); + } else { + this.logs.append(logLine); + } + this.logs.append(System.getProperty("line.separator")); + } + + public void writeLog() { + logger.log(Level.INFO, this.logs.toString()); + } + + private boolean hasLevel(String line) { + return getLevel(line) != null; + } + + private Level getLevel(String line) { + try { + Matcher matcher = logLinePattern.matcher(line); + if (matcher.matches()) { + return Level.parse(matcher.group(1)); + } else { + return null; + } + } catch (IllegalArgumentException e) { + return null; + } + } +} diff --git a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BaseEmulatorHelperTest.java b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BaseEmulatorHelperTest.java index b002a6198bac..2c6d7495be52 100644 --- a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BaseEmulatorHelperTest.java +++ b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BaseEmulatorHelperTest.java @@ -57,7 +57,7 @@ protected List getEmulatorRunners() { @Override protected Logger getLogger() { - return null; + return Logger.getLogger(TestEmulatorHelper.class.getName()); } @Override diff --git a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java index 56b406f9634a..0e6b995fd1fb 100644 --- a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java +++ b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java @@ -16,6 +16,7 @@ package com.google.cloud.testing; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import com.google.api.client.util.Charsets; @@ -53,6 +54,11 @@ public class BlockingProcessStreamReaderTest { + "[emulator] log line 2\n" + "[emulator] Nov 08, 2016 2:05:44 PM io.netty.buffer.PooledByteBufAllocator \n" + "[emulator] FINE: log line 3\n"; + private static final String LOG_LINES_WITHOUT_BLOCK_UNTIL_TEXT = + "INFO: log line 1\n" + + "log line 2\n" + + "FINE: log line 3\n"; + @Rule public Timeout globalTimeout = Timeout.seconds(10); @@ -96,4 +102,17 @@ public void testForwardAlreadyTaggedLogs() throws IOException, InterruptedExcept assertEquals("[emulator] log line 3", logger.getLogs().get(Level.FINE).iterator().next()); stream.close(); } + + @Test + public void testStartUpLogs() throws IOException, InterruptedException { + TestLogger logger = new TestLogger(); + InputStream stream = new ByteArrayInputStream(LOG_LINES_WITHOUT_BLOCK_UNTIL_TEXT.getBytes(Charsets.UTF_8)); + BlockingProcessStreamReader.start("emulator", stream, BLOCK_UNTIL, logger).join(); + assertThat(logger.logs.get(Level.INFO).iterator().next()).isEqualTo( + "log line 1" + System.lineSeparator() + + "log line 2" + System.lineSeparator() + + "log line 3" + System.lineSeparator() + ); + stream.close(); + } } diff --git a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java new file mode 100644 index 000000000000..f626a258cc40 --- /dev/null +++ b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.testing; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.junit.Test; + +public class StartupLogAggregatorTest { + + private static final String LOG_LINES = + "INFO: log line 1\n" + + "log line 2\n" + + "FINE: log line 3\n"; + private final TestLogger testLogger = new TestLogger(); + private StartupLogAggregator logAggregator = new StartupLogAggregator(testLogger); + + + @Test + public void shouldAggregateLogs() { + for (String logLine : LOG_LINES.split("\n")) { + logAggregator.process(logLine); + } + logAggregator.writeLog(); + + assertThat(testLogger.logs.get(Level.INFO).iterator().next()).isEqualTo( + "log line 1" + System.lineSeparator() + + "log line 2" + System.lineSeparator() + + "log line 3" + System.lineSeparator() + ); + } + + private static final class TestLogger extends Logger { + + private final Multimap logs = LinkedHashMultimap.create(); + + private TestLogger() { + super("text-logger", null); + } + + public void log(Level level, String msg) { + logs.put(level, msg); + } + + Multimap getLogs() { + return logs; + } + } + +} \ No newline at end of file From 5caa7c86d0694126c83f2c9c9cec9091fe992b30 Mon Sep 17 00:00:00 2001 From: Prateek Jain Date: Fri, 10 Feb 2023 10:33:04 +0530 Subject: [PATCH 2/6] Javadoc and lint fix --- .../testing/BlockingProcessStreamReader.java | 7 ++--- ...ggregator.java => StartupLogRecorder.java} | 20 +++++++++---- .../BlockingProcessStreamReaderTest.java | 21 +++++++------- ...rTest.java => StartupLogRecorderTest.java} | 29 +++++++++---------- 4 files changed, 43 insertions(+), 34 deletions(-) rename java-core/google-cloud-core/src/main/java/com/google/cloud/testing/{StartupLogAggregator.java => StartupLogRecorder.java} (77%) rename java-core/google-cloud-core/src/test/java/com/google/cloud/testing/{StartupLogAggregatorTest.java => StartupLogRecorderTest.java} (75%) diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java index 4f8973b2117b..0aee13f1d178 100644 --- a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java @@ -43,7 +43,6 @@ class BlockingProcessStreamReader extends Thread { private boolean collectionMode; private final String emulatorTag; private final Pattern logLinePattern; - private final StartupLogAggregator logAggregator; private BlockingProcessStreamReader( String emulator, InputStream stream, String blockUntil, Logger logger) throws IOException { @@ -53,20 +52,20 @@ private BlockingProcessStreamReader( this.logger = logger; this.emulatorTag = "[" + emulator + "]"; this.logLinePattern = Pattern.compile("(\\[" + emulator + "\\]\\s)?(\\w+):.*"); - this.logAggregator = new StartupLogAggregator(logger); + StartupLogRecorder logAggregator = new StartupLogRecorder(logger); if (!Strings.isNullOrEmpty(blockUntil)) { String line; do { line = errorReader.readLine(); if (line != null) { - logAggregator.process(line); + logAggregator.record(line); } } while (line != null && !line.contains(blockUntil)); } boolean streamClosed = errorReader.read() == -1; if (streamClosed) { - logAggregator.writeLog(); + logAggregator.flush(); } } diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogRecorder.java similarity index 77% rename from java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java rename to java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogRecorder.java index d290b99d649d..af13db959c00 100644 --- a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogRecorder.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,18 +21,27 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class StartupLogAggregator { +/** + * This class records the logs in memory and flush them onto the {@link java.util.logging.Logger} + * when {@link #flush()} method is called. + */ +class StartupLogRecorder { private final Pattern logLinePattern = Pattern.compile("([A-Z]+):.*"); private final Logger logger; private final StringBuilder logs; - public StartupLogAggregator(Logger logger) { + public StartupLogRecorder(Logger logger) { this.logger = logger; this.logs = new StringBuilder(); } - public void process(String logLine) { + /** + * Records a log line. + * + * @param logLine the log to record. + */ + public void record(String logLine) { if (hasLevel(logLine)) { String stripLevel = logLine.split(":")[1].trim(); this.logs.append(stripLevel); @@ -42,7 +51,8 @@ public void process(String logLine) { this.logs.append(System.getProperty("line.separator")); } - public void writeLog() { + /** Flush all the logs to the underlying {@link java.util.logging.Logger}. */ + public void flush() { logger.log(Level.INFO, this.logs.toString()); } diff --git a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java index 0e6b995fd1fb..9b9d94d0bb7d 100644 --- a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java +++ b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java @@ -55,10 +55,7 @@ public class BlockingProcessStreamReaderTest { + "[emulator] Nov 08, 2016 2:05:44 PM io.netty.buffer.PooledByteBufAllocator \n" + "[emulator] FINE: log line 3\n"; private static final String LOG_LINES_WITHOUT_BLOCK_UNTIL_TEXT = - "INFO: log line 1\n" - + "log line 2\n" - + "FINE: log line 3\n"; - + "INFO: log line 1\n" + "log line 2\n" + "FINE: log line 3\n"; @Rule public Timeout globalTimeout = Timeout.seconds(10); @@ -106,13 +103,17 @@ public void testForwardAlreadyTaggedLogs() throws IOException, InterruptedExcept @Test public void testStartUpLogs() throws IOException, InterruptedException { TestLogger logger = new TestLogger(); - InputStream stream = new ByteArrayInputStream(LOG_LINES_WITHOUT_BLOCK_UNTIL_TEXT.getBytes(Charsets.UTF_8)); + InputStream stream = + new ByteArrayInputStream(LOG_LINES_WITHOUT_BLOCK_UNTIL_TEXT.getBytes(Charsets.UTF_8)); BlockingProcessStreamReader.start("emulator", stream, BLOCK_UNTIL, logger).join(); - assertThat(logger.logs.get(Level.INFO).iterator().next()).isEqualTo( - "log line 1" + System.lineSeparator() + - "log line 2" + System.lineSeparator() + - "log line 3" + System.lineSeparator() - ); + assertThat(logger.logs.get(Level.INFO).iterator().next()) + .isEqualTo( + "log line 1" + + System.lineSeparator() + + "log line 2" + + System.lineSeparator() + + "log line 3" + + System.lineSeparator()); stream.close(); } } diff --git a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogRecorderTest.java similarity index 75% rename from java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java rename to java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogRecorderTest.java index f626a258cc40..735028a136bc 100644 --- a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java +++ b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogRecorderTest.java @@ -24,28 +24,28 @@ import java.util.logging.Logger; import org.junit.Test; -public class StartupLogAggregatorTest { +public class StartupLogRecorderTest { private static final String LOG_LINES = - "INFO: log line 1\n" - + "log line 2\n" - + "FINE: log line 3\n"; + "INFO: log line 1\n" + "log line 2\n" + "FINE: log line 3\n"; private final TestLogger testLogger = new TestLogger(); - private StartupLogAggregator logAggregator = new StartupLogAggregator(testLogger); - + private final StartupLogRecorder logRecorder = new StartupLogRecorder(testLogger); @Test public void shouldAggregateLogs() { for (String logLine : LOG_LINES.split("\n")) { - logAggregator.process(logLine); + logRecorder.record(logLine); } - logAggregator.writeLog(); + logRecorder.flush(); - assertThat(testLogger.logs.get(Level.INFO).iterator().next()).isEqualTo( - "log line 1" + System.lineSeparator() + - "log line 2" + System.lineSeparator() + - "log line 3" + System.lineSeparator() - ); + assertThat(testLogger.logs.get(Level.INFO).iterator().next()) + .isEqualTo( + "log line 1" + + System.lineSeparator() + + "log line 2" + + System.lineSeparator() + + "log line 3" + + System.lineSeparator()); } private static final class TestLogger extends Logger { @@ -64,5 +64,4 @@ Multimap getLogs() { return logs; } } - -} \ No newline at end of file +} From d6f9508a4b73b6e3081f7eebfddd5449d0cffb30 Mon Sep 17 00:00:00 2001 From: Prateek Jain Date: Fri, 10 Feb 2023 10:47:35 +0530 Subject: [PATCH 3/6] Added the fix reasoning to fix this bug --- .../com/google/cloud/testing/BlockingProcessStreamReader.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java index 0aee13f1d178..517d14345a10 100644 --- a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java @@ -58,11 +58,13 @@ private BlockingProcessStreamReader( do { line = errorReader.readLine(); if (line != null) { - logAggregator.record(line); + logAggregator.record(line); // recording the logs as these might be the error logs. } } while (line != null && !line.contains(blockUntil)); } + /* If the stream is closed here, that means subprocess has been failed to start. In that case, we + should flush the recorded startup logs to help the user in debugging */ boolean streamClosed = errorReader.read() == -1; if (streamClosed) { logAggregator.flush(); From 582bafa365cee3cd35b3303aa4bf8f8267dbbdc4 Mon Sep 17 00:00:00 2001 From: Prateek Jain Date: Fri, 10 Feb 2023 11:05:04 +0530 Subject: [PATCH 4/6] fixing logs --- .../com/google/cloud/testing/BlockingProcessStreamReader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java index 517d14345a10..76cdbd1f9db6 100644 --- a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java @@ -58,13 +58,13 @@ private BlockingProcessStreamReader( do { line = errorReader.readLine(); if (line != null) { - logAggregator.record(line); // recording the logs as these might be the error logs. + logAggregator.record(line); // recording the logs as these might be the error logs. } } while (line != null && !line.contains(blockUntil)); } /* If the stream is closed here, that means subprocess has been failed to start. In that case, we - should flush the recorded startup logs to help the user in debugging */ + should flush the recorded startup logs to help the user in debugging */ boolean streamClosed = errorReader.read() == -1; if (streamClosed) { logAggregator.flush(); From 257db0ec6c5000df5cde6e4a6a43fef6239009eb Mon Sep 17 00:00:00 2001 From: Prateek Jain Date: Mon, 13 Feb 2023 11:33:33 +0530 Subject: [PATCH 5/6] improving java doc and adding test to reset the internal state of log recorder on every 'flush' call --- .../testing/BlockingProcessStreamReader.java | 6 ++-- ...artupLogRecorder.java => LogRecorder.java} | 16 ++++++--- ...RecorderTest.java => LogRecorderTest.java} | 33 +++++++++++++++++-- 3 files changed, 45 insertions(+), 10 deletions(-) rename java-core/google-cloud-core/src/main/java/com/google/cloud/testing/{StartupLogRecorder.java => LogRecorder.java} (78%) rename java-core/google-cloud-core/src/test/java/com/google/cloud/testing/{StartupLogRecorderTest.java => LogRecorderTest.java} (65%) diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java index 76cdbd1f9db6..baa747a74458 100644 --- a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java @@ -52,13 +52,13 @@ private BlockingProcessStreamReader( this.logger = logger; this.emulatorTag = "[" + emulator + "]"; this.logLinePattern = Pattern.compile("(\\[" + emulator + "\\]\\s)?(\\w+):.*"); - StartupLogRecorder logAggregator = new StartupLogRecorder(logger); + LogRecorder logRecorder = new LogRecorder(logger); if (!Strings.isNullOrEmpty(blockUntil)) { String line; do { line = errorReader.readLine(); if (line != null) { - logAggregator.record(line); // recording the logs as these might be the error logs. + logRecorder.record(line); // recording the logs as these might be the error logs. } } while (line != null && !line.contains(blockUntil)); } @@ -67,7 +67,7 @@ private BlockingProcessStreamReader( should flush the recorded startup logs to help the user in debugging */ boolean streamClosed = errorReader.read() == -1; if (streamClosed) { - logAggregator.flush(); + logRecorder.flush(); } } diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogRecorder.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/LogRecorder.java similarity index 78% rename from java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogRecorder.java rename to java-core/google-cloud-core/src/main/java/com/google/cloud/testing/LogRecorder.java index af13db959c00..1e1a8a07b962 100644 --- a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogRecorder.java +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/LogRecorder.java @@ -24,14 +24,19 @@ /** * This class records the logs in memory and flush them onto the {@link java.util.logging.Logger} * when {@link #flush()} method is called. + * + *

This component is useful to hold logs for some time and flush them if needed. For example, we + * would want to ignore the trivial startup logs of a sub-process if it is started successfully, but + * in case any error occurs, these logs should be propagated to the user for the debugging + * purpose. */ -class StartupLogRecorder { +class LogRecorder { private final Pattern logLinePattern = Pattern.compile("([A-Z]+):.*"); private final Logger logger; - private final StringBuilder logs; + private StringBuilder logs; - public StartupLogRecorder(Logger logger) { + public LogRecorder(Logger logger) { this.logger = logger; this.logs = new StringBuilder(); } @@ -53,7 +58,10 @@ public void record(String logLine) { /** Flush all the logs to the underlying {@link java.util.logging.Logger}. */ public void flush() { - logger.log(Level.INFO, this.logs.toString()); + if (this.logs.length() > 0) { + logger.log(Level.INFO, this.logs.toString()); + this.logs = new StringBuilder(); + } } private boolean hasLevel(String line) { diff --git a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogRecorderTest.java b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/LogRecorderTest.java similarity index 65% rename from java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogRecorderTest.java rename to java-core/google-cloud-core/src/test/java/com/google/cloud/testing/LogRecorderTest.java index 735028a136bc..d6df12e2781e 100644 --- a/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogRecorderTest.java +++ b/java-core/google-cloud-core/src/test/java/com/google/cloud/testing/LogRecorderTest.java @@ -20,16 +20,17 @@ import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; +import com.google.common.truth.Truth; import java.util.logging.Level; import java.util.logging.Logger; import org.junit.Test; -public class StartupLogRecorderTest { +public class LogRecorderTest { private static final String LOG_LINES = "INFO: log line 1\n" + "log line 2\n" + "FINE: log line 3\n"; private final TestLogger testLogger = new TestLogger(); - private final StartupLogRecorder logRecorder = new StartupLogRecorder(testLogger); + private final LogRecorder logRecorder = new LogRecorder(testLogger); @Test public void shouldAggregateLogs() { @@ -48,9 +49,31 @@ public void shouldAggregateLogs() { + System.lineSeparator()); } + @Test + public void shouldBeEmptyAfterFlushCall() { + for (String logLine : LOG_LINES.split("\n")) { + logRecorder.record(logLine); + } + // first flush and assertion + logRecorder.flush(); + assertThat(testLogger.logs.get(Level.INFO).iterator().next()) + .isEqualTo( + "log line 1" + + System.lineSeparator() + + "log line 2" + + System.lineSeparator() + + "log line 3" + + System.lineSeparator()); + + // second flush and assertion + testLogger.reset(); + logRecorder.flush(); + Truth.assertThat(testLogger.logs.get(Level.INFO)).isEmpty(); + } + private static final class TestLogger extends Logger { - private final Multimap logs = LinkedHashMultimap.create(); + private Multimap logs = LinkedHashMultimap.create(); private TestLogger() { super("text-logger", null); @@ -60,6 +83,10 @@ public void log(Level level, String msg) { logs.put(level, msg); } + public void reset() { + this.logs = LinkedHashMultimap.create(); + } + Multimap getLogs() { return logs; } From 4044d9cd90d3656fb6d37a6e833e6a22f926f21f Mon Sep 17 00:00:00 2001 From: Prateek Date: Tue, 14 Feb 2023 06:30:54 +0530 Subject: [PATCH 6/6] Update java-core/google-cloud-core/src/main/java/com/google/cloud/testing/LogRecorder.java Co-authored-by: Tomo Suzuki --- .../src/main/java/com/google/cloud/testing/LogRecorder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/LogRecorder.java b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/LogRecorder.java index 1e1a8a07b962..7fd463defde6 100644 --- a/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/LogRecorder.java +++ b/java-core/google-cloud-core/src/main/java/com/google/cloud/testing/LogRecorder.java @@ -56,7 +56,7 @@ public void record(String logLine) { this.logs.append(System.getProperty("line.separator")); } - /** Flush all the logs to the underlying {@link java.util.logging.Logger}. */ + /** Flushes all the logs to the underlying {@link java.util.logging.Logger}. */ public void flush() { if (this.logs.length() > 0) { logger.log(Level.INFO, this.logs.toString());