/**
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2013, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.core.net;
import java.io.IOException;
import java.io.OutputStream;
import java.net.SocketException;
import java.net.UnknownHostException;
import ch.qos.logback.core.AppenderBase;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.Layout;
/**
* Base class for SyslogAppender.
*
* @author Ceki Gülcü
*
* @param <E> type of log event object
*/
public abstract class SyslogAppenderBase<E> extends AppenderBase<E> {
final static String SYSLOG_LAYOUT_URL = CoreConstants.CODES_URL
+ "#syslog_layout";
final static int MAX_MESSAGE_SIZE_LIMIT = 65000;
Layout<E> layout;
String facilityStr;
String syslogHost;
protected String suffixPattern;
protected SyslogOutputStream sos;
int port = SyslogConstants.SYSLOG_PORT;
boolean initialized = false;
private boolean lazyInit = false;
int maxMessageSize;
public void start() {
int errorCount = 0;
if (facilityStr == null) {
addError("The Facility option is mandatory");
errorCount++;
}
if (!lazyInit) {
// this connect() does not retry (unlike the one in SocketAppenderBase)
// so account for the error if no connection established
if (!connect()) {
errorCount++;
}
}
if (layout == null) {
layout = buildLayout();
}
if (errorCount == 0) {
super.start();
}
}
abstract public Layout<E> buildLayout();
abstract public int getSeverityForEvent(Object eventObject);
private boolean connect() {
try {
sos = new SyslogOutputStream(syslogHost, port);
final int systemDatagramSize = sos.getSendBufferSize();
if (maxMessageSize == 0) {
maxMessageSize = Math.min(systemDatagramSize, MAX_MESSAGE_SIZE_LIMIT);
addInfo("Defaulting maxMessageSize to [" + maxMessageSize + "]");
} else if (maxMessageSize > systemDatagramSize) {
addWarn("maxMessageSize of [" + maxMessageSize + "] is larger than the system defined datagram size of [" + systemDatagramSize + "].");
addWarn("This may result in dropped logs.");
}
} catch (UnknownHostException e) {
addError("Could not create SyslogWriter", e);
} catch (SocketException e) {
addWarn(
"Failed to bind to a random datagram socket. Will try to reconnect later.",
e);
}
// SyslogOutputStream must be non-null to be connected
return sos != null;
}
@Override
protected void append(E eventObject) {
if (!isStarted()) {
return;
}
if (!initialized && lazyInit) {
initialized = true;
connect();
}
if (sos == null) {
return;
}
try {
String msg = layout.doLayout(eventObject);
if(msg == null) {
return;
}
if (msg.length() > maxMessageSize) {
msg = msg.substring(0, maxMessageSize);
}
sos.write(msg.getBytes());
sos.flush();
postProcess(eventObject, sos);
} catch (IOException ioe) {
addError("Failed to send diagram to " + syslogHost, ioe);
}
}
protected void postProcess(Object event, OutputStream sw) {
}
/**
* Returns the integer value corresponding to the named syslog facility.
*
* @param facilityStr the syslog facility name
* @return the syslog facility code
* @throws IllegalArgumentException
* if the facility string is not recognized
*/
static public int facilityStringToint(String facilityStr) {
if ("KERN".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_KERN;
} else if ("USER".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_USER;
} else if ("MAIL".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_MAIL;
} else if ("DAEMON".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_DAEMON;
} else if ("AUTH".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_AUTH;
} else if ("SYSLOG".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_SYSLOG;
} else if ("LPR".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LPR;
} else if ("NEWS".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_NEWS;
} else if ("UUCP".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_UUCP;
} else if ("CRON".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_CRON;
} else if ("AUTHPRIV".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_AUTHPRIV;
} else if ("FTP".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_FTP;
} else if ("NTP".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_NTP;
} else if ("AUDIT".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_AUDIT;
} else if ("ALERT".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_ALERT;
} else if ("CLOCK".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_CLOCK;
} else if ("LOCAL0".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LOCAL0;
} else if ("LOCAL1".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LOCAL1;
} else if ("LOCAL2".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LOCAL2;
} else if ("LOCAL3".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LOCAL3;
} else if ("LOCAL4".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LOCAL4;
} else if ("LOCAL5".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LOCAL5;
} else if ("LOCAL6".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LOCAL6;
} else if ("LOCAL7".equalsIgnoreCase(facilityStr)) {
return SyslogConstants.LOG_LOCAL7;
} else {
throw new IllegalArgumentException(facilityStr
+ " is not a valid syslog facility string");
}
}
/**
* Returns the value of the <b>SyslogHost</b> option.
* @return the syslog hostname
*/
public String getSyslogHost() {
return syslogHost;
}
/**
* The <b>SyslogHost</b> option is the name of the the syslog host where log
* output should go.
*
* <b>WARNING</b> If the SyslogHost is not set, then this appender will fail.
* @param syslogHost the syslog hostname
*/
public void setSyslogHost(String syslogHost) {
this.syslogHost = syslogHost;
}
/**
* Returns the string value of the <b>Facility</b> option.
*
* See {@link #setFacility} for the set of allowed values.
* @return the current facility name
*/
public String getFacility() {
return facilityStr;
}
/**
* The <b>Facility</b> option must be set one of the strings KERN, USER, MAIL,
* DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT,
* ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6,
* LOCAL7. Case is not important.
*
* <p>
* See {@link SyslogConstants} and RFC 3164 for more information about the
* <b>Facility</b> option.
* @param facilityStr the name of the desired facility
*/
public void setFacility(String facilityStr) {
if (facilityStr != null) {
facilityStr = facilityStr.trim();
}
this.facilityStr = facilityStr;
}
/**
* Gets the syslog port to connect to
* @return the syslog port
*/
public int getPort() {
return port;
}
/**
* The port number on the syslog server to connect to. Normally, you would not
* want to change the default value, that is 514.
* @param port the desired syslog port
*/
public void setPort(int port) {
this.port = port;
}
/**
* Gets the maximum size of a syslog message
* @return the max message size (in characters)
*/
public int getMaxMessageSize() {
return maxMessageSize;
}
/**
* Maximum size for the syslog message (in characters); messages
* longer than this are truncated. The default value is 65400 (which
* is near the maximum for syslog-over-UDP). Note that the value is
* characters; the number of bytes may vary if non-ASCII characters
* are present.
* @param maxMessageSize the max message size (in characters)
*/
public void setMaxMessageSize(int maxMessageSize) {
this.maxMessageSize = maxMessageSize;
}
public Layout<E> getLayout() {
return layout;
}
public void setLayout(Layout<E> layout) {
addWarn("The layout of a SyslogAppender cannot be set directly. See also "
+ SYSLOG_LAYOUT_URL);
}
/**
* Gets the enable status of lazy initialization of the Syslog output
* stream
*
* @return true if enabled; false otherwise
*/
public boolean getLazy() {
return lazyInit;
}
/**
* Enables/disables lazy initialization of the Syslog output stream.
* This defers the connection process until the first outgoing message.
*
* @param enable true to enable lazy initialization; false otherwise
*/
public void setLazy(boolean enable) {
lazyInit = enable;
}
@Override
public void stop() {
sos.close();
super.stop();
}
/**
* See {@link #setSuffixPattern(String)}.
*
* @return the suffix pattern
*/
public String getSuffixPattern() {
return suffixPattern;
}
/**
* The <b>suffixPattern</b> option specifies the format of the
* non-standardized part of the message sent to the syslog server.
*
* @param suffixPattern the desired suffix pattern
*/
public void setSuffixPattern(String suffixPattern) {
this.suffixPattern = suffixPattern;
}
}