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&);