LOGCXX-324 Split up large syslog messages (#46)

LOGCXX-324 Split up large syslog messages
diff --git a/src/main/cpp/syslogappender.cpp b/src/main/cpp/syslogappender.cpp
index f330e44..65a17fd 100644
--- a/src/main/cpp/syslogappender.cpp
+++ b/src/main/cpp/syslogappender.cpp
@@ -22,10 +22,12 @@
 #include <log4cxx/spi/loggingevent.h>
 #include <log4cxx/level.h>
 #include <log4cxx/helpers/transcoder.h>
+#include <log4cxx/helpers/optionconverter.h>
 #if !defined(LOG4CXX)
 	#define LOG4CXX 1
 #endif
 #include <log4cxx/private/log4cxx_private.h>
+#include <apr_strings.h>
 
 #if LOG4CXX_HAVE_SYSLOG
 	#include <syslog.h>
@@ -64,7 +66,7 @@
 IMPLEMENT_LOG4CXX_OBJECT(SyslogAppender)
 
 SyslogAppender::SyslogAppender()
-	: syslogFacility(LOG_USER), facilityPrinting(false), sw(0)
+	: syslogFacility(LOG_USER), facilityPrinting(false), sw(0), maxMessageLength(1024)
 {
 	this->initSyslogFacilityStr();
 
@@ -72,7 +74,7 @@
 
 SyslogAppender::SyslogAppender(const LayoutPtr& layout1,
 	int syslogFacility1)
-	: syslogFacility(syslogFacility1), facilityPrinting(false), sw(0)
+	: syslogFacility(syslogFacility1), facilityPrinting(false), sw(0), maxMessageLength(1024)
 {
 	this->layout = layout1;
 	this->initSyslogFacilityStr();
@@ -80,7 +82,7 @@
 
 SyslogAppender::SyslogAppender(const LayoutPtr& layout1,
 	const LogString& syslogHost1, int syslogFacility1)
-	: syslogFacility(syslogFacility1), facilityPrinting(false), sw(0)
+	: syslogFacility(syslogFacility1), facilityPrinting(false), sw(0), maxMessageLength(1024)
 {
 	this->layout = layout1;
 	this->initSyslogFacilityStr();
@@ -306,20 +308,54 @@
 	}
 
 	LogString msg;
+	std::string encoded;
 	layout->format(msg, event, p);
 
+	Transcoder::encode(msg, encoded);
+
+	// Split up the message if it is over maxMessageLength in size.
+	// According to RFC 3164, the max message length is 1024, however
+	// newer systems(such as syslog-ng) can go up to 8k in size for their
+	// messages.  We will append (x/y) at the end of each message
+	// to indicate how far through the message we are
+	std::vector<LogString> packets;
+	if( msg.size() > maxMessageLength ){
+		LogString::iterator start = msg.begin();
+		while( start != msg.end() ){
+			LogString::iterator end = start + maxMessageLength - 12;
+			if( end > msg.end() ){
+				end = msg.end();
+			}
+			LogString newMsg = LogString( start, end );
+			packets.push_back( newMsg );
+			start = end;
+		}
+
+		int current = 1;
+		for( std::vector<LogString>::iterator it = packets.begin();
+			 it != packets.end();
+			 it++, current++ ){
+			char buf[12];
+			apr_snprintf( buf, sizeof(buf), "(%d/%d)", current, packets.size() );
+			it->append( buf );
+		}
+	}else{
+		packets.push_back( msg );
+	}
+
 	// On the local host, we can directly use the system function 'syslog'
 	// if it is available
 #if LOG4CXX_HAVE_SYSLOG
 
 	if (sw == 0)
 	{
-		std::string sbuf;
-		Transcoder::encode(msg, sbuf);
-
-		// use of "%s" to avoid a security hole
-		::syslog(syslogFacility | event->getLevel()->getSyslogEquivalent(),
-			"%s", sbuf.c_str());
+		for( std::vector<LogString>::iterator it = packets.begin();
+			 it != packets.end();
+			 it++ ){
+			// use of "%s" to avoid a security hole
+			::syslog(syslogFacility | event->getLevel()->getSyslogEquivalent(),
+				"%s", it->c_str());
+		}
 
 		return;
 	}
@@ -334,17 +370,21 @@
 		return;
 	}
 
-	LogString sbuf(1, 0x3C /* '<' */);
-	StringHelper::toString((syslogFacility | event->getLevel()->getSyslogEquivalent()), p, sbuf);
-	sbuf.append(1, (logchar) 0x3E /* '>' */);
+	for( std::vector<LogString>::iterator it = packets.begin();
+		 it != packets.end();
+		 it++ ){
+		LogString sbuf(1, 0x3C /* '<' */);
+		StringHelper::toString((syslogFacility | event->getLevel()->getSyslogEquivalent()), p, sbuf);
+		sbuf.append(1, (logchar) 0x3E /* '>' */);
 
-	if (facilityPrinting)
-	{
-		sbuf.append(facilityStr);
+		if (facilityPrinting)
+		{
+			sbuf.append(facilityStr);
+		}
+
+		sbuf.append(*it);
+		sw->write(sbuf);
 	}
-
-	sbuf.append(msg);
-	sw->write(sbuf);
 }
 
 void SyslogAppender::activateOptions(Pool&)
@@ -361,6 +401,10 @@
 	{
 		setFacility(value);
 	}
+	else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("MAXMESSAGELENGTH"), LOG4CXX_STR("maxmessagelength")))
+	{
+		setMaxMessageLength(OptionConverter::toInt(value, 1024));
+	}
 	else
 	{
 		AppenderSkeleton::setOption(option, value);
diff --git a/src/main/include/log4cxx/net/syslogappender.h b/src/main/include/log4cxx/net/syslogappender.h
index 5acf92c..689fd2c 100644
--- a/src/main/include/log4cxx/net/syslogappender.h
+++ b/src/main/include/log4cxx/net/syslogappender.h
@@ -30,7 +30,19 @@
 {
 namespace net
 {
-/** Use SyslogAppender to send log messages to a remote syslog daemon.*/
+/**
+ * Use SyslogAppender to send log messages to a remote syslog daemon.
+ *
+ * Note that by default, this appender will split up messages that are
+ * more than 1024 bytes long, for compatability with BSD syslog(see
+ * RFC 3164).  Modern syslog implementations(e.g. syslog-ng) can accept
+ * messages much larger, and may have a default of 8k.  You may modify
+ * the default size of the messages by setting the MaxMessageLength option.
+ *
+ * When the message is too large for the current MaxMessageLength,
+ * the packet number and total # will be appended to the end of the
+ * message like this: (5/10)
+ */
 class LOG4CXX_EXPORT SyslogAppender : public AppenderSkeleton
 {
 	public:
@@ -136,6 +148,16 @@
 			return facilityPrinting;
 		}
 
+		inline void setMaxMessageLength(int maxMessageLength1)
+		{
+			maxMessageLength = maxMessageLength1;
+		}
+
+		inline int getMaxMessageLength() const
+		{
+			return maxMessageLength;
+		}
+
 	protected:
 		void initSyslogFacilityStr();
 
@@ -145,6 +167,7 @@
 		helpers::SyslogWriter* sw;
 		LogString syslogHost;
 		int syslogHostPort;
+		int maxMessageLength;
 	private:
 		SyslogAppender(const SyslogAppender&);
 		SyslogAppender& operator=(const SyslogAppender&);