View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  // Contributors:   Mathias Bogaert
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     &lt;?xml version="1.0" ?&gt;
43   
44    &lt;!DOCTYPE log4j:eventSet SYSTEM "log4j.dtd" [&lt;!ENTITY data SYSTEM "abc"&gt;]&gt;
45   
46    &lt;log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/"&gt;
47   	&nbsp;&nbsp;&data;
48    &lt;/log4j:eventSet&gt;
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&uuml;lc&uuml;
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     // Reset working buffer. If the buffer is too large, then we need a new
126     // one in order to avoid the penalty of creating a large array.
127     if(buf.capacity() > UPPER_LIMIT) {
128       buf = new StringBuffer(DEFAULT_SIZE);
129     } else {
130       buf.setLength(0);
131     }
132     
133     // We yield to the \r\n heresy.
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     // Append the rendered message. Also make sure to escape any
147     // existing CDATA sections.
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 }