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, null);
167 if (level == null) {
168 LOGGER.error("Unknown level name: {}; use one of {}", key, Arrays.toString(Level.values()));
169 } else {
170 levelStyles.put(level, value);
171 }
172 }
173 }
174 return levelStyles;
175 }
176
177
178
179
180
181
182
183
184
185 public static HighlightConverter newInstance(final Configuration config, final String[] options) {
186 if (options.length < 1) {
187 LOGGER.error("Incorrect number of options on style. Expected at least 1, received " + options.length);
188 return null;
189 }
190 if (options[0] == null) {
191 LOGGER.error("No pattern supplied on style");
192 return null;
193 }
194 final PatternParser parser = PatternLayout.createPatternParser(config);
195 final List<PatternFormatter> formatters = parser.parse(options[0]);
196 final boolean disableAnsi = Arrays.toString(options).contains(PatternParser.DISABLE_ANSI + "=true");
197 final boolean noConsoleNoAnsi = Arrays.toString(options).contains(PatternParser.NO_CONSOLE_NO_ANSI + "=true");
198 final boolean hideAnsi = disableAnsi || (noConsoleNoAnsi && System.console() == null);
199 return new HighlightConverter(formatters, createLevelStyleMap(options), hideAnsi);
200 }
201
202 private final Map<Level, String> levelStyles;
203
204 private final List<PatternFormatter> patternFormatters;
205
206 private final boolean noAnsi;
207
208 private final String defaultStyle;
209
210
211
212
213
214
215
216
217
218 private HighlightConverter(final List<PatternFormatter> patternFormatters, final Map<Level, String> levelStyles, final boolean noAnsi) {
219 super("style", "style");
220 this.patternFormatters = patternFormatters;
221 this.levelStyles = levelStyles;
222 this.defaultStyle = AnsiEscape.getDefaultStyle();
223 this.noAnsi = noAnsi;
224 }
225
226
227
228
229 @Override
230 public void format(final LogEvent event, final StringBuilder toAppendTo) {
231 int start = 0;
232 int end = 0;
233 if (!noAnsi) {
234 start = toAppendTo.length();
235 toAppendTo.append(levelStyles.get(event.getLevel()));
236 end = toAppendTo.length();
237 }
238
239
240 for (int i = 0, size = patternFormatters.size(); i < size; i++) {
241 patternFormatters.get(i).format(event, toAppendTo);
242 }
243
244
245 final boolean empty = toAppendTo.length() == end;
246 if (!noAnsi) {
247 if (empty) {
248 toAppendTo.setLength(start);
249 } else {
250 toAppendTo.append(defaultStyle);
251 }
252 }
253 }
254
255 String getLevelStyle(Level level) {
256 return levelStyles.get(level);
257 }
258
259 @Override
260 public boolean handlesThrowable() {
261 for (final PatternFormatter formatter : patternFormatters) {
262 if (formatter .handlesThrowable()) {
263 return true;
264 }
265 }
266 return false;
267 }
268
269 }