Skip to content

Commit e1db1e8

Browse files
authored
Support for Ansi colors in console logger (#177)
1 parent 3105d39 commit e1db1e8

File tree

7 files changed

+203
-18
lines changed

7 files changed

+203
-18
lines changed

imagetool/src/main/bin/logging.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ com.oracle.weblogic.imagetool.level=INFO
2323
#
2424
java.util.logging.FileHandler.pattern=imagetool.log
2525
java.util.logging.FileHandler.count=1
26-
java.util.logging.SimpleFormatter.format=[%4$-7s] %5$s %n
2726
java.util.logging.FileHandler.level=FINEST
2827
java.util.logging.ConsoleHandler.level=INFO
28+
#java.util.logging.ConsoleHandler.color=false
2929

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (c) 2020, Oracle Corporation and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
package com.oracle.weblogic.imagetool.logging;
5+
6+
public enum AnsiColor {
7+
RESET("reset", "\u001B[0m"),
8+
9+
// basic 8 colors (available on almost all basic terminals)
10+
BLACK("black","\u001B[30m"),
11+
BLUE("blue","\u001B[34m"),
12+
CYAN("cyan","\u001B[36m"),
13+
GREEN("green","\u001B[32m"),
14+
PURPLE("purple","\u001B[35m"),
15+
RED("red","\u001B[31m"),
16+
WHITE("white","\u001B[37m"),
17+
YELLOW("yellow","\u001B[33m"),
18+
19+
// bright or bold versions of the basic 8
20+
BRIGHT_BLACK("brightblack", "\u001b[30;1m"),
21+
BRIGHT_BLUE("brightblue", "\u001b[34;1m"),
22+
BRIGHT_CYAN("brightcyan", "\u001b[36;1m"),
23+
BRIGHT_GREEN("brightgreen", "\u001b[32;1m"),
24+
BRIGHT_PURPLE("brightpurple", "\u001b[35;1m"),
25+
BRIGHT_RED("brightred", "\u001b[31;1m"),
26+
BRIGHT_WHITE("brightwhite", "\u001b[37;1m"),
27+
BRIGHT_YELLOW("brightyellow", "\u001b[33;1m"),
28+
29+
// decorations
30+
BOLD("bold","\u001b[1m"),
31+
UNDERLINE("underline","\u001b[4m");
32+
33+
private static boolean colorEnabled = true;
34+
private String name;
35+
private String value;
36+
37+
AnsiColor(String name, String value) {
38+
this.name = name;
39+
this.value = value;
40+
}
41+
42+
/**
43+
* Disables color logging. These enums will resolve to empty strings.
44+
*/
45+
public static void disable() {
46+
colorEnabled = false;
47+
}
48+
49+
/**
50+
* Convert the string value to a data type enum value.
51+
*
52+
* @param name color name to search for
53+
* @return matching enum for provided name, or null if not found
54+
*/
55+
public static AnsiColor fromValue(String name) {
56+
for (AnsiColor eachVal : AnsiColor.values()) {
57+
if (eachVal.name.equalsIgnoreCase(name)) {
58+
return eachVal;
59+
}
60+
}
61+
return null;
62+
}
63+
64+
@Override
65+
public String toString() {
66+
if (colorEnabled) {
67+
return value;
68+
} else {
69+
return "";
70+
}
71+
}
72+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright (c) 2020, Oracle Corporation and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
package com.oracle.weblogic.imagetool.logging;
5+
6+
import java.io.PrintWriter;
7+
import java.io.StringWriter;
8+
import java.util.logging.Formatter;
9+
import java.util.logging.Level;
10+
import java.util.logging.LogManager;
11+
import java.util.logging.LogRecord;
12+
import java.util.regex.Matcher;
13+
import java.util.regex.Pattern;
14+
15+
/**
16+
* A color-capable formatter for the console that defines a fixed output format.
17+
* Format is "[ LEVEL ] message", with optional throw-ables.
18+
*/
19+
public class ConsoleFormatter extends Formatter {
20+
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
21+
private static final Pattern colorPattern = Pattern.compile("\\[\\[([a-z]+): (.+?)]]");
22+
23+
static {
24+
// if user is redirecting output, do not print color codes
25+
if (System.console() == null) {
26+
AnsiColor.disable();
27+
} else {
28+
// check logging properties to see if the user wants to disable color output
29+
String flag = LogManager.getLogManager().getProperty("java.util.logging.ConsoleHandler.color");
30+
if (flag == null) {
31+
String os = System.getProperty("os.name");
32+
if (os != null && os.toLowerCase().contains("win")) {
33+
// For the Windows OS, default the color logging to disabled
34+
// The Windows OS terminal does not enable color by default
35+
AnsiColor.disable();
36+
}
37+
// The default for non-Windows OS's is color enabled.
38+
} else if (flag.equalsIgnoreCase("false")) {
39+
AnsiColor.disable();
40+
}
41+
}
42+
}
43+
44+
private AnsiColor getMessageLevelColor(Level level) {
45+
if (level.intValue() > 800) {
46+
return AnsiColor.BRIGHT_RED;
47+
} else if (level.intValue() < 800) {
48+
return AnsiColor.BRIGHT_WHITE;
49+
} else {
50+
return AnsiColor.BRIGHT_BLUE;
51+
}
52+
}
53+
54+
private String replaceColorTokens(String text) {
55+
Matcher matcher = colorPattern.matcher(text);
56+
57+
StringBuilder builder = new StringBuilder();
58+
int i = 0;
59+
while (matcher.find()) {
60+
AnsiColor replacement = AnsiColor.fromValue(matcher.group(1));
61+
builder.append(text, i, matcher.start());
62+
if (replacement == null) {
63+
builder.append(matcher.group(0));
64+
} else {
65+
builder.append(replacement);
66+
builder.append(matcher.group(2));
67+
builder.append(AnsiColor.RESET);
68+
}
69+
i = matcher.end();
70+
}
71+
builder.append(text.substring(i));
72+
return builder.toString();
73+
}
74+
75+
/**
76+
* Formats the log record.
77+
*
78+
* @param record the log record
79+
* @return the formatted log record
80+
*/
81+
@Override
82+
public synchronized String format(LogRecord record) {
83+
StringBuilder sb = new StringBuilder();
84+
85+
// Level
86+
sb.append("[")
87+
.append(getMessageLevelColor(record.getLevel()))
88+
.append(String.format("%-7s", record.getLevel().getLocalizedName()))
89+
.append(AnsiColor.RESET)
90+
.append("] ");
91+
92+
// message
93+
sb.append(replaceColorTokens(formatMessage(record)));
94+
95+
// throwable
96+
if (record.getThrown() != null) {
97+
try {
98+
StringWriter sw = new StringWriter();
99+
PrintWriter pw = new PrintWriter(sw);
100+
record.getThrown().printStackTrace(pw);
101+
pw.close();
102+
sb.append(LINE_SEPARATOR)
103+
.append(sw.toString())
104+
.append(LINE_SEPARATOR);
105+
} catch (Exception ex) {
106+
//ignore
107+
}
108+
}
109+
110+
// end of line
111+
sb.append(AnsiColor.RESET)
112+
.append(LINE_SEPARATOR);
113+
return sb.toString();
114+
}
115+
116+
}

imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/FileFormatter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ public class FileFormatter extends Formatter {
1818
private static final String CATALOG_KEY_PATTERN_STRING = "^[A-Z]{3,10}?-[0-9]{3,5}?$";
1919
private static final Pattern CATALOG_KEY_PATTERN = Pattern.compile(CATALOG_KEY_PATTERN_STRING);
2020

21-
//private static final String DATE_FORMAT_STRING = "####<{0,date} {0,time}>";
2221
private static final String DATE_FORMAT_STRING = "####<{0,date,yyyy.MM.dd} {0,time,HH:mm:ss}>";
2322
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
2423

imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/LoggingFacade.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package com.oracle.weblogic.imagetool.logging;
55

6+
import java.util.logging.ConsoleHandler;
67
import java.util.logging.FileHandler;
78
import java.util.logging.Handler;
89
import java.util.logging.Level;
@@ -31,6 +32,9 @@ public LoggingFacade(Logger logger) {
3132
if (handler instanceof FileHandler) {
3233
handler.setFormatter(new FileFormatter());
3334
}
35+
if (handler instanceof ConsoleHandler) {
36+
handler.setFormatter(new ConsoleFormatter());
37+
}
3438
}
3539
}
3640

imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/LoggingFactory.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,7 @@ public static LoggingFacade getLogger(String name) {
4646
*/
4747
public static synchronized LoggingFacade getLogger(String name, String resourceBundleName) {
4848

49-
LoggingFacade lf = facade.get(resourceBundleName);
50-
if (lf == null) {
51-
Logger logger = Logger.getLogger(name, resourceBundleName);
52-
lf = new LoggingFacade(logger);
53-
facade.put(resourceBundleName, lf);
54-
}
55-
56-
return lf;
49+
return facade.computeIfAbsent(resourceBundleName,
50+
k -> new LoggingFacade(Logger.getLogger(name, resourceBundleName)));
5751
}
5852
}

imagetool/src/main/resources/ImageTool.properties

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ IMG-0006=No patch conflicts detected
88
IMG-0007={0} is not a directory
99
IMG-0008=Updating OPatch in final image from version {0} to version {1}
1010
IMG-0009=skipping patch conflict check, no support credentials provided
11-
IMG-0010=Oracle Home will be set to {0}
11+
IMG-0010=Oracle Home will be set to [[cyan: {0}]]
1212
IMG-0011=Installer with key="{0}" is not in the local cache, please download the installer and add it to the cache with "imagetool cache addInstaller ..."
1313
IMG-0012=Validating patches
1414
IMG-0013=Error parsing additional build commands file {0}
1515
IMG-0014=Additional build command section, {0}, was not one of the valid sections: {1}
1616
IMG-0015=Additional build commands, adding {0} lines to section [{1}]
17-
IMG-0016=Image Tool build ID: {0}
18-
IMG-0017=Using patch {0} from cache: {1}
19-
IMG-0018=Downloading patch {0}...
17+
IMG-0016=Image Tool build ID: [[cyan: {0}]]
18+
IMG-0017=Using patch [[cyan: {0}]] from cache: {1}
19+
IMG-0018=Downloading patch [[cyan: {0}]]...
2020
IMG-0019=Finding patch number for latest PSU...
2121
IMG-0020=Latest PSU patch number is {0}
2222
IMG-0021=ORACLE_HOME value must be the same in both new and old image in order to rebase the domain
@@ -37,7 +37,7 @@ IMG-0035=additionalBuildFile could not be copied: {0}
3737
IMG-0036=Unable to find installer inventory file: {0}
3838
IMG-0037=Failed to find patch {0} for version {1}
3939
IMG-0038=In image {0}, the Oracle Home already exists at location: {1}
40-
IMG-0039=Using middleware installers ({0}) version {1}
40+
IMG-0039=Using middleware installers ([[green: {0}]]) version [[cyan: {1}]]
4141
IMG-0040=When providing custom response files, a response file must be provided for each of the installers {0}. Found {1}, expected {2}.
4242
IMG-0041=Using provided installer response file: {0} for {1} install
4343
IMG-0042={0} was not found or is not a regular file
@@ -51,14 +51,14 @@ IMG-0049=Could not find value for --path, file not found: {0}
5151
IMG-0050=Successfully added to cache. {0}={1}
5252
IMG-0051=Deleted entry {0}={1}
5353
IMG-0052=Nothing to delete for key: {0}
54-
IMG-0053=Build successful. Build time={0}s. Image tag={1}
55-
IMG-0054=Dry run complete. No image created.
54+
IMG-0053=[[brightgreen: Build successful.]] Build time={0}s. Image tag={1}
55+
IMG-0054=[[brightgreen: Dry run complete.]] No image created.
5656
IMG-0055=RunRCU can only be used when creating a new domain. Please try --wdtOperation=CREATE or removing --wdtRunRCU.
5757
IMG-0056=Patch {0} is not in the cache store and you have not provided Oracle Support credentials. Please provide --user with one of the password options or populate the cache store manually.
5858
IMG-0057=Could not locate patch ID {0} from Oracle
5959
IMG-0058=A patch cannot have an empty/null bug number: {0}
6060
IMG-0059=Invalid response from Oracle ARU, unable to locate download URL for {0}
61-
IMG-0060=Adding patch {0} to cache, path={1}
61+
IMG-0060=Adding patch [[cyan: {0}]] to cache, path={1}
6262
IMG-0061=Could not find key {0} in the cache for patch {1}
6363
IMG-0062=An OPatch version could not be found in the cache store and you have not provided Oracle Support credentials. Please provide --user with one of the password options or populate the cache store manually.
6464
IMG-0063=Found matching patch for {0}, but version is {1}. Check OPatch messages during build phase for issues with this patch.

0 commit comments

Comments
 (0)