001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.net; 015 016import java.io.IOException; 017import java.io.OutputStream; 018import java.net.SocketException; 019import java.net.UnknownHostException; 020import java.nio.charset.Charset; 021 022import ch.qos.logback.core.AppenderBase; 023import ch.qos.logback.core.CoreConstants; 024import ch.qos.logback.core.Layout; 025 026/** 027 * Base class for SyslogAppender. 028 * 029 * @author Ceki Gülcü 030 * 031 * @param <E> 032 */ 033public abstract class SyslogAppenderBase<E> extends AppenderBase<E> { 034 035 final static String SYSLOG_LAYOUT_URL = CoreConstants.CODES_URL + "#syslog_layout"; 036 final static int MAX_MESSAGE_SIZE_LIMIT = 65000; 037 038 Layout<E> layout; 039 String facilityStr; 040 String syslogHost; 041 protected String suffixPattern; 042 SyslogOutputStream sos; 043 int port = SyslogConstants.SYSLOG_PORT; 044 int maxMessageSize; 045 Charset charset; 046 047 public void start() { 048 int errorCount = 0; 049 if (facilityStr == null) { 050 addError("The Facility option is mandatory"); 051 errorCount++; 052 } 053 054 if (charset == null) { 055 // Using defaultCharset() preserves the previous behavior when String.getBytes() 056 // was 057 // called without arguments 058 charset = Charset.defaultCharset(); 059 } 060 061 try { 062 sos = createOutputStream(); 063 064 final int systemDatagramSize = sos.getSendBufferSize(); 065 if (maxMessageSize == 0) { 066 maxMessageSize = Math.min(systemDatagramSize, MAX_MESSAGE_SIZE_LIMIT); 067 addInfo("Defaulting maxMessageSize to [" + maxMessageSize + "]"); 068 } else if (maxMessageSize > systemDatagramSize) { 069 addWarn("maxMessageSize of [" + maxMessageSize 070 + "] is larger than the system defined datagram size of [" + systemDatagramSize + "]."); 071 addWarn("This may result in dropped logs."); 072 } 073 } catch (UnknownHostException e) { 074 addError("Could not create SyslogWriter", e); 075 errorCount++; 076 } catch (SocketException e) { 077 addWarn("Failed to bind to a random datagram socket. Will try to reconnect later.", e); 078 } 079 080 if (layout == null) { 081 layout = buildLayout(); 082 } 083 084 if (errorCount == 0) { 085 super.start(); 086 } 087 } 088 089 abstract public SyslogOutputStream createOutputStream() throws UnknownHostException, SocketException; 090 091 abstract public Layout<E> buildLayout(); 092 093 abstract public int getSeverityForEvent(Object eventObject); 094 095 @Override 096 protected void append(E eventObject) { 097 if (!isStarted()) { 098 return; 099 } 100 101 try { 102 String msg = layout.doLayout(eventObject); 103 if (msg == null) { 104 return; 105 } 106 if (msg.length() > maxMessageSize) { 107 msg = msg.substring(0, maxMessageSize); 108 } 109 sos.write(msg.getBytes(charset)); 110 sos.flush(); 111 postProcess(eventObject, sos); 112 } catch (IOException ioe) { 113 addError("Failed to send diagram to " + syslogHost, ioe); 114 } 115 } 116 117 protected void postProcess(Object event, OutputStream sw) { 118 119 } 120 121 /** 122 * Returns the integer value corresponding to the named syslog facility. 123 * 124 * @throws IllegalArgumentException if the facility string is not recognized 125 */ 126 static public int facilityStringToint(String facilityStr) { 127 if ("KERN".equalsIgnoreCase(facilityStr)) { 128 return SyslogConstants.LOG_KERN; 129 } else if ("USER".equalsIgnoreCase(facilityStr)) { 130 return SyslogConstants.LOG_USER; 131 } else if ("MAIL".equalsIgnoreCase(facilityStr)) { 132 return SyslogConstants.LOG_MAIL; 133 } else if ("DAEMON".equalsIgnoreCase(facilityStr)) { 134 return SyslogConstants.LOG_DAEMON; 135 } else if ("AUTH".equalsIgnoreCase(facilityStr)) { 136 return SyslogConstants.LOG_AUTH; 137 } else if ("SYSLOG".equalsIgnoreCase(facilityStr)) { 138 return SyslogConstants.LOG_SYSLOG; 139 } else if ("LPR".equalsIgnoreCase(facilityStr)) { 140 return SyslogConstants.LOG_LPR; 141 } else if ("NEWS".equalsIgnoreCase(facilityStr)) { 142 return SyslogConstants.LOG_NEWS; 143 } else if ("UUCP".equalsIgnoreCase(facilityStr)) { 144 return SyslogConstants.LOG_UUCP; 145 } else if ("CRON".equalsIgnoreCase(facilityStr)) { 146 return SyslogConstants.LOG_CRON; 147 } else if ("AUTHPRIV".equalsIgnoreCase(facilityStr)) { 148 return SyslogConstants.LOG_AUTHPRIV; 149 } else if ("FTP".equalsIgnoreCase(facilityStr)) { 150 return SyslogConstants.LOG_FTP; 151 } else if ("NTP".equalsIgnoreCase(facilityStr)) { 152 return SyslogConstants.LOG_NTP; 153 } else if ("AUDIT".equalsIgnoreCase(facilityStr)) { 154 return SyslogConstants.LOG_AUDIT; 155 } else if ("ALERT".equalsIgnoreCase(facilityStr)) { 156 return SyslogConstants.LOG_ALERT; 157 } else if ("CLOCK".equalsIgnoreCase(facilityStr)) { 158 return SyslogConstants.LOG_CLOCK; 159 } else if ("LOCAL0".equalsIgnoreCase(facilityStr)) { 160 return SyslogConstants.LOG_LOCAL0; 161 } else if ("LOCAL1".equalsIgnoreCase(facilityStr)) { 162 return SyslogConstants.LOG_LOCAL1; 163 } else if ("LOCAL2".equalsIgnoreCase(facilityStr)) { 164 return SyslogConstants.LOG_LOCAL2; 165 } else if ("LOCAL3".equalsIgnoreCase(facilityStr)) { 166 return SyslogConstants.LOG_LOCAL3; 167 } else if ("LOCAL4".equalsIgnoreCase(facilityStr)) { 168 return SyslogConstants.LOG_LOCAL4; 169 } else if ("LOCAL5".equalsIgnoreCase(facilityStr)) { 170 return SyslogConstants.LOG_LOCAL5; 171 } else if ("LOCAL6".equalsIgnoreCase(facilityStr)) { 172 return SyslogConstants.LOG_LOCAL6; 173 } else if ("LOCAL7".equalsIgnoreCase(facilityStr)) { 174 return SyslogConstants.LOG_LOCAL7; 175 } else { 176 throw new IllegalArgumentException(facilityStr + " is not a valid syslog facility string"); 177 } 178 } 179 180 /** 181 * Returns the value of the <b>SyslogHost</b> option. 182 */ 183 public String getSyslogHost() { 184 return syslogHost; 185 } 186 187 /** 188 * The <b>SyslogHost</b> option is the name of the syslog host where log 189 * output should go. 190 * 191 * <b>WARNING</b> If the SyslogHost is not set, then this appender will fail. 192 */ 193 public void setSyslogHost(String syslogHost) { 194 this.syslogHost = syslogHost; 195 } 196 197 /** 198 * Returns the string value of the <b>Facility</b> option. 199 * 200 * See {@link #setFacility} for the set of allowed values. 201 */ 202 public String getFacility() { 203 return facilityStr; 204 } 205 206 /** 207 * The <b>Facility</b> option must be set one of the strings KERN, USER, MAIL, 208 * DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT, 209 * ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. 210 * Case is not important. 211 * 212 * <p> 213 * See {@link ch.qos.logback.core.net.SyslogConstants SyslogConstants} and RFC 214 * 3164 for more information about the <b>Facility</b> option. 215 */ 216 public void setFacility(String facilityStr) { 217 if (facilityStr != null) { 218 facilityStr = facilityStr.trim(); 219 } 220 this.facilityStr = facilityStr; 221 } 222 223 /** 224 * 225 * @return 226 */ 227 public int getPort() { 228 return port; 229 } 230 231 /** 232 * The port number on the syslog server to connect to. Normally, you would not 233 * want to change the default value, that is 514. 234 */ 235 public void setPort(int port) { 236 this.port = port; 237 } 238 239 public int getMaxMessageSize() { 240 return maxMessageSize; 241 } 242 243 /** 244 * Maximum size for the syslog message (in characters); messages longer than 245 * this are truncated. The default value is 65400 (which is near the maximum for 246 * syslog-over-UDP). Note that the value is characters; the number of bytes may 247 * vary if non-ASCII characters 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 non-standardized 280 * 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 290 * writing to 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}