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