| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Contributors: Mathias Bogaert |
| |
| package org.apache.log4j.xml; |
| |
| import org.apache.log4j.Layout; |
| import org.apache.log4j.helpers.Transform; |
| import org.apache.log4j.spi.LocationInfo; |
| import org.apache.log4j.spi.LoggingEvent; |
| |
| import java.util.Set; |
| import java.util.Arrays; |
| |
| /** |
| * The output of the XMLLayout consists of a series of log4j:event |
| * elements as defined in the <a |
| * href="log4j.dtd">log4j.dtd</a>. It does not output a |
| * complete well-formed XML file. The output is designed to be |
| * included as an <em>external entity</em> in a separate file to form |
| * a correct XML file. |
| * |
| * <p>For example, if <code>abc</code> is the name of the file where |
| * the XMLLayout ouput goes, then a well-formed XML file would be: |
| * |
| <pre> |
| <?xml version="1.0" ?> |
| |
| <!DOCTYPE log4j:eventSet SYSTEM "log4j.dtd" [<!ENTITY data SYSTEM "abc">]> |
| |
| <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/"> |
| &data; |
| </log4j:eventSet> |
| </pre> |
| |
| * <p>This approach enforces the independence of the XMLLayout and the |
| * appender where it is embedded. |
| * |
| * <p>The <code>version</code> attribute helps components to correctly |
| * intrepret output generated by XMLLayout. The value of this |
| * attribute should be "1.1" for output generated by log4j versions |
| * prior to log4j 1.2 (final release) and "1.2" for relase 1.2 and |
| * later. |
| * |
| * Appenders using this layout should have their encoding |
| * set to UTF-8 or UTF-16, otherwise events containing |
| * non ASCII characters could result in corrupted |
| * log files. |
| * |
| * @author Ceki Gülcü |
| * @since 0.9.0 |
| * */ |
| public class XMLLayout extends Layout { |
| |
| private final int DEFAULT_SIZE = 256; |
| private final int UPPER_LIMIT = 2048; |
| |
| private StringBuffer buf = new StringBuffer(DEFAULT_SIZE); |
| private boolean locationInfo = false; |
| private boolean properties = false; |
| |
| /** |
| * The <b>LocationInfo</b> option takes a boolean value. By default, |
| * it is set to false which means there will be no location |
| * information output by this layout. If the the option is set to |
| * true, then the file name and line number of the statement at the |
| * origin of the log statement will be output. |
| * |
| * <p>If you are embedding this layout within an {@link |
| * org.apache.log4j.net.SMTPAppender} then make sure to set the |
| * <b>LocationInfo</b> option of that appender as well. |
| * */ |
| public void setLocationInfo(boolean flag) { |
| locationInfo = flag; |
| } |
| |
| /** |
| Returns the current value of the <b>LocationInfo</b> option. |
| */ |
| public boolean getLocationInfo() { |
| return locationInfo; |
| } |
| |
| /** |
| * Sets whether MDC key-value pairs should be output, default false. |
| * @param flag new value. |
| * @since 1.2.15 |
| */ |
| public void setProperties(final boolean flag) { |
| properties = flag; |
| } |
| |
| /** |
| * Gets whether MDC key-value pairs should be output. |
| * @return true if MDC key-value pairs are output. |
| * @since 1.2.15 |
| */ |
| public boolean getProperties() { |
| return properties; |
| } |
| |
| /** No options to activate. */ |
| public void activateOptions() { |
| } |
| |
| |
| /** |
| * Formats a {@link org.apache.log4j.spi.LoggingEvent} in conformance with the log4j.dtd. |
| * */ |
| public String format(final LoggingEvent event) { |
| |
| // Reset working buffer. If the buffer is too large, then we need a new |
| // one in order to avoid the penalty of creating a large array. |
| if(buf.capacity() > UPPER_LIMIT) { |
| buf = new StringBuffer(DEFAULT_SIZE); |
| } else { |
| buf.setLength(0); |
| } |
| |
| // We yield to the \r\n heresy. |
| |
| buf.append("<log4j:event logger=\""); |
| buf.append(Transform.escapeTags(event.getLoggerName())); |
| buf.append("\" timestamp=\""); |
| buf.append(event.timeStamp); |
| buf.append("\" level=\""); |
| buf.append(Transform.escapeTags(String.valueOf(event.getLevel()))); |
| buf.append("\" thread=\""); |
| buf.append(Transform.escapeTags(event.getThreadName())); |
| buf.append("\">\r\n"); |
| |
| buf.append("<log4j:message><![CDATA["); |
| // Append the rendered message. Also make sure to escape any |
| // existing CDATA sections. |
| Transform.appendEscapingCDATA(buf, event.getRenderedMessage()); |
| buf.append("]]></log4j:message>\r\n"); |
| |
| String ndc = event.getNDC(); |
| if(ndc != null) { |
| buf.append("<log4j:NDC><![CDATA["); |
| Transform.appendEscapingCDATA(buf, ndc); |
| buf.append("]]></log4j:NDC>\r\n"); |
| } |
| |
| String[] s = event.getThrowableStrRep(); |
| if(s != null) { |
| buf.append("<log4j:throwable><![CDATA["); |
| for(int i = 0; i < s.length; i++) { |
| Transform.appendEscapingCDATA(buf, s[i]); |
| buf.append("\r\n"); |
| } |
| buf.append("]]></log4j:throwable>\r\n"); |
| } |
| |
| if(locationInfo) { |
| LocationInfo locationInfo = event.getLocationInformation(); |
| buf.append("<log4j:locationInfo class=\""); |
| buf.append(Transform.escapeTags(locationInfo.getClassName())); |
| buf.append("\" method=\""); |
| buf.append(Transform.escapeTags(locationInfo.getMethodName())); |
| buf.append("\" file=\""); |
| buf.append(Transform.escapeTags(locationInfo.getFileName())); |
| buf.append("\" line=\""); |
| buf.append(locationInfo.getLineNumber()); |
| buf.append("\"/>\r\n"); |
| } |
| |
| if (properties) { |
| Set keySet = event.getPropertyKeySet(); |
| if (keySet.size() > 0) { |
| buf.append("<log4j:properties>\r\n"); |
| Object[] keys = keySet.toArray(); |
| Arrays.sort(keys); |
| for (int i = 0; i < keys.length; i++) { |
| String key = keys[i].toString(); |
| Object val = event.getMDC(key); |
| if (val != null) { |
| buf.append("<log4j:data name=\""); |
| buf.append(Transform.escapeTags(key)); |
| buf.append("\" value=\""); |
| buf.append(Transform.escapeTags(String.valueOf(val))); |
| buf.append("\"/>\r\n"); |
| } |
| } |
| buf.append("</log4j:properties>\r\n"); |
| } |
| } |
| |
| buf.append("</log4j:event>\r\n\r\n"); |
| |
| return buf.toString(); |
| } |
| |
| /** |
| The XMLLayout prints and does not ignore exceptions. Hence the |
| return value <code>false</code>. |
| */ |
| public boolean ignoresThrowable() { |
| return false; |
| } |
| } |