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 */
017package org.apache.logging.log4j.core.appender;
018
019import java.io.Serializable;
020import java.nio.charset.Charset;
021import java.nio.charset.StandardCharsets;
022
023import org.apache.logging.log4j.core.Appender;
024import org.apache.logging.log4j.core.Core;
025import org.apache.logging.log4j.core.Filter;
026import org.apache.logging.log4j.core.Layout;
027import org.apache.logging.log4j.core.config.Configuration;
028import org.apache.logging.log4j.core.config.plugins.Plugin;
029import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
030import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
031import org.apache.logging.log4j.core.config.plugins.PluginElement;
032import org.apache.logging.log4j.core.layout.LoggerFields;
033import org.apache.logging.log4j.core.layout.Rfc5424Layout;
034import org.apache.logging.log4j.core.layout.SyslogLayout;
035import org.apache.logging.log4j.core.net.AbstractSocketManager;
036import org.apache.logging.log4j.core.net.Advertiser;
037import org.apache.logging.log4j.core.net.Facility;
038import org.apache.logging.log4j.core.net.Protocol;
039import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
040import org.apache.logging.log4j.core.util.Constants;
041import org.apache.logging.log4j.util.EnglishEnums;
042
043/**
044 * The Syslog Appender.
045 */
046@Plugin(name = "Syslog", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
047public class SyslogAppender extends SocketAppender {
048
049    public static class Builder<B extends Builder<B>> extends AbstractBuilder<B>
050            implements org.apache.logging.log4j.core.util.Builder<SocketAppender> {
051
052        @PluginBuilderAttribute(value = "facility")
053        private Facility facility = Facility.LOCAL0;
054
055        @PluginBuilderAttribute("id")
056        private String id;
057        
058        @PluginBuilderAttribute(value = "enterpriseNumber")
059        private int enterpriseNumber = Rfc5424Layout.DEFAULT_ENTERPRISE_NUMBER;
060        
061        @PluginBuilderAttribute(value = "includeMdc")
062        private boolean includeMdc = true;
063        
064        @PluginBuilderAttribute("mdcId")
065        private String mdcId;
066        
067        @PluginBuilderAttribute("mdcPrefix")
068        private String mdcPrefix;
069        
070        @PluginBuilderAttribute("eventPrefix")
071        private String eventPrefix;
072        
073        @PluginBuilderAttribute(value = "newLine")
074        private boolean newLine;
075        
076        @PluginBuilderAttribute("newLineEscape")
077        private String escapeNL;
078        
079        @PluginBuilderAttribute("appName")
080        private String appName;
081        
082        @PluginBuilderAttribute("messageId")
083        private String msgId;
084        
085        @PluginBuilderAttribute("mdcExcludes")
086        private String excludes;
087        
088        @PluginBuilderAttribute("mdcIncludes")
089        private String includes;
090        
091        @PluginBuilderAttribute("mdcRequired")
092        private String required;
093        
094        @PluginBuilderAttribute("format")
095        private String format;
096        
097        @PluginBuilderAttribute("charset")
098        private Charset charsetName = StandardCharsets.UTF_8;
099        
100        @PluginBuilderAttribute("exceptionPattern")
101        private String exceptionPattern;
102        
103        @PluginElement("LoggerFields")
104        private LoggerFields[] loggerFields;
105
106        @SuppressWarnings({"resource", "unchecked"})
107        @Override
108        public SyslogAppender build() {
109            final Protocol protocol = getProtocol();
110            final SslConfiguration sslConfiguration = getSslConfiguration();
111            final boolean useTlsMessageFormat = sslConfiguration != null || protocol == Protocol.SSL;
112            final Configuration configuration = getConfiguration();
113            Layout<? extends Serializable> layout = getLayout();
114            if (layout == null) {
115                layout = RFC5424.equalsIgnoreCase(format)
116                        ? Rfc5424Layout.createLayout(facility, id, enterpriseNumber, includeMdc, mdcId, mdcPrefix,
117                                eventPrefix, newLine, escapeNL, appName, msgId, excludes, includes, required,
118                                exceptionPattern, useTlsMessageFormat, loggerFields, configuration)
119                        :
120                        // @formatter:off
121                        SyslogLayout.newBuilder()
122                            .setFacility(facility)
123                            .setIncludeNewLine(newLine)
124                            .setEscapeNL(escapeNL)
125                            .setCharset(charsetName)
126                            .build();
127                        // @formatter:off
128            }
129            final String name = getName();
130            if (name == null) {
131                LOGGER.error("No name provided for SyslogAppender");
132                return null;
133            }
134            final AbstractSocketManager manager = createSocketManager(name, protocol, getHost(), getPort(), getConnectTimeoutMillis(),
135                    sslConfiguration, getReconnectDelayMillis(), getImmediateFail(), layout, Constants.ENCODER_BYTE_BUFFER_SIZE, null);
136
137            return new SyslogAppender(name, layout, getFilter(), isIgnoreExceptions(), isImmediateFlush(), manager,
138                    getAdvertise() ? configuration.getAdvertiser() : null);
139        }
140
141        public Facility getFacility() {
142            return facility;
143        }
144
145        public String getId() {
146            return id;
147        }
148
149        public int getEnterpriseNumber() {
150            return enterpriseNumber;
151        }
152
153        public boolean isIncludeMdc() {
154            return includeMdc;
155        }
156
157        public String getMdcId() {
158            return mdcId;
159        }
160
161        public String getMdcPrefix() {
162            return mdcPrefix;
163        }
164
165        public String getEventPrefix() {
166            return eventPrefix;
167        }
168
169        public boolean isNewLine() {
170            return newLine;
171        }
172
173        public String getEscapeNL() {
174            return escapeNL;
175        }
176
177        public String getAppName() {
178            return appName;
179        }
180
181        public String getMsgId() {
182            return msgId;
183        }
184
185        public String getExcludes() {
186            return excludes;
187        }
188
189        public String getIncludes() {
190            return includes;
191        }
192
193        public String getRequired() {
194            return required;
195        }
196
197        public String getFormat() {
198            return format;
199        }
200
201        public Charset getCharsetName() {
202            return charsetName;
203        }
204
205        public String getExceptionPattern() {
206            return exceptionPattern;
207        }
208
209        public LoggerFields[] getLoggerFields() {
210            return loggerFields;
211        }
212
213        public B setFacility(final Facility facility) {
214            this.facility = facility;
215            return asBuilder();
216        }
217
218        public B setId(final String id) {
219            this.id = id;
220            return asBuilder();
221        }
222
223        public B setEnterpriseNumber(final int enterpriseNumber) {
224            this.enterpriseNumber = enterpriseNumber;
225            return asBuilder();
226        }
227
228        public B setIncludeMdc(final boolean includeMdc) {
229            this.includeMdc = includeMdc;
230            return asBuilder();
231        }
232
233        public B setMdcId(final String mdcId) {
234            this.mdcId = mdcId;
235            return asBuilder();
236        }
237
238        public B setMdcPrefix(final String mdcPrefix) {
239            this.mdcPrefix = mdcPrefix;
240            return asBuilder();
241        }
242
243        public B setEventPrefix(final String eventPrefix) {
244            this.eventPrefix = eventPrefix;
245            return asBuilder();
246        }
247
248        public B setNewLine(final boolean newLine) {
249            this.newLine = newLine;
250            return asBuilder();
251        }
252
253        public B setEscapeNL(final String escapeNL) {
254            this.escapeNL = escapeNL;
255            return asBuilder();
256        }
257
258        public B setAppName(final String appName) {
259            this.appName = appName;
260            return asBuilder();
261        }
262
263        public B setMsgId(final String msgId) {
264            this.msgId = msgId;
265            return asBuilder();
266        }
267
268        public B setExcludes(final String excludes) {
269            this.excludes = excludes;
270            return asBuilder();
271        }
272
273        public B setIncludes(final String includes) {
274            this.includes = includes;
275            return asBuilder();
276        }
277
278        public B setRequired(final String required) {
279            this.required = required;
280            return asBuilder();
281        }
282
283        public B setFormat(final String format) {
284            this.format = format;
285            return asBuilder();
286        }
287
288        public B setCharsetName(final Charset charset) {
289            this.charsetName = charset;
290            return asBuilder();
291        }
292
293        public B setExceptionPattern(final String exceptionPattern) {
294            this.exceptionPattern = exceptionPattern;
295            return asBuilder();
296        }
297
298        public B setLoggerFields(final LoggerFields[] loggerFields) {
299            this.loggerFields = loggerFields;
300            return asBuilder();
301        }
302    }
303    
304    protected static final String RFC5424 = "RFC5424";
305
306    protected SyslogAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
307                             final boolean ignoreExceptions, final boolean immediateFlush,
308                             final AbstractSocketManager manager, final Advertiser advertiser) {
309        super(name, layout, filter, manager, ignoreExceptions, immediateFlush, advertiser);
310
311    }
312
313    /**
314     * Creates a SyslogAppender.
315     * @param host The name of the host to connect to.
316     * @param port The port to connect to on the target host.
317     * @param protocolStr The Protocol to use.
318     * @param sslConfiguration TODO
319     * @param connectTimeoutMillis the connect timeout in milliseconds.
320     * @param reconnectDelayMillis The interval in which failed writes should be retried.
321     * @param immediateFail True if the write should fail if no socket is immediately available.
322     * @param name The name of the Appender.
323     * @param immediateFlush "true" if data should be flushed on each write.
324     * @param ignoreExceptions If {@code "true"} (default) exceptions encountered when appending events are logged;
325     *                         otherwise they are propagated to the caller.
326     * @param facility The Facility is used to try to classify the message.
327     * @param id The default structured data id to use when formatting according to RFC 5424.
328     * @param enterpriseNumber The IANA enterprise number.
329     * @param includeMdc Indicates whether data from the ThreadContextMap will be included in the RFC 5424 Syslog
330     * record. Defaults to "true:.
331     * @param mdcId The id to use for the MDC Structured Data Element.
332     * @param mdcPrefix The prefix to add to MDC key names.
333     * @param eventPrefix The prefix to add to event key names.
334     * @param newLine If true, a newline will be appended to the end of the syslog record. The default is false.
335     * @param escapeNL String that should be used to replace newlines within the message text.
336     * @param appName The value to use as the APP-NAME in the RFC 5424 syslog record.
337     * @param msgId The default value to be used in the MSGID field of RFC 5424 syslog records.
338     * @param excludes A comma separated list of mdc keys that should be excluded from the LogEvent.
339     * @param includes A comma separated list of mdc keys that should be included in the FlumeEvent.
340     * @param required A comma separated list of mdc keys that must be present in the MDC.
341     * @param format If set to "RFC5424" the data will be formatted in accordance with RFC 5424. Otherwise,
342     * it will be formatted as a BSD Syslog record.
343     * @param filter A Filter to determine if the event should be handled by this Appender.
344     * @param configuration The Configuration.
345     * @param charset The character set to use when converting the syslog String to a byte array.
346     * @param exceptionPattern The converter pattern to use for formatting exceptions.
347     * @param loggerFields The logger fields
348     * @param advertise Whether to advertise
349     * @return A SyslogAppender.
350     * @deprecated Use {@link #newSyslogAppenderBuilder()}.
351     */
352    @Deprecated
353    public static <B extends Builder<B>> SyslogAppender createAppender(
354            // @formatter:off
355            final String host,
356            final int port,
357            final String protocolStr,
358            final SslConfiguration sslConfiguration,
359            final int connectTimeoutMillis,
360            final int reconnectDelayMillis,
361            final boolean immediateFail,
362            final String name,
363            final boolean immediateFlush,
364            final boolean ignoreExceptions,
365            final Facility facility,
366            final String id,
367            final int enterpriseNumber,
368            final boolean includeMdc,
369            final String mdcId,
370            final String mdcPrefix,
371            final String eventPrefix,
372            final boolean newLine,
373            final String escapeNL,
374            final String appName,
375            final String msgId,
376            final String excludes,
377            final String includes,
378            final String required,
379            final String format,
380            final Filter filter,
381            final Configuration configuration,
382            final Charset charset,
383            final String exceptionPattern,
384            final LoggerFields[] loggerFields, 
385            final boolean advertise) {
386        // @formatter:on
387
388        // @formatter:off
389        return SyslogAppender.<B>newSyslogAppenderBuilder()
390                .withHost(host)
391                .withPort(port)
392                .withProtocol(EnglishEnums.valueOf(Protocol.class, protocolStr))
393                .withSslConfiguration(sslConfiguration)
394                .withConnectTimeoutMillis(connectTimeoutMillis)
395                .withReconnectDelayMillis(reconnectDelayMillis)
396                .withImmediateFail(immediateFail)
397                .withName(appName)
398                .withImmediateFlush(immediateFlush)
399                .withIgnoreExceptions(ignoreExceptions)
400                .withFilter(filter)
401                .setConfiguration(configuration)
402                .withAdvertise(advertise)
403                .setFacility(facility)
404                .setId(id)
405                .setEnterpriseNumber(enterpriseNumber)
406                .setIncludeMdc(includeMdc)
407                .setMdcId(mdcId)
408                .setMdcPrefix(mdcPrefix)
409                .setEventPrefix(eventPrefix)
410                .setNewLine(newLine)
411                .setAppName(appName)
412                .setMsgId(msgId)
413                .setExcludes(excludes)
414                .setIncludeMdc(includeMdc)
415                .setRequired(required)
416                .setFormat(format)
417                .setCharsetName(charset)
418                .setExceptionPattern(exceptionPattern)
419                .setLoggerFields(loggerFields)
420                .build();
421        // @formatter:on
422    }
423    
424    // Calling this method newBuilder() does not compile
425    @PluginBuilderFactory
426    public static <B extends Builder<B>> B newSyslogAppenderBuilder() {
427        return new Builder<B>().asBuilder();
428    }
429
430}