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<String, String> DEFAULT_STYLES = new HashMap<>();
84
85 private static final Map<String, 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<String, String>> STYLES = new HashMap<>();
94
95 static {
96
97 DEFAULT_STYLES.put(Level.FATAL.name(), AnsiEscape.createSequence("BRIGHT", "RED"));
98 DEFAULT_STYLES.put(Level.ERROR.name(), AnsiEscape.createSequence("BRIGHT", "RED"));
99 DEFAULT_STYLES.put(Level.WARN.name(), AnsiEscape.createSequence("YELLOW"));
100 DEFAULT_STYLES.put(Level.INFO.name(), AnsiEscape.createSequence("GREEN"));
101 DEFAULT_STYLES.put(Level.DEBUG.name(), AnsiEscape.createSequence("CYAN"));
102 DEFAULT_STYLES.put(Level.TRACE.name(), AnsiEscape.createSequence("BLACK"));
103
104 LOGBACK_STYLES.put(Level.FATAL.name(), AnsiEscape.createSequence("BLINK", "BRIGHT", "RED"));
105 LOGBACK_STYLES.put(Level.ERROR.name(), AnsiEscape.createSequence("BRIGHT", "RED"));
106 LOGBACK_STYLES.put(Level.WARN.name(), AnsiEscape.createSequence("RED"));
107 LOGBACK_STYLES.put(Level.INFO.name(), AnsiEscape.createSequence("BLUE"));
108 LOGBACK_STYLES.put(Level.DEBUG.name(), AnsiEscape.createSequence((String[]) null));
109 LOGBACK_STYLES.put(Level.TRACE.name(), 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<String, 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<String, 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<String, 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, null);
167 if (level == null) {
168 LOGGER.warn("Setting style for yet unknown level name {}", key);
169 levelStyles.put(key, value);
170 } else {
171 levelStyles.put(level.name(), 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<String, 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<String, 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 final String levelStyle = levelStyles.get(event.getLevel().name());
235 if (!noAnsi) {
236 start = toAppendTo.length();
237 if (levelStyle != null) {
238 toAppendTo.append(levelStyle);
239 }
240 end = toAppendTo.length();
241 }
242
243
244 for (int i = 0, size = patternFormatters.size(); i < size; i++) {
245 patternFormatters.get(i).format(event, toAppendTo);
246 }
247
248
249 final boolean empty = toAppendTo.length() == end;
250 if (!noAnsi) {
251 if (empty) {
252 toAppendTo.setLength(start);
253 } else if (levelStyle != null) {
254 toAppendTo.append(defaultStyle);
255 }
256 }
257 }
258
259 String getLevelStyle(final Level level) {
260 return levelStyles.get(level.name());
261 }
262
263 @Override
264 public boolean handlesThrowable() {
265 for (final PatternFormatter formatter : patternFormatters) {
266 if (formatter .handlesThrowable()) {
267 return true;
268 }
269 }
270 return false;
271 }
272
273 }