1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.layout;
18
19 import java.nio.charset.Charset;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
24 import org.apache.logging.log4j.core.LogEvent;
25 import org.apache.logging.log4j.core.config.plugins.Plugin;
26 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
27 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
28 import org.apache.logging.log4j.core.helpers.Charsets;
29 import org.apache.logging.log4j.core.helpers.Strings;
30 import org.apache.logging.log4j.core.helpers.Throwables;
31 import org.apache.logging.log4j.core.helpers.Transform;
32 import org.apache.logging.log4j.message.Message;
33 import org.apache.logging.log4j.message.MultiformatMessage;
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
79 @Plugin(name = "XMLLayout", category = "Core", elementType = "layout", printObject = true)
80 public class XMLLayout extends AbstractStringLayout {
81
82 private static final String XML_NAMESPACE = "http://logging.apache.org/log4j/2.0/events";
83 private static final String ROOT_TAG = "Events";
84 private static final int DEFAULT_SIZE = 256;
85
86
87 private static final String DEFAULT_EOL = "\r\n";
88 private static final String COMPACT_EOL = "";
89 private static final String DEFAULT_INDENT = " ";
90 private static final String COMPACT_INDENT = "";
91 private static final String DEFAULT_NS_PREFIX = "log4j";
92
93 private static final String[] FORMATS = new String[] {"xml"};
94
95 private final boolean locationInfo;
96 private final boolean properties;
97 private final boolean complete;
98 private final String namespacePrefix;
99 private final String eol;
100 private final String indent1;
101 private final String indent2;
102 private final String indent3;
103
104 protected XMLLayout(final boolean locationInfo, final boolean properties, final boolean complete,
105 boolean compact, final String nsPrefix, final Charset charset) {
106 super(charset);
107 this.locationInfo = locationInfo;
108 this.properties = properties;
109 this.complete = complete;
110 this.eol = compact ? COMPACT_EOL : DEFAULT_EOL;
111 this.indent1 = compact ? COMPACT_INDENT : DEFAULT_INDENT;
112 this.indent2 = this.indent1 + this.indent1;
113 this.indent3 = this.indent2 + this.indent1;
114 this.namespacePrefix = (Strings.isEmpty(nsPrefix) ? DEFAULT_NS_PREFIX : nsPrefix) + ":";
115 }
116
117
118
119
120
121
122
123 @Override
124 public String toSerializable(final LogEvent event) {
125 final StringBuilder buf = new StringBuilder(DEFAULT_SIZE);
126
127 buf.append(this.indent1);
128 buf.append('<');
129 if (!complete) {
130 buf.append(this.namespacePrefix);
131 }
132 buf.append("Event logger=\"");
133 String name = event.getLoggerName();
134 if (name.isEmpty()) {
135 name = "root";
136 }
137 buf.append(Transform.escapeHtmlTags(name));
138 buf.append("\" timestamp=\"");
139 buf.append(event.getMillis());
140 buf.append("\" level=\"");
141 buf.append(Transform.escapeHtmlTags(String.valueOf(event.getLevel())));
142 buf.append("\" thread=\"");
143 buf.append(Transform.escapeHtmlTags(event.getThreadName()));
144 buf.append("\">");
145 buf.append(this.eol);
146
147 final Message msg = event.getMessage();
148 if (msg != null) {
149 boolean xmlSupported = false;
150 if (msg instanceof MultiformatMessage) {
151 final String[] formats = ((MultiformatMessage) msg).getFormats();
152 for (final String format : formats) {
153 if (format.equalsIgnoreCase("XML")) {
154 xmlSupported = true;
155 break;
156 }
157 }
158 }
159 buf.append(this.indent2);
160 buf.append('<');
161 if (!complete) {
162 buf.append(this.namespacePrefix);
163 }
164 buf.append("Message>");
165 if (xmlSupported) {
166 buf.append(((MultiformatMessage) msg).getFormattedMessage(FORMATS));
167 } else {
168 buf.append("<![CDATA[");
169
170
171 Transform.appendEscapingCDATA(buf, event.getMessage().getFormattedMessage());
172 buf.append("]]>");
173 }
174 buf.append("</");
175 if (!complete) {
176 buf.append(this.namespacePrefix);
177 }
178 buf.append("Message>");
179 buf.append(this.eol);
180 }
181
182 if (event.getContextStack().getDepth() > 0) {
183 buf.append(this.indent2);
184 buf.append('<');
185 if (!complete) {
186 buf.append(this.namespacePrefix);
187 }
188 buf.append("NDC><![CDATA[");
189 Transform.appendEscapingCDATA(buf, event.getContextStack().toString());
190 buf.append("]]></");
191 if (!complete) {
192 buf.append(this.namespacePrefix);
193 }
194 buf.append("NDC>");
195 buf.append(this.eol);
196 }
197
198 final Throwable throwable = event.getThrown();
199 if (throwable != null) {
200 final List<String> s = Throwables.toStringList(throwable);
201 buf.append(this.indent2);
202 buf.append('<');
203 if (!complete) {
204 buf.append(this.namespacePrefix);
205 }
206 buf.append("Throwable><![CDATA[");
207 for (final String str : s) {
208 Transform.appendEscapingCDATA(buf, str);
209 buf.append(this.eol);
210 }
211 buf.append("]]></");
212 if (!complete) {
213 buf.append(this.namespacePrefix);
214 }
215 buf.append("Throwable>");
216 buf.append(this.eol);
217 }
218
219 if (locationInfo) {
220 final StackTraceElement element = event.getSource();
221 buf.append(this.indent2);
222 buf.append('<');
223 if (!complete) {
224 buf.append(this.namespacePrefix);
225 }
226 buf.append("LocationInfo class=\"");
227 buf.append(Transform.escapeHtmlTags(element.getClassName()));
228 buf.append("\" method=\"");
229 buf.append(Transform.escapeHtmlTags(element.getMethodName()));
230 buf.append("\" file=\"");
231 buf.append(Transform.escapeHtmlTags(element.getFileName()));
232 buf.append("\" line=\"");
233 buf.append(element.getLineNumber());
234 buf.append("\"/>");
235 buf.append(this.eol);
236 }
237
238 if (properties && event.getContextMap().size() > 0) {
239 buf.append(this.indent2);
240 buf.append('<');
241 if (!complete) {
242 buf.append(this.namespacePrefix);
243 }
244 buf.append("Properties>");
245 buf.append(this.eol);
246 for (final Map.Entry<String, String> entry : event.getContextMap().entrySet()) {
247 buf.append(this.indent3);
248 buf.append('<');
249 if (!complete) {
250 buf.append(this.namespacePrefix);
251 }
252 buf.append("Data name=\"");
253 buf.append(Transform.escapeHtmlTags(entry.getKey()));
254 buf.append("\" value=\"");
255 buf.append(Transform.escapeHtmlTags(String.valueOf(entry.getValue())));
256 buf.append("\"/>");
257 buf.append(this.eol);
258 }
259 buf.append(this.indent2);
260 buf.append("</");
261 if (!complete) {
262 buf.append(this.namespacePrefix);
263 }
264 buf.append("Properties>");
265 buf.append(this.eol);
266 }
267
268 buf.append(this.indent1);
269 buf.append("</");
270 if (!complete) {
271 buf.append(this.namespacePrefix);
272 }
273 buf.append("Event>");
274 buf.append(this.eol);
275
276 return buf.toString();
277 }
278
279
280
281
282
283
284
285
286
287
288 @Override
289 public byte[] getHeader() {
290 if (!complete) {
291 return null;
292 }
293 final StringBuilder buf = new StringBuilder();
294 buf.append("<?xml version=\"1.0\" encoding=\"");
295 buf.append(this.getCharset().name());
296 buf.append("\"?>");
297 buf.append(this.eol);
298
299 buf.append('<');
300 buf.append(ROOT_TAG);
301 buf.append(" xmlns=\"" + XML_NAMESPACE + "\">");
302 buf.append(this.eol);
303 return buf.toString().getBytes(this.getCharset());
304 }
305
306
307
308
309
310
311
312 @Override
313 public byte[] getFooter() {
314 if (!complete) {
315 return null;
316 }
317 return ("</" + ROOT_TAG + ">" + this.eol).getBytes(getCharset());
318 }
319
320
321
322
323
324
325
326 @Override
327 public Map<String, String> getContentFormat() {
328 final Map<String, String> result = new HashMap<String, String>();
329
330 result.put("xsd", "log4j-events.xsd");
331 result.put("version", "2.0");
332 return result;
333 }
334
335 @Override
336
337
338
339 public String getContentType() {
340 return "text/xml; charset=" + this.getCharset();
341 }
342
343
344
345
346
347
348
349
350
351
352
353
354 @PluginFactory
355 public static XMLLayout createLayout(
356 @PluginAttribute("locationInfo") final String locationInfo,
357 @PluginAttribute("properties") final String properties,
358 @PluginAttribute("complete") final String completeStr,
359 @PluginAttribute("compact") final String compactStr,
360 @PluginAttribute("namespacePrefix") final String namespacePrefix,
361 @PluginAttribute("charset") final String charsetName) {
362 final Charset charset = Charsets.getSupportedCharset(charsetName, Charsets.UTF_8);
363 final boolean info = Boolean.parseBoolean(locationInfo);
364 final boolean props = Boolean.parseBoolean(properties);
365 final boolean complete = Boolean.parseBoolean(completeStr);
366 final boolean compact = Boolean.parseBoolean(compactStr);
367 return new XMLLayout(info, props, complete, compact, namespacePrefix, charset);
368 }
369 }