001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017 package org.apache.logging.log4j.core.layout;
018
019 import java.util.HashMap;
020 import java.util.Map;
021 import org.apache.logging.log4j.core.LogEvent;
022 import org.apache.logging.log4j.core.config.plugins.Plugin;
023 import org.apache.logging.log4j.core.config.plugins.PluginAttr;
024 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
025 import org.apache.logging.log4j.core.helpers.Charsets;
026 import org.apache.logging.log4j.core.net.Facility;
027 import org.apache.logging.log4j.core.net.Priority;
028
029 import java.net.InetAddress;
030 import java.net.UnknownHostException;
031 import java.nio.charset.Charset;
032 import java.text.SimpleDateFormat;
033 import java.util.Date;
034 import java.util.Locale;
035 import java.util.regex.Matcher;
036 import java.util.regex.Pattern;
037
038
039 /**
040 * Formats a log event as a BSD Log record.
041 */
042 @Plugin(name = "SyslogLayout", category = "Core", elementType = "layout", printObject = true)
043 public class SyslogLayout extends AbstractStringLayout {
044 /**
045 * Match newlines in a platform-independent manner.
046 */
047 public static final Pattern NEWLINE_PATTERN = Pattern.compile("\\r?\\n");
048
049 private final Facility facility;
050 private final boolean includeNewLine;
051 private final String escapeNewLine;
052
053 /**
054 * Date format used if header = true.
055 */
056 private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss ", Locale.ENGLISH);
057 /**
058 * Host name used to identify messages from this appender.
059 */
060 private final String localHostname = getLocalHostname();
061
062
063
064 protected SyslogLayout(final Facility facility, final boolean includeNL, final String escapeNL, final Charset c) {
065 super(c);
066 this.facility = facility;
067 this.includeNewLine = includeNL;
068 this.escapeNewLine = escapeNL == null ? null : Matcher.quoteReplacement(escapeNL);
069 }
070
071 /**
072 * Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the BSD Log record format.
073 *
074 * @param event The LogEvent
075 * @return the event formatted as a String.
076 */
077 @Override
078 public String toSerializable(final LogEvent event) {
079 final StringBuilder buf = new StringBuilder();
080
081 buf.append("<");
082 buf.append(Priority.getPriority(facility, event.getLevel()));
083 buf.append(">");
084 addDate(event.getMillis(), buf);
085 buf.append(" ");
086 buf.append(localHostname);
087 buf.append(" ");
088
089 String message = event.getMessage().getFormattedMessage();
090 if (null != escapeNewLine) {
091 message = NEWLINE_PATTERN.matcher(message).replaceAll(escapeNewLine);
092 }
093 buf.append(message);
094
095 if (includeNewLine) {
096 buf.append("\n");
097 }
098 return buf.toString();
099 }
100
101 /**
102 * This method gets the network name of the machine we are running on.
103 * Returns "UNKNOWN_LOCALHOST" in the unlikely case where the host name
104 * cannot be found.
105 *
106 * @return String the name of the local host
107 */
108 private String getLocalHostname() {
109 try {
110 final InetAddress addr = InetAddress.getLocalHost();
111 return addr.getHostName();
112 } catch (final UnknownHostException uhe) {
113 LOGGER.error("Could not determine local host name", uhe);
114 return "UNKNOWN_LOCALHOST";
115 }
116 }
117
118 private synchronized void addDate(final long timestamp, final StringBuilder buf) {
119 final int index = buf.length() + 4;
120 buf.append(dateFormat.format(new Date(timestamp)));
121 // RFC 3164 says leading space, not leading zero on days 1-9
122 if (buf.charAt(index) == '0') {
123 buf.setCharAt(index, ' ');
124 }
125 }
126
127 /**
128 * SyslogLayout's content format is specified by:<p/>
129 * Key: "structured" Value: "false"<p/>
130 * Key: "dateFormat" Value: "MMM dd HH:mm:ss "<p/>
131 * Key: "format" Value: "<LEVEL>TIMESTAMP PROP(HOSTNAME) MESSAGE"<p/>
132 * Key: "formatType" Value: "logfilepatternreceiver" (format uses the keywords supported by LogFilePatternReceiver)
133 * @return Map of content format keys supporting SyslogLayout
134 */
135 @Override
136 public Map<String, String> getContentFormat()
137 {
138 Map<String, String> result = new HashMap<String, String>();
139 result.put("structured", "false");
140 result.put("formatType", "logfilepatternreceiver");
141 result.put("dateFormat", dateFormat.toPattern());
142 result.put("format", "<LEVEL>TIMESTAMP PROP(HOSTNAME) MESSAGE");
143 return result;
144 }
145
146 /**
147 * Create a SyslogLayout.
148 * @param facility The Facility is used to try to classify the message.
149 * @param includeNL If true a newline will be appended to the result.
150 * @param escapeNL Pattern to use for replacing newlines.
151 * @param charsetName The character set.
152 * @return A SyslogLayout.
153 */
154 @PluginFactory
155 public static SyslogLayout createLayout(@PluginAttr("facility") final String facility,
156 @PluginAttr("newLine") final String includeNL,
157 @PluginAttr("newLineEscape") final String escapeNL,
158 @PluginAttr("charset") final String charsetName) {
159 final Charset charset = Charsets.getSupportedCharset(charsetName);
160 final boolean includeNewLine = includeNL == null ? false : Boolean.valueOf(includeNL);
161 final Facility f = Facility.toFacility(facility, Facility.LOCAL0);
162 return new SyslogLayout(f, includeNewLine, escapeNL, charset);
163 }
164 }