View Javadoc

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