1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.log4j.xml;
21
22 import org.apache.log4j.Layout;
23 import org.apache.log4j.helpers.Transform;
24 import org.apache.log4j.spi.LocationInfo;
25 import org.apache.log4j.spi.LoggingEvent;
26
27 import java.util.Set;
28 import java.util.Arrays;
29
30 /***
31 * The output of the XMLLayout consists of a series of log4j:event
32 * elements as defined in the <a
33 * href="log4j.dtd">log4j.dtd</a>. It does not output a
34 * complete well-formed XML file. The output is designed to be
35 * included as an <em>external entity</em> in a separate file to form
36 * a correct XML file.
37 *
38 * <p>For example, if <code>abc</code> is the name of the file where
39 * the XMLLayout ouput goes, then a well-formed XML file would be:
40 *
41 <pre>
42 <?xml version="1.0" ?>
43
44 <!DOCTYPE log4j:eventSet SYSTEM "log4j.dtd" [<!ENTITY data SYSTEM "abc">]>
45
46 <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/">
47 &data;
48 </log4j:eventSet>
49 </pre>
50
51 * <p>This approach enforces the independence of the XMLLayout and the
52 * appender where it is embedded.
53 *
54 * <p>The <code>version</code> attribute helps components to correctly
55 * intrepret output generated by XMLLayout. The value of this
56 * attribute should be "1.1" for output generated by log4j versions
57 * prior to log4j 1.2 (final release) and "1.2" for relase 1.2 and
58 * later.
59 *
60 * Appenders using this layout should have their encoding
61 * set to UTF-8 or UTF-16, otherwise events containing
62 * non ASCII characters could result in corrupted
63 * log files.
64 *
65 * @author Ceki Gülcü
66 * @since 0.9.0
67 * */
68 public class XMLLayout extends Layout {
69
70 private final int DEFAULT_SIZE = 256;
71 private final int UPPER_LIMIT = 2048;
72
73 private StringBuffer buf = new StringBuffer(DEFAULT_SIZE);
74 private boolean locationInfo = false;
75 private boolean properties = false;
76
77 /***
78 * The <b>LocationInfo</b> option takes a boolean value. By default,
79 * it is set to false which means there will be no location
80 * information output by this layout. If the the option is set to
81 * true, then the file name and line number of the statement at the
82 * origin of the log statement will be output.
83 *
84 * <p>If you are embedding this layout within an {@link
85 * org.apache.log4j.net.SMTPAppender} then make sure to set the
86 * <b>LocationInfo</b> option of that appender as well.
87 * */
88 public void setLocationInfo(boolean flag) {
89 locationInfo = flag;
90 }
91
92 /***
93 Returns the current value of the <b>LocationInfo</b> option.
94 */
95 public boolean getLocationInfo() {
96 return locationInfo;
97 }
98
99 /***
100 * Sets whether MDC key-value pairs should be output, default false.
101 * @param flag new value.
102 */
103 public void setProperties(final boolean flag) {
104 properties = flag;
105 }
106
107 /***
108 * Gets whether MDC key-value pairs should be output.
109 * @return true if MDC key-value pairs are output.
110 */
111 public boolean getProperties() {
112 return properties;
113 }
114
115 /*** No options to activate. */
116 public void activateOptions() {
117 }
118
119
120 /***
121 * Formats a {@link org.apache.log4j.spi.LoggingEvent} in conformance with the log4j.dtd.
122 * */
123 public String format(final LoggingEvent event) {
124
125
126
127 if(buf.capacity() > UPPER_LIMIT) {
128 buf = new StringBuffer(DEFAULT_SIZE);
129 } else {
130 buf.setLength(0);
131 }
132
133
134
135 buf.append("<log4j:event logger=\"");
136 buf.append(Transform.escapeTags(event.getLoggerName()));
137 buf.append("\" timestamp=\"");
138 buf.append(event.timeStamp);
139 buf.append("\" level=\"");
140 buf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
141 buf.append("\" thread=\"");
142 buf.append(Transform.escapeTags(event.getThreadName()));
143 buf.append("\">\r\n");
144
145 buf.append("<log4j:message><![CDATA[");
146
147
148 Transform.appendEscapingCDATA(buf, event.getRenderedMessage());
149 buf.append("]]></log4j:message>\r\n");
150
151 String ndc = event.getNDC();
152 if(ndc != null) {
153 buf.append("<log4j:NDC><![CDATA[");
154 Transform.appendEscapingCDATA(buf, ndc);
155 buf.append("]]></log4j:NDC>\r\n");
156 }
157
158 String[] s = event.getThrowableStrRep();
159 if(s != null) {
160 buf.append("<log4j:throwable><![CDATA[");
161 for(int i = 0; i < s.length; i++) {
162 Transform.appendEscapingCDATA(buf, s[i]);
163 buf.append("\r\n");
164 }
165 buf.append("]]></log4j:throwable>\r\n");
166 }
167
168 if(locationInfo) {
169 LocationInfo locationInfo = event.getLocationInformation();
170 buf.append("<log4j:locationInfo class=\"");
171 buf.append(Transform.escapeTags(locationInfo.getClassName()));
172 buf.append("\" method=\"");
173 buf.append(Transform.escapeTags(locationInfo.getMethodName()));
174 buf.append("\" file=\"");
175 buf.append(Transform.escapeTags(locationInfo.getFileName()));
176 buf.append("\" line=\"");
177 buf.append(locationInfo.getLineNumber());
178 buf.append("\"/>\r\n");
179 }
180
181 if (properties) {
182 Set keySet = event.getPropertyKeySet();
183 if (keySet.size() > 0) {
184 buf.append("<log4j:properties>\r\n");
185 Object[] keys = keySet.toArray();
186 Arrays.sort(keys);
187 for (int i = 0; i < keys.length; i++) {
188 String key = keys[i].toString();
189 Object val = event.getMDC(key);
190 if (val != null) {
191 buf.append("<log4j:data name=\"");
192 buf.append(Transform.escapeTags(key));
193 buf.append("\" value=\"");
194 buf.append(Transform.escapeTags(String.valueOf(val)));
195 buf.append("\"/>\r\n");
196 }
197 }
198 buf.append("</log4j:properties>\r\n");
199 }
200 }
201
202 buf.append("</log4j:event>\r\n\r\n");
203
204 return buf.toString();
205 }
206
207 /***
208 The XMLLayout prints and does not ignore exceptions. Hence the
209 return value <code>false</code>.
210 */
211 public boolean ignoresThrowable() {
212 return false;
213 }
214 }