1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.pattern;
18
19 import java.util.Arrays;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.Map;
24
25 import org.apache.logging.log4j.Level;
26 import org.apache.logging.log4j.core.LogEvent;
27 import org.apache.logging.log4j.core.config.Configuration;
28 import org.apache.logging.log4j.core.config.plugins.Plugin;
29 import org.apache.logging.log4j.core.layout.PatternLayout;
30 import org.apache.logging.log4j.util.Strings;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 @Plugin(name = "highlight", category = PatternConverter.CATEGORY)
70 @ConverterKeys({ "highlight" })
71 public final class HighlightConverter extends LogEventPatternConverter implements AnsiConverter {
72
73 private static final Map<Level, String> DEFAULT_STYLES = new HashMap<>();
74
75 private static final Map<Level, String> LOGBACK_STYLES = new HashMap<>();
76
77 private static final String STYLE_KEY = "STYLE";
78
79 private static final String STYLE_KEY_DEFAULT = "DEFAULT";
80
81 private static final String STYLE_KEY_LOGBACK = "LOGBACK";
82
83 private static final Map<String, Map<Level, String>> STYLES = new HashMap<>();
84
85 static {
86
87 DEFAULT_STYLES.put(Level.FATAL, AnsiEscape.createSequence("BRIGHT", "RED"));
88 DEFAULT_STYLES.put(Level.ERROR, AnsiEscape.createSequence("BRIGHT", "RED"));
89 DEFAULT_STYLES.put(Level.WARN, AnsiEscape.createSequence("YELLOW"));
90 DEFAULT_STYLES.put(Level.INFO, AnsiEscape.createSequence("GREEN"));
91 DEFAULT_STYLES.put(Level.DEBUG, AnsiEscape.createSequence("CYAN"));
92 DEFAULT_STYLES.put(Level.TRACE, AnsiEscape.createSequence("BLACK"));
93
94 LOGBACK_STYLES.put(Level.FATAL, AnsiEscape.createSequence("BLINK", "BRIGHT", "RED"));
95 LOGBACK_STYLES.put(Level.ERROR, AnsiEscape.createSequence("BRIGHT", "RED"));
96 LOGBACK_STYLES.put(Level.WARN, AnsiEscape.createSequence("RED"));
97 LOGBACK_STYLES.put(Level.INFO, AnsiEscape.createSequence("BLUE"));
98 LOGBACK_STYLES.put(Level.DEBUG, AnsiEscape.createSequence((String[]) null));
99 LOGBACK_STYLES.put(Level.TRACE, AnsiEscape.createSequence((String[]) null));
100
101 STYLES.put(STYLE_KEY_DEFAULT, DEFAULT_STYLES);
102 STYLES.put(STYLE_KEY_LOGBACK, LOGBACK_STYLES);
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 private static Map<Level, String> createLevelStyleMap(final String[] options) {
134 if (options.length < 2) {
135 return DEFAULT_STYLES;
136 }
137
138 final String string = options[1].replaceAll(PatternParser.NO_CONSOLE_NO_ANSI + "=(true|false)", Strings.EMPTY);
139
140 final Map<String, String> styles = AnsiEscape.createMap(string, new String[] {STYLE_KEY});
141 final Map<Level, String> levelStyles = new HashMap<>(DEFAULT_STYLES);
142 for (final Map.Entry<String, String> entry : styles.entrySet()) {
143 final String key = entry.getKey().toUpperCase(Locale.ENGLISH);
144 final String value = entry.getValue();
145 if (STYLE_KEY.equalsIgnoreCase(key)) {
146 final Map<Level, String> enumMap = STYLES.get(value.toUpperCase(Locale.ENGLISH));
147 if (enumMap == null) {
148 LOGGER.error("Unknown level style: " + value + ". Use one of " +
149 Arrays.toString(STYLES.keySet().toArray()));
150 } else {
151 levelStyles.putAll(enumMap);
152 }
153 } else {
154 final Level level = Level.toLevel(key);
155 if (level == null) {
156 LOGGER.error("Unknown level name: " + key + ". Use one of " +
157 Arrays.toString(DEFAULT_STYLES.keySet().toArray()));
158 } else {
159 levelStyles.put(level, value);
160 }
161 }
162 }
163 return levelStyles;
164 }
165
166
167
168
169
170
171
172
173
174 public static HighlightConverter newInstance(final Configuration config, final String[] options) {
175 if (options.length < 1) {
176 LOGGER.error("Incorrect number of options on style. Expected at least 1, received " + options.length);
177 return null;
178 }
179 if (options[0] == null) {
180 LOGGER.error("No pattern supplied on style");
181 return null;
182 }
183 final PatternParser parser = PatternLayout.createPatternParser(config);
184 final List<PatternFormatter> formatters = parser.parse(options[0]);
185 final boolean noConsoleNoAnsi = Arrays.toString(options).contains(PatternParser.NO_CONSOLE_NO_ANSI + "=true");
186 final boolean hideAnsi = noConsoleNoAnsi && System.console() == null;
187 return new HighlightConverter(formatters, createLevelStyleMap(options), hideAnsi);
188 }
189
190 private final Map<Level, String> levelStyles;
191
192 private final List<PatternFormatter> patternFormatters;
193
194 private final boolean noAnsi;
195
196
197
198
199
200
201
202
203
204 private HighlightConverter(final List<PatternFormatter> patternFormatters, final Map<Level, String> levelStyles, final boolean noAnsi) {
205 super("style", "style");
206 this.patternFormatters = patternFormatters;
207 this.levelStyles = levelStyles;
208 this.noAnsi = noAnsi;
209 }
210
211
212
213
214 @Override
215 public void format(final LogEvent event, final StringBuilder toAppendTo) {
216 final StringBuilder buf = new StringBuilder();
217 for (final PatternFormatter formatter : patternFormatters) {
218 formatter.format(event, buf);
219 }
220
221 if (buf.length() > 0) {
222 if (noAnsi) {
223 toAppendTo.append(buf.toString());
224 } else {
225 toAppendTo.append(levelStyles.get(event.getLevel())).append(buf.toString()).
226 append(AnsiEscape.getDefaultStyle());
227 }
228 }
229 }
230
231 @Override
232 public boolean handlesThrowable() {
233 for (final PatternFormatter formatter : patternFormatters) {
234 if (formatter .handlesThrowable()) {
235 return true;
236 }
237 }
238 return false;
239 }
240 }