View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.net;
15  
16  import java.io.IOException;
17  import java.io.OutputStream;
18  import java.net.SocketException;
19  import java.net.UnknownHostException;
20  import java.nio.charset.Charset;
21  
22  import ch.qos.logback.core.AppenderBase;
23  import ch.qos.logback.core.CoreConstants;
24  import ch.qos.logback.core.Layout;
25  
26  /**
27   * Base class for SyslogAppender.
28   * 
29   * @author Ceki Gülcü
30   * 
31   * @param <E>
32   */
33  public abstract class SyslogAppenderBase<E> extends AppenderBase<E> {
34  
35      final static String SYSLOG_LAYOUT_URL = CoreConstants.CODES_URL + "#syslog_layout";
36      final static int MAX_MESSAGE_SIZE_LIMIT = 65000;
37  
38      Layout<E> layout;
39      String facilityStr;
40      String syslogHost;
41      protected String suffixPattern;
42      SyslogOutputStream sos;
43      int port = SyslogConstants.SYSLOG_PORT;
44      int maxMessageSize;
45      Charset charset;
46  
47      public void start() {
48          int errorCount = 0;
49          if (facilityStr == null) {
50              addError("The Facility option is mandatory");
51              errorCount++;
52          }
53  
54          if (charset == null) {
55              // Using defaultCharset() preserves the previous behavior when String.getBytes() was
56              // called without arguments
57              charset = Charset.defaultCharset();
58          }
59  
60          try {
61              sos = createOutputStream();
62  
63              final int systemDatagramSize = sos.getSendBufferSize();
64              if (maxMessageSize == 0) {
65                  maxMessageSize = Math.min(systemDatagramSize, MAX_MESSAGE_SIZE_LIMIT);
66                  addInfo("Defaulting maxMessageSize to [" + maxMessageSize + "]");
67              } else if (maxMessageSize > systemDatagramSize) {
68                  addWarn("maxMessageSize of [" + maxMessageSize + "] is larger than the system defined datagram size of [" + systemDatagramSize + "].");
69                  addWarn("This may result in dropped logs.");
70              }
71          } catch (UnknownHostException e) {
72              addError("Could not create SyslogWriter", e);
73              errorCount++;
74          } catch (SocketException e) {
75              addWarn("Failed to bind to a random datagram socket. Will try to reconnect later.", e);
76          }
77  
78          if (layout == null) {
79              layout = buildLayout();
80          }
81  
82          if (errorCount == 0) {
83              super.start();
84          }
85      }
86  
87      abstract public SyslogOutputStream createOutputStream() throws UnknownHostException, SocketException;
88  
89      abstract public Layout<E> buildLayout();
90  
91      abstract public int getSeverityForEvent(Object eventObject);
92  
93      @Override
94      protected void append(E eventObject) {
95          if (!isStarted()) {
96              return;
97          }
98  
99          try {
100             String msg = layout.doLayout(eventObject);
101             if (msg == null) {
102                 return;
103             }
104             if (msg.length() > maxMessageSize) {
105                 msg = msg.substring(0, maxMessageSize);
106             }
107             sos.write(msg.getBytes(charset));
108             sos.flush();
109             postProcess(eventObject, sos);
110         } catch (IOException ioe) {
111             addError("Failed to send diagram to " + syslogHost, ioe);
112         }
113     }
114 
115     protected void postProcess(Object event, OutputStream sw) {
116 
117     }
118 
119     /**
120      * Returns the integer value corresponding to the named syslog facility.
121      * 
122      * @throws IllegalArgumentException
123      *           if the facility string is not recognized
124      */
125     static public int facilityStringToint(String facilityStr) {
126         if ("KERN".equalsIgnoreCase(facilityStr)) {
127             return SyslogConstants.LOG_KERN;
128         } else if ("USER".equalsIgnoreCase(facilityStr)) {
129             return SyslogConstants.LOG_USER;
130         } else if ("MAIL".equalsIgnoreCase(facilityStr)) {
131             return SyslogConstants.LOG_MAIL;
132         } else if ("DAEMON".equalsIgnoreCase(facilityStr)) {
133             return SyslogConstants.LOG_DAEMON;
134         } else if ("AUTH".equalsIgnoreCase(facilityStr)) {
135             return SyslogConstants.LOG_AUTH;
136         } else if ("SYSLOG".equalsIgnoreCase(facilityStr)) {
137             return SyslogConstants.LOG_SYSLOG;
138         } else if ("LPR".equalsIgnoreCase(facilityStr)) {
139             return SyslogConstants.LOG_LPR;
140         } else if ("NEWS".equalsIgnoreCase(facilityStr)) {
141             return SyslogConstants.LOG_NEWS;
142         } else if ("UUCP".equalsIgnoreCase(facilityStr)) {
143             return SyslogConstants.LOG_UUCP;
144         } else if ("CRON".equalsIgnoreCase(facilityStr)) {
145             return SyslogConstants.LOG_CRON;
146         } else if ("AUTHPRIV".equalsIgnoreCase(facilityStr)) {
147             return SyslogConstants.LOG_AUTHPRIV;
148         } else if ("FTP".equalsIgnoreCase(facilityStr)) {
149             return SyslogConstants.LOG_FTP;
150         } else if ("NTP".equalsIgnoreCase(facilityStr)) {
151             return SyslogConstants.LOG_NTP;
152         } else if ("AUDIT".equalsIgnoreCase(facilityStr)) {
153             return SyslogConstants.LOG_AUDIT;
154         } else if ("ALERT".equalsIgnoreCase(facilityStr)) {
155             return SyslogConstants.LOG_ALERT;
156         } else if ("CLOCK".equalsIgnoreCase(facilityStr)) {
157             return SyslogConstants.LOG_CLOCK;
158         } else if ("LOCAL0".equalsIgnoreCase(facilityStr)) {
159             return SyslogConstants.LOG_LOCAL0;
160         } else if ("LOCAL1".equalsIgnoreCase(facilityStr)) {
161             return SyslogConstants.LOG_LOCAL1;
162         } else if ("LOCAL2".equalsIgnoreCase(facilityStr)) {
163             return SyslogConstants.LOG_LOCAL2;
164         } else if ("LOCAL3".equalsIgnoreCase(facilityStr)) {
165             return SyslogConstants.LOG_LOCAL3;
166         } else if ("LOCAL4".equalsIgnoreCase(facilityStr)) {
167             return SyslogConstants.LOG_LOCAL4;
168         } else if ("LOCAL5".equalsIgnoreCase(facilityStr)) {
169             return SyslogConstants.LOG_LOCAL5;
170         } else if ("LOCAL6".equalsIgnoreCase(facilityStr)) {
171             return SyslogConstants.LOG_LOCAL6;
172         } else if ("LOCAL7".equalsIgnoreCase(facilityStr)) {
173             return SyslogConstants.LOG_LOCAL7;
174         } else {
175             throw new IllegalArgumentException(facilityStr + " is not a valid syslog facility string");
176         }
177     }
178 
179     /**
180      * Returns the value of the <b>SyslogHost</b> option.
181      */
182     public String getSyslogHost() {
183         return syslogHost;
184     }
185 
186     /**
187      * The <b>SyslogHost</b> option is the name of the the syslog host where log
188      * output should go.
189      * 
190      * <b>WARNING</b> If the SyslogHost is not set, then this appender will fail.
191      */
192     public void setSyslogHost(String syslogHost) {
193         this.syslogHost = syslogHost;
194     }
195 
196     /**
197      * Returns the string value of the <b>Facility</b> option.
198      * 
199      * See {@link #setFacility} for the set of allowed values.
200      */
201     public String getFacility() {
202         return facilityStr;
203     }
204 
205     /**
206      * The <b>Facility</b> option must be set one of the strings KERN, USER, MAIL,
207      * DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT,
208      * ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6,
209      * LOCAL7. Case is not important.
210      * 
211      * <p>
212      * See {@link ch.qos.logback.core.net.SyslogConstants SyslogConstants} and RFC 3164 for more information about the
213      * <b>Facility</b> option.
214      */
215     public void setFacility(String facilityStr) {
216         if (facilityStr != null) {
217             facilityStr = facilityStr.trim();
218         }
219         this.facilityStr = facilityStr;
220     }
221 
222     /**
223      * 
224      * @return
225      */
226     public int getPort() {
227         return port;
228     }
229 
230     /**
231      * The port number on the syslog server to connect to. Normally, you would not
232      * want to change the default value, that is 514.
233      */
234     public void setPort(int port) {
235         this.port = port;
236     }
237 
238     public int getMaxMessageSize() {
239         return maxMessageSize;
240     }
241 
242     /**
243      * Maximum size for the syslog message (in characters); messages
244      * longer than this are truncated. The default value is 65400 (which
245      * is near the maximum for syslog-over-UDP). Note that the value is
246      * characters; the number of bytes may vary if non-ASCII characters
247      * are present.
248      */
249     public void setMaxMessageSize(int maxMessageSize) {
250         this.maxMessageSize = maxMessageSize;
251     }
252 
253     public Layout<E> getLayout() {
254         return layout;
255     }
256 
257     public void setLayout(Layout<E> layout) {
258         addWarn("The layout of a SyslogAppender cannot be set directly. See also " + SYSLOG_LAYOUT_URL);
259     }
260 
261     @Override
262     public void stop() {
263         if (sos != null) {
264             sos.close();
265         }
266         super.stop();
267     }
268 
269     /**
270        * See {@link #setSuffixPattern #setSuffixPattern(String)}.
271        * 
272        * @return
273        */
274     public String getSuffixPattern() {
275         return suffixPattern;
276     }
277 
278     /**
279      * The <b>suffixPattern</b> option specifies the format of the
280      * non-standardized part of the message sent to the syslog server.
281      * 
282      * @param suffixPattern
283      */
284     public void setSuffixPattern(String suffixPattern) {
285         this.suffixPattern = suffixPattern;
286     }
287 
288     /**
289      * Returns the Charset used to encode String messages into byte sequences when writing to
290      * syslog.
291      */
292     public Charset getCharset() {
293         return charset;
294     }
295 
296     /**
297      * The Charset to use when encoding messages into byte sequences.
298      *
299      * @param charset
300      */
301     public void setCharset(Charset charset) {
302         this.charset = charset;
303     }
304 }