| /* |
| * 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. |
| */ |
| package org.apache.log4j.layout; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.nio.charset.StandardCharsets; |
| import java.util.List; |
| |
| import org.apache.logging.log4j.core.Layout; |
| import org.apache.logging.log4j.core.LogEvent; |
| import org.apache.logging.log4j.core.layout.AbstractStringLayout; |
| import org.apache.logging.log4j.core.layout.ByteBufferDestination; |
| import org.apache.logging.log4j.core.util.Transform; |
| import org.apache.logging.log4j.plugins.Node; |
| import org.apache.logging.log4j.plugins.Plugin; |
| import org.apache.logging.log4j.plugins.PluginAttribute; |
| import org.apache.logging.log4j.plugins.PluginFactory; |
| import org.apache.logging.log4j.util.BiConsumer; |
| import org.apache.logging.log4j.util.ReadOnlyStringMap; |
| import org.apache.logging.log4j.util.Strings; |
| |
| /** |
| * Port of XMLLayout in Log4j 1.x. Provided for compatibility with existing Log4j 1 configurations. |
| * |
| * Originally developed by Ceki Gülcü, Mathias Bogaert. |
| */ |
| @Plugin(name = "Log4j1XmlLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true) |
| public final class Log4j1XmlLayout extends AbstractStringLayout { |
| |
| private final boolean locationInfo; |
| private final boolean properties; |
| |
| @PluginFactory |
| public static Log4j1XmlLayout createLayout( |
| // @formatter:off |
| @PluginAttribute final boolean locationInfo, |
| @PluginAttribute final boolean properties |
| // @formatter:on |
| ) { |
| return new Log4j1XmlLayout(locationInfo, properties); |
| } |
| |
| private Log4j1XmlLayout(final boolean locationInfo, final boolean properties) { |
| super(StandardCharsets.UTF_8); |
| this.locationInfo = locationInfo; |
| this.properties = properties; |
| } |
| |
| public boolean isLocationInfo() { |
| return locationInfo; |
| } |
| |
| public boolean isProperties() { |
| return properties; |
| } |
| |
| @Override |
| public void encode(final LogEvent event, final ByteBufferDestination destination) { |
| final StringBuilder text = getStringBuilder(); |
| formatTo(event, text); |
| getStringBuilderEncoder().encode(text, destination); |
| } |
| |
| @Override |
| public String toSerializable(final LogEvent event) { |
| final StringBuilder text = getStringBuilder(); |
| formatTo(event, text); |
| return text.toString(); |
| } |
| |
| private void formatTo(final LogEvent event, final StringBuilder buf) { |
| // We yield to the \r\n heresy. |
| |
| buf.append("<log4j:event logger=\""); |
| buf.append(Transform.escapeHtmlTags(event.getLoggerName())); |
| buf.append("\" timestamp=\""); |
| buf.append(event.getTimeMillis()); |
| buf.append("\" level=\""); |
| buf.append(Transform.escapeHtmlTags(String.valueOf(event.getLevel()))); |
| buf.append("\" thread=\""); |
| buf.append(Transform.escapeHtmlTags(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.getMessage().getFormattedMessage()); |
| buf.append("]]></log4j:message>\r\n"); |
| |
| final List<String> ndc = event.getContextStack().asList(); |
| if (!ndc.isEmpty()) { |
| buf.append("<log4j:NDC><![CDATA["); |
| Transform.appendEscapingCData(buf, Strings.join(ndc, ' ')); |
| buf.append("]]></log4j:NDC>\r\n"); |
| } |
| |
| @SuppressWarnings("ThrowableResultOfMethodCallIgnored") |
| final Throwable thrown = event.getThrown(); |
| if (thrown != null) { |
| buf.append("<log4j:throwable><![CDATA["); |
| final StringWriter w = new StringWriter(); |
| thrown.printStackTrace(new PrintWriter(w)); |
| Transform.appendEscapingCData(buf, w.toString()); |
| buf.append("]]></log4j:throwable>\r\n"); |
| } |
| |
| if (locationInfo) { |
| final StackTraceElement source = event.getSource(); |
| if (source != null) { |
| buf.append("<log4j:locationInfo class=\""); |
| buf.append(Transform.escapeHtmlTags(source.getClassName())); |
| buf.append("\" method=\""); |
| buf.append(Transform.escapeHtmlTags(source.getMethodName())); |
| buf.append("\" file=\""); |
| buf.append(Transform.escapeHtmlTags(source.getFileName())); |
| buf.append("\" line=\""); |
| buf.append(source.getLineNumber()); |
| buf.append("\"/>\r\n"); |
| } |
| } |
| |
| if (properties) { |
| final ReadOnlyStringMap contextMap = event.getContextData(); |
| if (!contextMap.isEmpty()) { |
| buf.append("<log4j:properties>\r\n"); |
| contextMap.forEach((BiConsumer<String, String>) (key, val) -> { |
| if (val != null) { |
| buf.append("<log4j:data name=\""); |
| buf.append(Transform.escapeHtmlTags(key)); |
| buf.append("\" value=\""); |
| buf.append(Transform.escapeHtmlTags(val)); |
| buf.append("\"/>\r\n"); |
| } |
| }); |
| buf.append("</log4j:properties>\r\n"); |
| } |
| } |
| |
| buf.append("</log4j:event>\r\n\r\n"); |
| } |
| |
| } |