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<Level, String>();
74
75 private static final Map<Level, String> LOGBACK_STYLES = new HashMap<Level, String>();
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<String, Map<Level, String>>();
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<Level, String>(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 = options.length > 1
186 && (PatternParser.NO_CONSOLE_NO_ANSI + "=true").equals(options[1]);
187 final boolean hideAnsi = noConsoleNoAnsi && System.console() == null;
188 return new HighlightConverter(formatters, createLevelStyleMap(options), hideAnsi);
189 }
190
191 private final Map<Level, String> levelStyles;
192
193 private final List<PatternFormatter> patternFormatters;
194
195 private boolean noAnsi;
196
197
198
199
200
201
202
203
204
205 private HighlightConverter(final List<PatternFormatter> patternFormatters, final Map<Level, String> levelStyles, final boolean noAnsi) {
206 super("style", "style");
207 this.patternFormatters = patternFormatters;
208 this.levelStyles = levelStyles;
209 this.noAnsi = noAnsi;
210 }
211
212
213
214
215 @Override
216 public void format(final LogEvent event, final StringBuilder toAppendTo) {
217 final StringBuilder buf = new StringBuilder();
218 for (final PatternFormatter formatter : patternFormatters) {
219 formatter.format(event, buf);
220 }
221
222 if (buf.length() > 0) {
223 if (noAnsi) {
224 toAppendTo.append(buf.toString());
225 } else {
226 toAppendTo.append(levelStyles.get(event.getLevel())).append(buf.toString()).
227 append(AnsiEscape.getDefaultStyle());
228 }
229 }
230 }
231
232 @Override
233 public boolean handlesThrowable() {
234 for (final PatternFormatter formatter : patternFormatters) {
235 if (formatter .handlesThrowable()) {
236 return true;
237 }
238 }
239 return false;
240 }
241 }