Index: core/src/main/java/org/apache/logging/log4j/core/layout/SyslogWrapLayout.java =================================================================== --- core/src/main/java/org/apache/logging/log4j/core/layout/SyslogWrapLayout.java (revision 0) +++ core/src/main/java/org/apache/logging/log4j/core/layout/SyslogWrapLayout.java (working copy) @@ -0,0 +1,198 @@ +/* + * 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.logging.log4j.core.layout; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttr; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.helpers.Charsets; +import org.apache.logging.log4j.core.layout.AbstractStringLayout; +import org.apache.logging.log4j.core.net.Facility; +import org.apache.logging.log4j.core.net.Priority; + +/** + * Layout wraps any layout and formats message from the wrapped layout into a syslog format + * + * layout based on SyslogLayout + * + * */ +@Plugin(name = "SyslogWrapLayout", category = "Core", elementType = "layout", printObject = true) +public class SyslogWrapLayout extends AbstractStringLayout { + /** + * Match newlines in a platform-independent manner. + */ + public static final Pattern NEWLINE_PATTERN = Pattern.compile("\\r?\\n"); + + private final Facility facility; + private final boolean includeNewLine; + private final String escapeNewLine; + private final boolean addHost; + + /** + * Date format used if header = true. + */ + private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss ", Locale.ENGLISH); + /** + * Host name used to identify messages from this appender. + */ + private final String localHostname = getLocalHostname(); + + protected Layout layout; + + protected SyslogWrapLayout(final Facility facility, final boolean includeNL, final String escapeNL, + final Charset c, final boolean addHost, Layout layout) { + super(c); + this.facility = facility; + this.includeNewLine = includeNL; + this.escapeNewLine = escapeNL == null ? null : Matcher.quoteReplacement(escapeNL); + this.addHost = addHost; + this.layout = layout; + } + + /** + * Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the BSD Log record format. + * + * @param event + * The LogEvent + * @return the event formatted as a String. + */ + @Override + public String toSerializable(final LogEvent event) { + final StringBuilder buf = new StringBuilder(); + + buf.append("<"); + buf.append(Priority.getPriority(facility, event.getLevel())); + buf.append(">"); + addDate(event.getMillis(), buf); + buf.append(" "); + + if (addHost) { + buf.append(localHostname); + buf.append(" "); + } + + String message; + if (layout == null) { + message = event.getMessage().getFormattedMessage(); + } else { + message = layout.toSerializable(event).toString(); + } + + if (null != escapeNewLine) { + message = NEWLINE_PATTERN.matcher(message).replaceAll(escapeNewLine); + } + buf.append(message); + + if (includeNewLine) { + buf.append("\n"); + } + return buf.toString(); + } + + /** + * This method gets the network name of the machine we are running on. Returns "UNKNOWN_LOCALHOST" in the unlikely + * case where the host name cannot be found. + * + * @return String the name of the local host + */ + private String getLocalHostname() { + try { + final InetAddress addr = InetAddress.getLocalHost(); + return addr.getHostName(); + } catch (final UnknownHostException uhe) { + LOGGER.error("Could not determine local host name", uhe); + return "UNKNOWN_LOCALHOST"; + } + } + + private synchronized void addDate(final long timestamp, final StringBuilder buf) { + final int index = buf.length() + 4; + buf.append(dateFormat.format(new Date(timestamp))); + // RFC 3164 says leading space, not leading zero on days 1-9 + if (buf.charAt(index) == '0') { + buf.setCharAt(index, ' '); + } + } + + /** + * SyslogLayout's content format is specified by: + *

+ * Key: "structured" Value: "false" + *

+ * Key: "dateFormat" Value: "MMM dd HH:mm:ss " + *

+ * Key: "format" Value: "TIMESTAMP PROP(HOSTNAME) MESSAGE" + *

+ * Key: "formatType" Value: "logfilepatternreceiver" (format uses the keywords supported by LogFilePatternReceiver) + * + * @return Map of content format keys supporting SyslogLayout + */ + @Override + public Map getContentFormat() { + Map result = new HashMap(); + result.put("structured", "false"); + result.put("formatType", "logfilepatternreceiver"); + result.put("dateFormat", dateFormat.toPattern()); + result.put("format", "TIMESTAMP PROP(HOSTNAME) MESSAGE"); + return result; + } + + /** + * Create a SyslogWrapLayout. + * + * @param facility + * The Facility is used to try to classify the message. + * @param includeNL + * If true a newline will be appended to the result. + * @param escapeNL + * Pattern to use for replacing newlines. + * @param charsetName + * The character set. + * @param addHost + * If false, host name is not added to the message. + * @param layout + * Wrapped layout eg. PatternLayout, can be null, than class acts as SyslogLayout + * @return A SyslogWrapLayout. + */ + @PluginFactory + public static SyslogWrapLayout createLayout(@PluginAttr("facility") final String facility, + @PluginAttr("newLine") final String includeNL, + @PluginAttr("newLineEscape") final String escapeNL, + @PluginAttr("charset") final String charsetName, + @PluginAttr("addHost") final String addHost, + @PluginElement("layout") Layout layout) { + final Charset charset = Charsets.getSupportedCharset(charsetName); + final boolean includeNewLine = includeNL == null ? false : Boolean.valueOf(includeNL); + final Facility f = Facility.toFacility(facility, Facility.LOCAL0); + final boolean addHostFlag = addHost == null ? false : Boolean.valueOf(addHost); + return new SyslogWrapLayout(f, includeNewLine, escapeNL, charset, addHostFlag, layout); + } +} \ No newline at end of file