diff --git a/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java b/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java index 90fbb764d5..4f8973b211 100644 --- a/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java +++ b/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/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java b/google-cloud-core/src/main/java/com/google/cloud/testing/StartupLogAggregator.java new file mode 100644 index 0000000000..d290b99d64 --- /dev/null +++ b/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/google-cloud-core/src/test/java/com/google/cloud/testing/BaseEmulatorHelperTest.java b/google-cloud-core/src/test/java/com/google/cloud/testing/BaseEmulatorHelperTest.java index b002a6198b..2c6d7495be 100644 --- a/google-cloud-core/src/test/java/com/google/cloud/testing/BaseEmulatorHelperTest.java +++ b/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/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java b/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java index 56b406f963..0e6b995fd1 100644 --- a/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java +++ b/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/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java b/google-cloud-core/src/test/java/com/google/cloud/testing/StartupLogAggregatorTest.java new file mode 100644 index 0000000000..f626a258cc --- /dev/null +++ b/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