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  package org.apache.log4j.layout;
18  
19  import java.io.PrintWriter;
20  import java.io.StringWriter;
21  import java.nio.charset.StandardCharsets;
22  import java.util.List;
23  
24  import org.apache.logging.log4j.core.Layout;
25  import org.apache.logging.log4j.core.LogEvent;
26  import org.apache.logging.log4j.core.config.Node;
27  import org.apache.logging.log4j.core.config.plugins.Plugin;
28  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
29  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
30  import org.apache.logging.log4j.core.layout.AbstractStringLayout;
31  import org.apache.logging.log4j.core.layout.ByteBufferDestination;
32  import org.apache.logging.log4j.core.util.Transform;
33  import org.apache.logging.log4j.util.BiConsumer;
34  import org.apache.logging.log4j.util.ReadOnlyStringMap;
35  import org.apache.logging.log4j.util.Strings;
36  
37  /**
38   * Port of XMLLayout in Log4j 1.x. Provided for compatibility with existing Log4j 1 configurations.
39   *
40   * Originally developed by Ceki Gülcü, Mathias Bogaert.
41   */
42  @Plugin(name = "Log4j1XmlLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
43  public final class Log4j1XmlLayout extends AbstractStringLayout {
44  
45      private final boolean locationInfo;
46      private final boolean properties;
47  
48      @PluginFactory
49      public static Log4j1XmlLayout createLayout(
50              // @formatter:off
51              @PluginAttribute(value = "locationInfo") final boolean locationInfo,
52              @PluginAttribute(value = "properties") final boolean properties
53              // @formatter:on
54      ) {
55          return new Log4j1XmlLayout(locationInfo, properties);
56      }
57  
58      private Log4j1XmlLayout(final boolean locationInfo, final boolean properties) {
59          super(StandardCharsets.UTF_8);
60          this.locationInfo = locationInfo;
61          this.properties = properties;
62      }
63  
64      public boolean isLocationInfo() {
65          return locationInfo;
66      }
67  
68      public boolean isProperties() {
69          return properties;
70      }
71  
72      @Override
73      public void encode(final LogEvent event, final ByteBufferDestination destination) {
74          final StringBuilder text = getStringBuilder();
75          formatTo(event, text);
76          getStringBuilderEncoder().encode(text, destination);
77      }
78  
79      @Override
80      public String toSerializable(final LogEvent event) {
81          final StringBuilder text = getStringBuilder();
82          formatTo(event, text);
83          return text.toString();
84      }
85  
86      private void formatTo(final LogEvent event, final StringBuilder buf) {
87          // We yield to the \r\n heresy.
88  
89          buf.append("<log4j:event logger=\"");
90          buf.append(Transform.escapeHtmlTags(event.getLoggerName()));
91          buf.append("\" timestamp=\"");
92          buf.append(event.getTimeMillis());
93          buf.append("\" level=\"");
94          buf.append(Transform.escapeHtmlTags(String.valueOf(event.getLevel())));
95          buf.append("\" thread=\"");
96          buf.append(Transform.escapeHtmlTags(event.getThreadName()));
97          buf.append("\">\r\n");
98  
99          buf.append("<log4j:message><![CDATA[");
100         // Append the rendered message. Also make sure to escape any existing CDATA sections.
101         Transform.appendEscapingCData(buf, event.getMessage().getFormattedMessage());
102         buf.append("]]></log4j:message>\r\n");
103 
104         final List<String> ndc = event.getContextStack().asList();
105         if (!ndc.isEmpty()) {
106             buf.append("<log4j:NDC><![CDATA[");
107             Transform.appendEscapingCData(buf, Strings.join(ndc, ' '));
108             buf.append("]]></log4j:NDC>\r\n");
109         }
110 
111         @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
112 		final Throwable thrown = event.getThrown();
113         if (thrown != null) {
114             buf.append("<log4j:throwable><![CDATA[");
115             final StringWriter w = new StringWriter();
116             thrown.printStackTrace(new PrintWriter(w));
117             Transform.appendEscapingCData(buf, w.toString());
118             buf.append("]]></log4j:throwable>\r\n");
119         }
120 
121         if (locationInfo) {
122             final StackTraceElement source = event.getSource();
123             if (source != null) {
124                 buf.append("<log4j:locationInfo class=\"");
125                 buf.append(Transform.escapeHtmlTags(source.getClassName()));
126                 buf.append("\" method=\"");
127                 buf.append(Transform.escapeHtmlTags(source.getMethodName()));
128                 buf.append("\" file=\"");
129                 buf.append(Transform.escapeHtmlTags(source.getFileName()));
130                 buf.append("\" line=\"");
131                 buf.append(source.getLineNumber());
132                 buf.append("\"/>\r\n");
133             }
134         }
135 
136         if (properties) {
137             final ReadOnlyStringMap contextMap = event.getContextData();
138             if (!contextMap.isEmpty()) {
139                 buf.append("<log4j:properties>\r\n");
140                 contextMap.forEach(new BiConsumer<String, String>() {
141                     @Override
142                     public void accept(final String key, final String val) {
143                         if (val != null) {
144                             buf.append("<log4j:data name=\"");
145                             buf.append(Transform.escapeHtmlTags(key));
146                             buf.append("\" value=\"");
147                             buf.append(Transform.escapeHtmlTags(val));
148                             buf.append("\"/>\r\n");
149                         }
150                     }
151                 });
152                 buf.append("</log4j:properties>\r\n");
153             }
154         }
155 
156         buf.append("</log4j:event>\r\n\r\n");
157     }
158 
159 }