View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2009, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.log4j;
15  
16  import java.util.Map;
17  import java.util.Set;
18  import java.util.Map.Entry;
19  
20  import ch.qos.logback.classic.spi.ILoggingEvent;
21  import ch.qos.logback.classic.spi.IThrowableProxy;
22  import ch.qos.logback.classic.spi.StackTraceElementProxy;
23  import ch.qos.logback.core.CoreConstants;
24  import ch.qos.logback.core.LayoutBase;
25  import ch.qos.logback.core.helpers.Transform;
26  
27  // Code is based on revision 309623 of org.apache.log4j.xml.XMLLayout dated "Wed
28  // Jul 31 09:25:14 2002 UTC" as authored by Ceki Gulcu.
29  // See also http://tinyurl.com/dch9mr
30  
31  /**
32   * 
33   * Generates log4j.dtd compliant XML documents.
34   * 
35   * 
36   * @author Ceki Gülcü
37   */
38  public class XMLLayout extends LayoutBase<ILoggingEvent> {
39  
40    private final int DEFAULT_SIZE = 256;
41    private final int UPPER_LIMIT = 2048;
42  
43    private StringBuilder buf = new StringBuilder(DEFAULT_SIZE);
44    private boolean locationInfo = false;
45    private boolean properties = false;
46  
47    @Override
48    public void start() {
49      super.start();
50    }
51  
52    /**
53     * The <b>LocationInfo</b> option takes a boolean value. By default, it is
54     * set to false which means there will be no location information output by
55     * this layout. If the the option is set to true, then the file name and line
56     * number of the statement at the origin of the log statement will be output.
57     * 
58     * <p>If you are embedding this layout within an {@link
59     * org.apache.log4j.net.SMTPAppender} then make sure to set the
60     * <b>LocationInfo</b> option of that appender as well.
61     */
62    public void setLocationInfo(boolean flag) {
63      locationInfo = flag;
64    }
65  
66    /**
67     * Returns the current value of the <b>LocationInfo</b> option.
68     */
69    public boolean getLocationInfo() {
70      return locationInfo;
71    }
72  
73    /**
74     * Sets whether MDC key-value pairs should be output, default false.
75     * 
76     * @param flag
77     *                new value.
78     * @since 1.2.15
79     */
80    public void setProperties(final boolean flag) {
81      properties = flag;
82    }
83  
84    /**
85     * Gets whether MDC key-value pairs should be output.
86     * 
87     * @return true if MDC key-value pairs are output.
88     * @since 1.2.15
89     */
90    public boolean getProperties() {
91      return properties;
92    }
93  
94    /**
95     * Formats a {@link ILoggingEvent} in conformity with the log4j.dtd.
96     */
97    public String doLayout(ILoggingEvent event) {
98  
99      // Reset working buffer. If the buffer is too large, then we need a new
100     // one in order to avoid the penalty of creating a large array.
101     if (buf.capacity() > UPPER_LIMIT) {
102       buf = new StringBuilder(DEFAULT_SIZE);
103     } else {
104       buf.setLength(0);
105     }
106 
107     // We yield to the \r\n heresy.
108 
109     buf.append("<log4j:event logger=\"");
110     buf.append(event.getLoggerName());
111     buf.append("\"\r\n");
112     buf.append("             timestamp=\"");
113     buf.append(event.getTimeStamp());
114     buf.append("\" level=\"");
115     buf.append(event.getLevel());
116     buf.append("\" thread=\"");
117     buf.append(event.getThreadName());
118     buf.append("\">\r\n");
119 
120     buf.append("  <log4j:message><![CDATA[");
121     // Append the rendered message. Also make sure to escape any
122     // existing CDATA sections.
123     Transform.appendEscapingCDATA(buf, event.getFormattedMessage());
124     buf.append("]]></log4j:message>\r\n");
125 
126     // logback does not support NDC
127     // String ndc = event.getNDC();
128 
129 
130     IThrowableProxy tp = event.getThrowableProxy();
131     if (tp != null) {
132       StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
133       buf.append("  <log4j:throwable><![CDATA[");
134       for (StackTraceElementProxy step : stepArray) {
135         buf.append(CoreConstants.TAB);
136         buf.append(step.toString());
137         buf.append("\r\n");
138       }
139       buf.append("]]></log4j:throwable>\r\n");
140     }
141 
142     if (locationInfo) {
143       StackTraceElement[] callerDataArray = event.getCallerData();
144       if (callerDataArray != null && callerDataArray.length > 0) {
145         StackTraceElement immediateCallerData = callerDataArray[0];
146         buf.append("  <log4j:locationInfo class=\"");
147         buf.append(immediateCallerData.getClassName());
148         buf.append("\"\r\n");
149         buf.append("                      method=\"");
150         buf.append(Transform.escapeTags(immediateCallerData.getMethodName()));
151         buf.append("\" file=\"");
152         buf.append(immediateCallerData.getFileName());
153         buf.append("\" line=\"");
154         buf.append(immediateCallerData.getLineNumber());
155         buf.append("\"/>\r\n");
156       }
157     }
158 
159     /*
160      * <log4j:properties> <log4j:data name="name" value="value"/>
161      * </log4j:properties>
162      */
163     if (this.getProperties()) {
164       Map<String, String> propertyMap = event.getMDCPropertyMap();
165 
166       if ((propertyMap != null) && (propertyMap.size() != 0)) {
167         Set<Entry<String, String>> entrySet = propertyMap.entrySet();
168         buf.append("  <log4j:properties>");
169         for (Entry<String, String> entry : entrySet) {
170           buf.append("\r\n    <log4j:data");
171           buf.append(" name='" + Transform.escapeTags(entry.getKey()) + "'");
172           buf.append(" value='" + Transform.escapeTags(entry.getValue()) + "'");
173           buf.append(" />");
174         }
175         buf.append("\r\n  </log4j:properties>");
176       }
177     }
178 
179     buf.append("\r\n</log4j:event>\r\n\r\n");
180 
181     return buf.toString();
182   }
183 
184   @Override
185   public String getContentType() {
186     return "text/xml";
187   }
188 
189 }