From f4d28ba0d2cd58e7c6a3d33a11438c5226a52997 Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Fri, 7 Dec 2018 16:35:43 -0500 Subject: [PATCH 01/12] Adds SingleThreadedLogger to be able to keep a footer for where progress indication will go. --- .../plugins/common/SingleThreadedLogger.java | 110 ++++++++++++++++++ .../common/SingleThreadedLoggerTest.java | 83 +++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java create mode 100644 jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java new file mode 100644 index 0000000000..d1ceadaf40 --- /dev/null +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java @@ -0,0 +1,110 @@ +/* + * Copyright 2018 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.tools.jib.plugins.common; + +import com.google.common.util.concurrent.Futures; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Consumer; + +/** + * Keeps all log messages in a sequential, deterministic order along with an additional footer that + * always appears below log messages. + */ +class SingleThreadedLogger { + + /** ANSI escape sequence for moving the cursor up one line. */ + private static final String CURSOR_UP_SEQUENCE = "\033[1A"; + + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + + private final Consumer plainLogger; + + private String footer = ""; + private int footerLineCount = 0; + + SingleThreadedLogger(Consumer plainLogger) { + this.plainLogger = plainLogger; + } + + /** + * Runs {@code messageLogger} asynchronously. + * + * @param messageLogger the {@link Runnable} intended to synchronously log a message to the + * console + * @return a {@link Future} to track completion + */ + public Future log(Runnable messageLogger) { + return log(messageLogger, footer, footerLineCount); + } + + /** + * Sets the footer asynchronously. + * + * @param footer the footer + * @param lineCount the number of lines in the footer + * @return a {@link Future} to track completion + */ + public Future setFooter(String footer, int lineCount) { + if (footer.equals(this.footer)) { + return Futures.immediateFuture(null); + } + + Future future = log(() -> {}, this.footer, this.footerLineCount); + + this.footer = footer; + this.footerLineCount = lineCount; + + return future; + } + + private Future log(Runnable messageLogger, String previousFooter, int previousLineCount) { + return executorService.submit( + () -> { + StringBuilder plainLogBuilder = new StringBuilder(); + + // Moves the cursor up to the start of the footer. + // TODO: Optimize to single init. + for (int i = 0; i < previousLineCount; i++) { + // Moves cursor up. + plainLogBuilder.append(CURSOR_UP_SEQUENCE); + } + + // Overwrites the footer with whitespace. + // TODO: Optimize to single init. + for (int i = 0; i < previousFooter.length(); i++) { + plainLogBuilder.append(previousFooter.charAt(i) == '\n' ? '\n' : ' '); + } + // plainLogBuilder.append('\n'); + + // Moves the cursor up again. + // TODO: Optimize to single init. + for (int i = 0; i < previousLineCount; i++) { + // Moves cursor up. + plainLogBuilder.append(CURSOR_UP_SEQUENCE); + } + + // Writes out logMessage and footer. + plainLogger.accept(plainLogBuilder.toString()); + messageLogger.run(); + plainLogger.accept(footer); + + return null; + }); + } +} diff --git a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java new file mode 100644 index 0000000000..03e981947e --- /dev/null +++ b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2018 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.tools.jib.plugins.common; + +import java.time.Duration; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.Assert; +import org.junit.Test; + +/** Tests for {@link SingleThreadedLogger}. */ +public class SingleThreadedLoggerTest { + + private static final Duration FUTURE_TIMEOUT = Duration.ofSeconds(1); + + private final StringBuilder logBuilder = new StringBuilder(); + private final SingleThreadedLogger testSingleThreadedLogger = + new SingleThreadedLogger(logBuilder::append); + + @Test + public void testLog_noFooter() throws InterruptedException, ExecutionException, TimeoutException { + testSingleThreadedLogger + .log(() -> logBuilder.append("message\n")) + .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + + Assert.assertEquals("message\n", logBuilder.toString()); + } + + @Test + public void testLog_sameFooter() + throws InterruptedException, ExecutionException, TimeoutException { + testSingleThreadedLogger + .setFooter("footer", 1) + .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + testSingleThreadedLogger + .log(() -> logBuilder.append("message\n")) + .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + testSingleThreadedLogger + .log(() -> logBuilder.append("another message\n")) + .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + + Assert.assertEquals( + "footer\033[1A \033[1Amessage\nfooter\033[1A \033[1Aanother message\nfooter", + logBuilder.toString()); + } + + @Test + public void testLog_changingFooter() + throws InterruptedException, ExecutionException, TimeoutException { + testSingleThreadedLogger + .setFooter("footer", 1) + .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + testSingleThreadedLogger + .log(() -> logBuilder.append("message\n")) + .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + testSingleThreadedLogger + .setFooter("two line\nfooter", 2) + .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + testSingleThreadedLogger + .log(() -> logBuilder.append("another message\n")) + .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + + Assert.assertEquals( + "footer\033[1A \033[1Amessage\nfooter\033[1A \033[1Atwo line\nfooter" + + "\033[1A\033[1A \n \033[1A\033[1Aanother message\ntwo line\nfooter", + logBuilder.toString()); + } +} From b66c72f2921852b17cb8614d81476b7234fac902 Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Mon, 10 Dec 2018 10:16:10 -0500 Subject: [PATCH 02/12] small changes --- .../cloud/tools/jib/plugins/common/SingleThreadedLogger.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java index d1ceadaf40..06d7d40bcc 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java @@ -54,7 +54,7 @@ public Future log(Runnable messageLogger) { } /** - * Sets the footer asynchronously. + * Sets the footer asynchronously. This will replace the previously-printed footer with the new {@code footer}. * * @param footer the footer * @param lineCount the number of lines in the footer @@ -90,7 +90,6 @@ private Future log(Runnable messageLogger, String previousFooter, int prev for (int i = 0; i < previousFooter.length(); i++) { plainLogBuilder.append(previousFooter.charAt(i) == '\n' ? '\n' : ' '); } - // plainLogBuilder.append('\n'); // Moves the cursor up again. // TODO: Optimize to single init. From 1935d9e3751e63d529a2311b954f0975beda56da Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Mon, 10 Dec 2018 11:58:30 -0500 Subject: [PATCH 03/12] format --- .../cloud/tools/jib/plugins/common/SingleThreadedLogger.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java index 06d7d40bcc..dbec3fb126 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java @@ -54,7 +54,8 @@ public Future log(Runnable messageLogger) { } /** - * Sets the footer asynchronously. This will replace the previously-printed footer with the new {@code footer}. + * Sets the footer asynchronously. This will replace the previously-printed footer with the new + * {@code footer}. * * @param footer the footer * @param lineCount the number of lines in the footer From 7c7efb1611fab01b4821c2a7e9773314cb3db3ae Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Mon, 10 Dec 2018 14:17:13 -0500 Subject: [PATCH 04/12] Uses ED. --- .../plugins/common/SingleThreadedLogger.java | 23 +++++++------------ .../common/SingleThreadedLoggerTest.java | 8 +++---- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java index dbec3fb126..50f2725ddc 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java @@ -31,6 +31,9 @@ class SingleThreadedLogger { /** ANSI escape sequence for moving the cursor up one line. */ private static final String CURSOR_UP_SEQUENCE = "\033[1A"; + /** ANSI escape sequence for erasing to end of display. */ + private static final String ERASE_DISPLAY_BELOW = "\033[0J"; + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); private final Consumer plainLogger; @@ -50,7 +53,7 @@ class SingleThreadedLogger { * @return a {@link Future} to track completion */ public Future log(Runnable messageLogger) { - return log(messageLogger, footer, footerLineCount); + return log(messageLogger, footerLineCount); } /** @@ -66,7 +69,7 @@ public Future setFooter(String footer, int lineCount) { return Futures.immediateFuture(null); } - Future future = log(() -> {}, this.footer, this.footerLineCount); + Future future = log(() -> {}, this.footerLineCount); this.footer = footer; this.footerLineCount = lineCount; @@ -74,7 +77,7 @@ public Future setFooter(String footer, int lineCount) { return future; } - private Future log(Runnable messageLogger, String previousFooter, int previousLineCount) { + private Future log(Runnable messageLogger, int previousLineCount) { return executorService.submit( () -> { StringBuilder plainLogBuilder = new StringBuilder(); @@ -86,18 +89,8 @@ private Future log(Runnable messageLogger, String previousFooter, int prev plainLogBuilder.append(CURSOR_UP_SEQUENCE); } - // Overwrites the footer with whitespace. - // TODO: Optimize to single init. - for (int i = 0; i < previousFooter.length(); i++) { - plainLogBuilder.append(previousFooter.charAt(i) == '\n' ? '\n' : ' '); - } - - // Moves the cursor up again. - // TODO: Optimize to single init. - for (int i = 0; i < previousLineCount; i++) { - // Moves cursor up. - plainLogBuilder.append(CURSOR_UP_SEQUENCE); - } + // Erases everything below cursor. + plainLogBuilder.append(ERASE_DISPLAY_BELOW); // Writes out logMessage and footer. plainLogger.accept(plainLogBuilder.toString()); diff --git a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java index 03e981947e..c77fab8670 100644 --- a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java +++ b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java @@ -38,7 +38,7 @@ public void testLog_noFooter() throws InterruptedException, ExecutionException, .log(() -> logBuilder.append("message\n")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - Assert.assertEquals("message\n", logBuilder.toString()); + Assert.assertEquals("\033[0Jmessage\n", logBuilder.toString()); } @Test @@ -55,7 +55,7 @@ public void testLog_sameFooter() .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); Assert.assertEquals( - "footer\033[1A \033[1Amessage\nfooter\033[1A \033[1Aanother message\nfooter", + "\033[0Jfooter\033[1A\033[0Jmessage\nfooter\033[1A\033[0Janother message\nfooter", logBuilder.toString()); } @@ -76,8 +76,8 @@ public void testLog_changingFooter() .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); Assert.assertEquals( - "footer\033[1A \033[1Amessage\nfooter\033[1A \033[1Atwo line\nfooter" - + "\033[1A\033[1A \n \033[1A\033[1Aanother message\ntwo line\nfooter", + "\033[0Jfooter\033[1A\033[0Jmessage\nfooter\033[1A\033[0Jtwo line\nfooter" + + "\033[1A\033[1A\033[0Janother message\ntwo line\nfooter", logBuilder.toString()); } } From 22fa1d81f3522e55a966ba2321fb8a20f5c13066 Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Mon, 10 Dec 2018 14:46:53 -0500 Subject: [PATCH 05/12] List of strings for footer. --- .../plugins/common/SingleThreadedLogger.java | 26 +++++++++---------- .../common/SingleThreadedLoggerTest.java | 8 +++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java index 50f2725ddc..044d4584d5 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java @@ -17,6 +17,8 @@ package com.google.cloud.tools.jib.plugins.common; import com.google.common.util.concurrent.Futures; +import java.util.Collections; +import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -38,8 +40,7 @@ class SingleThreadedLogger { private final Consumer plainLogger; - private String footer = ""; - private int footerLineCount = 0; + private List footerLines = Collections.emptyList(); SingleThreadedLogger(Consumer plainLogger) { this.plainLogger = plainLogger; @@ -53,28 +54,25 @@ class SingleThreadedLogger { * @return a {@link Future} to track completion */ public Future log(Runnable messageLogger) { - return log(messageLogger, footerLineCount); + return log(messageLogger, footerLines.size()); } /** * Sets the footer asynchronously. This will replace the previously-printed footer with the new - * {@code footer}. + * {@code footerLines}. * - * @param footer the footer - * @param lineCount the number of lines in the footer + * @param footerLines the footer, with each line as an element (no newline at end) * @return a {@link Future} to track completion */ - public Future setFooter(String footer, int lineCount) { - if (footer.equals(this.footer)) { + public Future setFooter(List footerLines) { + if (footerLines.equals(this.footerLines)) { return Futures.immediateFuture(null); } - Future future = log(() -> {}, this.footerLineCount); + int previousLineCount = this.footerLines.size(); + this.footerLines = footerLines; - this.footer = footer; - this.footerLineCount = lineCount; - - return future; + return log(() -> {}, previousLineCount); } private Future log(Runnable messageLogger, int previousLineCount) { @@ -95,7 +93,7 @@ private Future log(Runnable messageLogger, int previousLineCount) { // Writes out logMessage and footer. plainLogger.accept(plainLogBuilder.toString()); messageLogger.run(); - plainLogger.accept(footer); + plainLogger.accept(String.join("\n", footerLines)); return null; }); diff --git a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java index c77fab8670..18cb4dc27c 100644 --- a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java +++ b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java @@ -17,6 +17,8 @@ package com.google.cloud.tools.jib.plugins.common; import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -45,7 +47,7 @@ public void testLog_noFooter() throws InterruptedException, ExecutionException, public void testLog_sameFooter() throws InterruptedException, ExecutionException, TimeoutException { testSingleThreadedLogger - .setFooter("footer", 1) + .setFooter(Collections.singletonList("footer")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); testSingleThreadedLogger .log(() -> logBuilder.append("message\n")) @@ -63,13 +65,13 @@ public void testLog_sameFooter() public void testLog_changingFooter() throws InterruptedException, ExecutionException, TimeoutException { testSingleThreadedLogger - .setFooter("footer", 1) + .setFooter(Collections.singletonList("footer")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); testSingleThreadedLogger .log(() -> logBuilder.append("message\n")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); testSingleThreadedLogger - .setFooter("two line\nfooter", 2) + .setFooter(Arrays.asList("two line", "footer")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); testSingleThreadedLogger .log(() -> logBuilder.append("another message\n")) From a1fac107320bb24054ac8a3c62268d4adaf2e0fc Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Mon, 10 Dec 2018 18:21:52 -0500 Subject: [PATCH 06/12] Fixes concurrency mistake.: --- .../jib/plugins/common/SingleThreadedLogger.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java index 044d4584d5..493171e87e 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java @@ -54,7 +54,7 @@ class SingleThreadedLogger { * @return a {@link Future} to track completion */ public Future log(Runnable messageLogger) { - return log(messageLogger, footerLines.size()); + return log(messageLogger, footerLines); } /** @@ -69,20 +69,17 @@ public Future setFooter(List footerLines) { return Futures.immediateFuture(null); } - int previousLineCount = this.footerLines.size(); - this.footerLines = footerLines; - - return log(() -> {}, previousLineCount); + return log(() -> {}, footerLines); } - private Future log(Runnable messageLogger, int previousLineCount) { + private Future log(Runnable messageLogger, List newFooterLines) { return executorService.submit( () -> { StringBuilder plainLogBuilder = new StringBuilder(); // Moves the cursor up to the start of the footer. // TODO: Optimize to single init. - for (int i = 0; i < previousLineCount; i++) { + for (int i = 0; i < this.footerLines.size(); i++) { // Moves cursor up. plainLogBuilder.append(CURSOR_UP_SEQUENCE); } @@ -93,7 +90,9 @@ private Future log(Runnable messageLogger, int previousLineCount) { // Writes out logMessage and footer. plainLogger.accept(plainLogBuilder.toString()); messageLogger.run(); - plainLogger.accept(String.join("\n", footerLines)); + plainLogger.accept(String.join("\n", newFooterLines)); + + this.footerLines = newFooterLines; return null; }); From d03d4cd0d44f8d2dc053090ba2fa69bc61378602 Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Tue, 11 Dec 2018 11:08:30 -0500 Subject: [PATCH 07/12] rename --- ...dLogger.java => AnsiLoggerWithFooter.java} | 6 ++--- ...est.java => AnsiLoggerWithFooterTest.java} | 24 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) rename jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/{SingleThreadedLogger.java => AnsiLoggerWithFooter.java} (94%) rename jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/{SingleThreadedLoggerTest.java => AnsiLoggerWithFooterTest.java} (86%) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java similarity index 94% rename from jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java rename to jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java index 493171e87e..e889c46b8a 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLogger.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java @@ -26,9 +26,9 @@ /** * Keeps all log messages in a sequential, deterministic order along with an additional footer that - * always appears below log messages. + * always appears below log messages. This is intended to log both the messages and the footer to the same console. */ -class SingleThreadedLogger { +class AnsiLoggerWithFooter { /** ANSI escape sequence for moving the cursor up one line. */ private static final String CURSOR_UP_SEQUENCE = "\033[1A"; @@ -42,7 +42,7 @@ class SingleThreadedLogger { private List footerLines = Collections.emptyList(); - SingleThreadedLogger(Consumer plainLogger) { + AnsiLoggerWithFooter(Consumer plainLogger) { this.plainLogger = plainLogger; } diff --git a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooterTest.java similarity index 86% rename from jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java rename to jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooterTest.java index 18cb4dc27c..3438d5a327 100644 --- a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/SingleThreadedLoggerTest.java +++ b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooterTest.java @@ -25,18 +25,18 @@ import org.junit.Assert; import org.junit.Test; -/** Tests for {@link SingleThreadedLogger}. */ -public class SingleThreadedLoggerTest { +/** Tests for {@link AnsiLoggerWithFooter}. */ +public class AnsiLoggerWithFooterTest { private static final Duration FUTURE_TIMEOUT = Duration.ofSeconds(1); private final StringBuilder logBuilder = new StringBuilder(); - private final SingleThreadedLogger testSingleThreadedLogger = - new SingleThreadedLogger(logBuilder::append); + private final AnsiLoggerWithFooter testAnsiLoggerWithFooter = + new AnsiLoggerWithFooter(logBuilder::append); @Test public void testLog_noFooter() throws InterruptedException, ExecutionException, TimeoutException { - testSingleThreadedLogger + testAnsiLoggerWithFooter .log(() -> logBuilder.append("message\n")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); @@ -46,13 +46,13 @@ public void testLog_noFooter() throws InterruptedException, ExecutionException, @Test public void testLog_sameFooter() throws InterruptedException, ExecutionException, TimeoutException { - testSingleThreadedLogger + testAnsiLoggerWithFooter .setFooter(Collections.singletonList("footer")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - testSingleThreadedLogger + testAnsiLoggerWithFooter .log(() -> logBuilder.append("message\n")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - testSingleThreadedLogger + testAnsiLoggerWithFooter .log(() -> logBuilder.append("another message\n")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); @@ -64,16 +64,16 @@ public void testLog_sameFooter() @Test public void testLog_changingFooter() throws InterruptedException, ExecutionException, TimeoutException { - testSingleThreadedLogger + testAnsiLoggerWithFooter .setFooter(Collections.singletonList("footer")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - testSingleThreadedLogger + testAnsiLoggerWithFooter .log(() -> logBuilder.append("message\n")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - testSingleThreadedLogger + testAnsiLoggerWithFooter .setFooter(Arrays.asList("two line", "footer")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - testSingleThreadedLogger + testAnsiLoggerWithFooter .log(() -> logBuilder.append("another message\n")) .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); From e3ea9b1fe3b7293de29134a38f6bc2c2aacb9ffe Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Tue, 11 Dec 2018 11:08:55 -0500 Subject: [PATCH 08/12] format --- .../cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java index e889c46b8a..6878b58362 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java @@ -26,7 +26,8 @@ /** * Keeps all log messages in a sequential, deterministic order along with an additional footer that - * always appears below log messages. This is intended to log both the messages and the footer to the same console. + * always appears below log messages. This is intended to log both the messages and the footer to + * the same console. */ class AnsiLoggerWithFooter { From 464f663ba215f4f05c200b558ea0a1558e741fc1 Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Tue, 11 Dec 2018 18:32:21 -0500 Subject: [PATCH 09/12] Adds #shutDown. --- .../plugins/common/AnsiLoggerWithFooter.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java index 6878b58362..36da1ddf46 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java @@ -17,17 +17,21 @@ package com.google.cloud.tools.jib.plugins.common; import com.google.common.util.concurrent.Futures; +import java.time.Duration; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; /** * Keeps all log messages in a sequential, deterministic order along with an additional footer that * always appears below log messages. This is intended to log both the messages and the footer to * the same console. + * + * Make sure to call {@link #shutDown} when finished. */ class AnsiLoggerWithFooter { @@ -37,6 +41,8 @@ class AnsiLoggerWithFooter { /** ANSI escape sequence for erasing to end of display. */ private static final String ERASE_DISPLAY_BELOW = "\033[0J"; + private static final Duration EXECUTOR_SHUTDOWN_WAIT = Duration.ofSeconds(1); + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); private final Consumer plainLogger; @@ -47,6 +53,18 @@ class AnsiLoggerWithFooter { this.plainLogger = plainLogger; } + /** Shuts down the {@link #executorService}. */ + public void shutDown() { + executorService.shutdown(); + try { + if (!executorService.awaitTermination(EXECUTOR_SHUTDOWN_WAIT.getSeconds(), TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + /** * Runs {@code messageLogger} asynchronously. * From 3f58dccb5ec03f686cf05726c0521e20f9c80146 Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Tue, 11 Dec 2018 18:32:47 -0500 Subject: [PATCH 10/12] format --- .../cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java index 36da1ddf46..46477781ce 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java @@ -31,7 +31,7 @@ * always appears below log messages. This is intended to log both the messages and the footer to * the same console. * - * Make sure to call {@link #shutDown} when finished. + *

Make sure to call {@link #shutDown} when finished. */ class AnsiLoggerWithFooter { @@ -57,7 +57,8 @@ class AnsiLoggerWithFooter { public void shutDown() { executorService.shutdown(); try { - if (!executorService.awaitTermination(EXECUTOR_SHUTDOWN_WAIT.getSeconds(), TimeUnit.SECONDS)) { + if (!executorService.awaitTermination( + EXECUTOR_SHUTDOWN_WAIT.getSeconds(), TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException ex) { From 72298647c2cdc5c1662b515745bd528f531d5bd9 Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Tue, 11 Dec 2018 18:48:23 -0500 Subject: [PATCH 11/12] footer in bold --- .../plugins/common/AnsiLoggerWithFooter.java | 19 ++++++++++++++- .../common/AnsiLoggerWithFooterTest.java | 23 ++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java index 46477781ce..f9ed9ee284 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java @@ -41,6 +41,12 @@ class AnsiLoggerWithFooter { /** ANSI escape sequence for erasing to end of display. */ private static final String ERASE_DISPLAY_BELOW = "\033[0J"; + /** ANSI escape sequence for setting all further characters to bold. */ + private static final String BOLD = "\033[1m"; + + /** ANSI escape sequence for setting all further characters to not bold. */ + private static final String UNBOLD = "\033[0m"; + private static final Duration EXECUTOR_SHUTDOWN_WAIT = Duration.ofSeconds(1); private final ExecutorService executorService = Executors.newSingleThreadExecutor(); @@ -81,6 +87,8 @@ public Future log(Runnable messageLogger) { * Sets the footer asynchronously. This will replace the previously-printed footer with the new * {@code footerLines}. * + *

The footer is printed in bold. + * * @param footerLines the footer, with each line as an element (no newline at end) * @return a {@link Future} to track completion */ @@ -110,7 +118,16 @@ private Future log(Runnable messageLogger, List newFooterLines) { // Writes out logMessage and footer. plainLogger.accept(plainLogBuilder.toString()); messageLogger.run(); - plainLogger.accept(String.join("\n", newFooterLines)); + + if (newFooterLines.size() > 0) { + StringBuilder footerBuilder = new StringBuilder(); + for (String newFooterLine : newFooterLines) { + footerBuilder.append(BOLD).append(newFooterLine).append(UNBOLD).append('\n'); + } + // Removes last newline. + footerBuilder.setLength(footerBuilder.length() - 1); + plainLogger.accept(footerBuilder.toString()); + } this.footerLines = newFooterLines; diff --git a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooterTest.java b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooterTest.java index 3438d5a327..924f947611 100644 --- a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooterTest.java +++ b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooterTest.java @@ -57,7 +57,14 @@ public void testLog_sameFooter() .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); Assert.assertEquals( - "\033[0Jfooter\033[1A\033[0Jmessage\nfooter\033[1A\033[0Janother message\nfooter", + "\033[0J" + + "\033[1mfooter\033[0m" + + "\033[1A\033[0J" + + "message\n" + + "\033[1mfooter\033[0m" + + "\033[1A\033[0J" + + "another message\n" + + "\033[1mfooter\033[0m", logBuilder.toString()); } @@ -78,8 +85,18 @@ public void testLog_changingFooter() .get(FUTURE_TIMEOUT.getSeconds(), TimeUnit.SECONDS); Assert.assertEquals( - "\033[0Jfooter\033[1A\033[0Jmessage\nfooter\033[1A\033[0Jtwo line\nfooter" - + "\033[1A\033[1A\033[0Janother message\ntwo line\nfooter", + "\033[0J" + + "\033[1mfooter\033[0m" + + "\033[1A\033[0J" + + "message\n" + + "\033[1mfooter\033[0m" + + "\033[1A\033[0J" + + "\033[1mtwo line\033[0m\n" + + "\033[1mfooter\033[0m" + + "\033[1A\033[1A\033[0J" + + "another message\n" + + "\033[1mtwo line\033[0m\n" + + "\033[1mfooter\033[0m", logBuilder.toString()); } } From 6892ddcc16836b6c37d313d6970d9f9f1349146c Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Wed, 12 Dec 2018 11:08:11 -0500 Subject: [PATCH 12/12] Adds awaitTermination. --- .../cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java index f9ed9ee284..35c448843d 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/AnsiLoggerWithFooter.java @@ -62,6 +62,10 @@ class AnsiLoggerWithFooter { /** Shuts down the {@link #executorService}. */ public void shutDown() { executorService.shutdown(); + } + + /** Waits for the {@link #executorService} to terminate. */ + public void awaitTermination() { try { if (!executorService.awaitTermination( EXECUTOR_SHUTDOWN_WAIT.getSeconds(), TimeUnit.SECONDS)) {