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.PerformanceSensitive;
31 import org.apache.logging.log4j.util.Strings;
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
70
71
72
73
74
75
76
77
78 @Plugin(name = "highlight", category = PatternConverter.CATEGORY)
79 @ConverterKeys({ "highlight" })
80 @PerformanceSensitive("allocation")
81 public final class HighlightConverter extends LogEventPatternConverter implements AnsiConverter {
82
83 private static final Map<Level, String> DEFAULT_STYLES = new HashMap<>();
84
85 private static final Map<Level, String> LOGBACK_STYLES = new HashMap<>();
86
87 private static final String STYLE_KEY = "STYLE";
88
89 private static final String STYLE_KEY_DEFAULT = "DEFAULT";
90
91 private static final String STYLE_KEY_LOGBACK = "LOGBACK";
92
93 private static final Map<String, Map<Level, String>> STYLES = new HashMap<>();
94
95 static {
96
97 DEFAULT_STYLES.put(Level.FATAL, AnsiEscape.createSequence("BRIGHT", "RED"));
98 DEFAULT_STYLES.put(Level.ERROR, AnsiEscape.createSequence("BRIGHT", "RED"));
99 DEFAULT_STYLES.put(Level.WARN, AnsiEscape.createSequence("YELLOW"));
100 DEFAULT_STYLES.put(Level.INFO, AnsiEscape.createSequence("GREEN"));
101 DEFAULT_STYLES.put(Level.DEBUG, AnsiEscape.createSequence("CYAN"));
102 DEFAULT_STYLES.put(Level.TRACE, AnsiEscape.createSequence("BLACK"));
103
104 LOGBACK_STYLES.put(Level.FATAL, AnsiEscape.createSequence("BLINK", "BRIGHT", "RED"));
105 LOGBACK_STYLES.put(Level.ERROR, AnsiEscape.createSequence("BRIGHT", "RED"));
106 LOGBACK_STYLES.put(Level.WARN, AnsiEscape.createSequence("RED"));
107 LOGBACK_STYLES.put(Level.INFO, AnsiEscape.createSequence("BLUE"));
108 LOGBACK_STYLES.put(Level.DEBUG, AnsiEscape.createSequence((String[]) null));
109 LOGBACK_STYLES.put(Level.TRACE, AnsiEscape.createSequence((String[]) null));
110
111 STYLES.put(STYLE_KEY_DEFAULT, DEFAULT_STYLES);
112 STYLES.put(STYLE_KEY_LOGBACK, LOGBACK_STYLES);
113 }
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 private static Map<Level, String> createLevelStyleMap(final String[] options) {
144 if (options.length < 2) {
145 return DEFAULT_STYLES;
146 }
147
148 final String string = options[1]
149 .replaceAll(PatternParser.DISABLE_ANSI + "=(true|false)", Strings.EMPTY)
150 .replaceAll(PatternParser.NO_CONSOLE_NO_ANSI + "=(true|false)", Strings.EMPTY);
151
152 final Map<String, String> styles = AnsiEscape.createMap(string, new String[] {STYLE_KEY});
153 final Map<Level, String> levelStyles = new HashMap<>(DEFAULT_STYLES);
154 for (final Map.Entry<String, String> entry : styles.entrySet()) {
155 final String key = entry.getKey().toUpperCase(Locale.ENGLISH);
156 final String value = entry.getValue();
157 if (STYLE_KEY.equalsIgnoreCase(key)) {
158 final Map<Level, String> enumMap = STYLES.get(value.toUpperCase(Locale.ENGLISH));
159 if (enumMap == null) {
160 LOGGER.error("Unknown level style: " + value + ". Use one of " +
161 Arrays.toString(STYLES.keySet().toArray()));
162 } else {
163 levelStyles.putAll(enumMap);
164 }
165 } else {
166 final Level level = Level.toLevel(key);
167 if (level == null) {
168 LOGGER.error("Unknown level name: " + key + ". Use one of " +
169 Arrays.toString(DEFAULT_STYLES.keySet().toArray()));
170 } else {
171 levelStyles.put(level, value);
172 }
173 }
174 }
175 return levelStyles;
176 }
177
178
179
180
181
182
183
184
185
186 public static HighlightConverter newInstance(final Configuration config, final String[] options) {
187 if (options.length < 1) {
188 LOGGER.error("Incorrect number of options on style. Expected at least 1, received " + options.length);
189 return null;
190 }
191 if (options[0] == null) {
192 LOGGER.error("No pattern supplied on style");
193 return null;
194 }
195 final PatternParser parser = PatternLayout.createPatternParser(config);
196 final List<PatternFormatter> formatters = parser.parse(options[0]);
197 final boolean disableAnsi = Arrays.toString(options).contains(PatternParser.DISABLE_ANSI + "=true");
198 final boolean noConsoleNoAnsi = Arrays.toString(options).contains(PatternParser.NO_CONSOLE_NO_ANSI + "=true");
199 final boolean hideAnsi = disableAnsi || (noConsoleNoAnsi && System.console() == null);
200 return new HighlightConverter(formatters, createLevelStyleMap(options), hideAnsi);
201 }
202
203 private final Map<Level, String> levelStyles;
204
205 private final List<PatternFormatter> patternFormatters;
206
207 private final boolean noAnsi;
208
209 private final String defaultStyle;
210
211
212
213
214
215
216
217
218
219 private HighlightConverter(final List<PatternFormatter> patternFormatters, final Map<Level, String> levelStyles, final boolean noAnsi) {
220 super("style", "style");
221 this.patternFormatters = patternFormatters;
222 this.levelStyles = levelStyles;
223 this.defaultStyle = AnsiEscape.getDefaultStyle();
224 this.noAnsi = noAnsi;
225 }
226
227
228
229
230 @Override
231 public void format(final LogEvent event, final StringBuilder toAppendTo) {
232 int start = 0;
233 int end = 0;
234 if (!noAnsi) {
235 start = toAppendTo.length();
236 toAppendTo.append(levelStyles.get(event.getLevel()));
237 end = toAppendTo.length();
238 }
239
240
241 for (int i = 0, size = patternFormatters.size(); i < size; i++) {
242 patternFormatters.get(i).format(event, toAppendTo);
243 }
244
245
246 final boolean empty = toAppendTo.length() == end;
247 if (!noAnsi) {
248 if (empty) {
249 toAppendTo.setLength(start);
250 } else {
251 toAppendTo.append(defaultStyle);
252 }
253 }
254 }
255
256 @Override
257 public boolean handlesThrowable() {
258 for (final PatternFormatter formatter : patternFormatters) {
259 if (formatter .handlesThrowable()) {
260 return true;
261 }
262 }
263 return false;
264 }
265 }