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 org.apache.logging.log4j.core.LogEvent;
020    import org.apache.logging.log4j.core.config.plugins.Plugin;
021    import org.apache.logging.log4j.core.config.plugins.PluginAttr;
022    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
023    import org.apache.logging.log4j.core.net.Facility;
024    import org.apache.logging.log4j.core.net.Priority;
025    import org.apache.logging.log4j.util.EnglishEnums;
026    
027    import java.net.InetAddress;
028    import java.net.UnknownHostException;
029    import java.nio.charset.Charset;
030    import java.text.SimpleDateFormat;
031    import java.util.Date;
032    import java.util.Locale;
033    
034    
035    /**
036     * Formats a log event as a BSD Log record.
037     */
038    @Plugin(name = "SyslogLayout", type = "Core", elementType = "layout", printObject = true)
039    public class SyslogLayout extends AbstractStringLayout {
040    
041        private final Facility facility;
042        private final boolean includeNewLine;
043    
044        /**
045         * Date format used if header = true.
046         */
047        private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss ", Locale.ENGLISH);
048        /**
049         * Host name used to identify messages from this appender.
050         */
051        private final String localHostname = getLocalHostname();
052    
053    
054        protected SyslogLayout(Facility facility, boolean includeNL, Charset c) {
055            super(c);
056            this.facility = facility;
057            this.includeNewLine = includeNL;
058        }
059    
060        /**
061         * Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the log4j.dtd.
062         *
063         * @param event The LogEvent
064         * @return the event formatted as a String.
065         */
066        public String toSerializable(final LogEvent event) {
067            StringBuilder buf = new StringBuilder();
068    
069            buf.append("<");
070            buf.append(Priority.getPriority(facility, event.getLevel()));
071            buf.append(">");
072            addDate(event.getMillis(), buf);
073            buf.append(" ");
074            buf.append(localHostname);
075            buf.append(" ");
076            buf.append(event.getMessage().getFormattedMessage());
077            if (includeNewLine) {
078                buf.append("\n");
079            }
080            return buf.toString();
081        }
082    
083        /**
084         * This method gets the network name of the machine we are running on.
085         * Returns "UNKNOWN_LOCALHOST" in the unlikely case where the host name
086         * cannot be found.
087         *
088         * @return String the name of the local host
089         */
090        private String getLocalHostname() {
091            try {
092                InetAddress addr = InetAddress.getLocalHost();
093                return addr.getHostName();
094            } catch (UnknownHostException uhe) {
095                LOGGER.error("Could not determine local host name", uhe);
096                return "UNKNOWN_LOCALHOST";
097            }
098        }
099    
100        private synchronized void addDate(final long timestamp, StringBuilder buf) {
101            int index = buf.length() + 4;
102            buf.append(dateFormat.format(new Date(timestamp)));
103            //  RFC 3164 says leading space, not leading zero on days 1-9
104            if (buf.charAt(index) == '0') {
105                buf.setCharAt(index, ' ');
106            }
107        }
108    
109        /**
110         * Create a SyslogLayout.
111         * @param facility The Facility is used to try to classify the message.
112         * @param includeNL If true a newline will be appended to the result.
113         * @param charset The character set.
114         * @return A SyslogLayout.
115         */
116        @PluginFactory
117        public static SyslogLayout createLayout(@PluginAttr("facility") String facility,
118                                                @PluginAttr("newLine") String includeNL,
119                                                @PluginAttr("charset") String charset) {
120    
121            Charset c = Charset.isSupported("UTF-8") ? Charset.forName("UTF-8") : Charset.defaultCharset();
122            if (charset != null) {
123                if (Charset.isSupported(charset)) {
124                    c = Charset.forName(charset);
125                } else {
126                    LOGGER.error("Charset " + charset + " is not supported for layout, using " + c.displayName());
127                }
128            }
129            boolean includeNewLine = includeNL == null ? false : Boolean.valueOf(includeNL);
130            Facility f = Facility.toFacility(facility, Facility.LOCAL0);
131            return new SyslogLayout(f, includeNewLine, c);
132        }
133    }