View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.util;
21  
22  import org.slf4j.MDC;
23  import org.slf4j.helpers.BasicMDCAdapter;
24  
25  import java.util.logging.Formatter;
26  import java.util.logging.LogRecord;
27  import java.util.Set;
28  import java.util.Arrays;
29  
30  /**
31   * Implementation of {@link java.util.logging.Formatter} that generates xml in the log4j format.
32   * <p>
33   * The generated xml corresponds 100% with what is generated by
34   * log4j's <a href=http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/XMLLayout.html">XMLLayout</a>
35   * <p>
36   * The MDC properties will only be correct when <code>format</code> is called from the same thread
37   * that generated the LogRecord.
38   * <p>
39   * The file and line attributes in the locationInfo element will always be "?"
40   * since java.util.logging.LogRecord does not provide that info.
41   * <p>
42   * The implementation is heavily based on org.apache.log4j.xml.XMLLayout
43   * </p>
44   * 
45   * @author The Apache MINA Project (dev@mina.apache.org)
46   * @version $Rev: 662908 $, $Date: 2008-06-04 00:37:22 +0200 (mer, 04 jun 2008) $
47   */
48  public class Log4jXmlFormatter extends Formatter {
49  
50      private final int DEFAULT_SIZE = 256;
51      private final int UPPER_LIMIT = 2048;
52  
53      private StringBuffer buf = new StringBuffer(DEFAULT_SIZE);
54      private boolean locationInfo = false;
55      private boolean properties = false;
56  
57      /**
58       * The <b>LocationInfo</b> option takes a boolean value. By default,
59       * it is set to false which means there will be no location
60       * information output by this layout. If the the option is set to
61       * true, then the file name and line number of the statement at the
62       * origin of the log statement will be output.
63       *
64       * @param flag whether locationInfo should be output by this layout
65       */
66      public void setLocationInfo(boolean flag) {
67          locationInfo = flag;
68      }
69  
70      /**
71       * Returns the current value of the <b>LocationInfo</b> option.
72       *
73       * @return whether locationInfo will be output by this layout
74       */
75      public boolean getLocationInfo() {
76          return locationInfo;
77      }
78  
79      /**
80       * Sets whether MDC key-value pairs should be output, default false.
81       *
82       * @param flag new value.
83       */
84      public void setProperties(final boolean flag) {
85          properties = flag;
86      }
87  
88      /**
89       * Gets whether MDC key-value pairs should be output.
90       *
91       * @return true if MDC key-value pairs are output.
92       */
93      public boolean getProperties() {
94          return properties;
95      }
96  
97      @SuppressWarnings("unchecked")
98      public String format(final LogRecord record) {
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 StringBuffer(DEFAULT_SIZE);
103         } else {
104             buf.setLength(0);
105         }
106         buf.append("<log4j:event logger=\"");
107         buf.append(Transform.escapeTags(record.getLoggerName()));
108         buf.append("\" timestamp=\"");
109         buf.append(record.getMillis());
110         buf.append("\" level=\"");
111 
112         buf.append(Transform.escapeTags(record.getLevel().getName()));
113         buf.append("\" thread=\"");
114         buf.append(String.valueOf(record.getThreadID()));
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, record.getMessage());
121         buf.append("]]></log4j:message>\r\n");
122 
123         if (record.getThrown() != null) {
124             String[] s = Transform.getThrowableStrRep(record.getThrown());
125             if (s != null) {
126                 buf.append("<log4j:throwable><![CDATA[");
127                 for (String value : s) {
128                     Transform.appendEscapingCDATA(buf, value);
129                     buf.append("\r\n");
130                 }
131                 buf.append("]]></log4j:throwable>\r\n");
132             }
133         }
134 
135         if (locationInfo) {
136             buf.append("<log4j:locationInfo class=\"");
137             buf.append(Transform.escapeTags(record.getSourceClassName()));
138             buf.append("\" method=\"");
139             buf.append(Transform.escapeTags(record.getSourceMethodName()));
140             buf.append("\" file=\"?\" line=\"?\"/>\r\n");
141         }
142 
143         if (properties) {
144             if (MDC.getMDCAdapter() instanceof BasicMDCAdapter) {
145                 BasicMDCAdapter mdcAdapter = (BasicMDCAdapter) MDC.getMDCAdapter();
146                 Set keySet = mdcAdapter.getKeys();
147                 if (keySet != null && keySet.size() > 0) {
148                     buf.append("<log4j:properties>\r\n");
149                     Object[] keys = keySet.toArray();
150                     Arrays.sort(keys);
151                     for (Object key1 : keys) {
152                         String key = key1.toString();
153                         Object val = mdcAdapter.get(key);
154                         if (val != null) {
155                             buf.append("<log4j:data name=\"");
156                             buf.append(Transform.escapeTags(key));
157                             buf.append("\" value=\"");
158                             buf.append(Transform.escapeTags(String.valueOf(val)));
159                             buf.append("\"/>\r\n");
160                         }
161                     }
162                     buf.append("</log4j:properties>\r\n");
163                 }
164             }
165         }
166         buf.append("</log4j:event>\r\n\r\n");
167 
168         return buf.toString();
169     }
170 
171 }