blob: 65a17fd5a043a79411b3b7bd062445bbbbf1bfc2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <log4cxx/net/syslogappender.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/helpers/datagramsocket.h>
#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>
#else
/* facility codes */
#define LOG_KERN (0<<3) /* kernel messages */
#define LOG_USER (1<<3) /* random user-level messages */
#define LOG_MAIL (2<<3) /* mail system */
#define LOG_DAEMON (3<<3) /* system daemons */
#define LOG_AUTH (4<<3) /* security/authorization messages */
#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
#define LOG_LPR (6<<3) /* line printer subsystem */
#define LOG_NEWS (7<<3) /* network news subsystem */
#define LOG_UUCP (8<<3) /* UUCP subsystem */
#define LOG_CRON (9<<3) /* clock daemon */
#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
#define LOG_FTP (11<<3) /* ftp daemon */
/* other codes through 15 reserved for system use */
#define LOG_LOCAL0 (16<<3) /* reserved for local use */
#define LOG_LOCAL1 (17<<3) /* reserved for local use */
#define LOG_LOCAL2 (18<<3) /* reserved for local use */
#define LOG_LOCAL3 (19<<3) /* reserved for local use */
#define LOG_LOCAL4 (20<<3) /* reserved for local use */
#define LOG_LOCAL5 (21<<3) /* reserved for local use */
#define LOG_LOCAL6 (22<<3) /* reserved for local use */
#define LOG_LOCAL7 (23<<3) /* reserved for local use */
#endif
#define LOG_UNDEF -1
using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::net;
IMPLEMENT_LOG4CXX_OBJECT(SyslogAppender)
SyslogAppender::SyslogAppender()
: syslogFacility(LOG_USER), facilityPrinting(false), sw(0), maxMessageLength(1024)
{
this->initSyslogFacilityStr();
}
SyslogAppender::SyslogAppender(const LayoutPtr& layout1,
int syslogFacility1)
: syslogFacility(syslogFacility1), facilityPrinting(false), sw(0), maxMessageLength(1024)
{
this->layout = layout1;
this->initSyslogFacilityStr();
}
SyslogAppender::SyslogAppender(const LayoutPtr& layout1,
const LogString& syslogHost1, int syslogFacility1)
: syslogFacility(syslogFacility1), facilityPrinting(false), sw(0), maxMessageLength(1024)
{
this->layout = layout1;
this->initSyslogFacilityStr();
setSyslogHost(syslogHost1);
}
SyslogAppender::~SyslogAppender()
{
finalize();
}
/** Release any resources held by this SyslogAppender.*/
void SyslogAppender::close()
{
closed = true;
if (sw != 0)
{
delete sw;
sw = 0;
}
}
void SyslogAppender::initSyslogFacilityStr()
{
facilityStr = getFacilityString(this->syslogFacility);
if (facilityStr.empty())
{
Pool p;
LogString msg(LOG4CXX_STR("\""));
StringHelper::toString(syslogFacility, p, msg);
msg.append(LOG4CXX_STR("\" is an unknown syslog facility. Defaulting to \"USER\"."));
LogLog::error(msg);
this->syslogFacility = LOG_USER;
facilityStr = LOG4CXX_STR("user:");
}
else
{
facilityStr += LOG4CXX_STR(":");
}
}
/**
Returns the specified syslog facility as a lower-case String,
e.g. "kern", "user", etc.
*/
LogString SyslogAppender::getFacilityString(
int syslogFacility)
{
switch (syslogFacility)
{
case LOG_KERN:
return LOG4CXX_STR("kern");
case LOG_USER:
return LOG4CXX_STR("user");
case LOG_MAIL:
return LOG4CXX_STR("mail");
case LOG_DAEMON:
return LOG4CXX_STR("daemon");
case LOG_AUTH:
return LOG4CXX_STR("auth");
case LOG_SYSLOG:
return LOG4CXX_STR("syslog");
case LOG_LPR:
return LOG4CXX_STR("lpr");
case LOG_NEWS:
return LOG4CXX_STR("news");
case LOG_UUCP:
return LOG4CXX_STR("uucp");
case LOG_CRON:
return LOG4CXX_STR("cron");
#ifdef LOG_AUTHPRIV
case LOG_AUTHPRIV:
return LOG4CXX_STR("authpriv");
#endif
#ifdef LOG_FTP
case LOG_FTP:
return LOG4CXX_STR("ftp");
#endif
case LOG_LOCAL0:
return LOG4CXX_STR("local0");
case LOG_LOCAL1:
return LOG4CXX_STR("local1");
case LOG_LOCAL2:
return LOG4CXX_STR("local2");
case LOG_LOCAL3:
return LOG4CXX_STR("local3");
case LOG_LOCAL4:
return LOG4CXX_STR("local4");
case LOG_LOCAL5:
return LOG4CXX_STR("local5");
case LOG_LOCAL6:
return LOG4CXX_STR("local6");
case LOG_LOCAL7:
return LOG4CXX_STR("local7");
default:
return LogString();
}
}
int SyslogAppender::getFacility(
const LogString& s)
{
if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("KERN"), LOG4CXX_STR("kern")))
{
return LOG_KERN;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("USER"), LOG4CXX_STR("user")))
{
return LOG_USER;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("MAIL"), LOG4CXX_STR("mail")))
{
return LOG_MAIL;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("DAEMON"), LOG4CXX_STR("daemon")))
{
return LOG_DAEMON;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("AUTH"), LOG4CXX_STR("auth")))
{
return LOG_AUTH;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("SYSLOG"), LOG4CXX_STR("syslog")))
{
return LOG_SYSLOG;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LPR"), LOG4CXX_STR("lpr")))
{
return LOG_LPR;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("NEWS"), LOG4CXX_STR("news")))
{
return LOG_NEWS;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("UUCP"), LOG4CXX_STR("uucp")))
{
return LOG_UUCP;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("CRON"), LOG4CXX_STR("cron")))
{
return LOG_CRON;
}
#ifdef LOG_AUTHPRIV
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("AUTHPRIV"), LOG4CXX_STR("authpriv")))
{
return LOG_AUTHPRIV;
}
#endif
#ifdef LOG_FTP
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("FTP"), LOG4CXX_STR("ftp")))
{
return LOG_FTP;
}
#endif
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL0"), LOG4CXX_STR("local0")))
{
return LOG_LOCAL0;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL1"), LOG4CXX_STR("local1")))
{
return LOG_LOCAL1;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL2"), LOG4CXX_STR("local2")))
{
return LOG_LOCAL2;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL3"), LOG4CXX_STR("local3")))
{
return LOG_LOCAL3;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL4"), LOG4CXX_STR("local4")))
{
return LOG_LOCAL4;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL5"), LOG4CXX_STR("local5")))
{
return LOG_LOCAL5;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL6"), LOG4CXX_STR("local6")))
{
return LOG_LOCAL6;
}
else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL7"), LOG4CXX_STR("local7")))
{
return LOG_LOCAL7;
}
else
{
return LOG_UNDEF;
}
}
void SyslogAppender::append(const spi::LoggingEventPtr& event, Pool& p)
{
if (!isAsSevereAsThreshold(event->getLevel()))
{
return;
}
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)
{
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;
}
#endif
// We must not attempt to append if sw is null.
if (sw == 0)
{
errorHandler->error(LOG4CXX_STR("No syslog host is set for SyslogAppedender named \"") +
this->name + LOG4CXX_STR("\"."));
return;
}
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);
}
sbuf.append(*it);
sw->write(sbuf);
}
}
void SyslogAppender::activateOptions(Pool&)
{
}
void SyslogAppender::setOption(const LogString& option, const LogString& value)
{
if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SYSLOGHOST"), LOG4CXX_STR("sysloghost")))
{
setSyslogHost(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FACILITY"), LOG4CXX_STR("facility")))
{
setFacility(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("MAXMESSAGELENGTH"), LOG4CXX_STR("maxmessagelength")))
{
setMaxMessageLength(OptionConverter::toInt(value, 1024));
}
else
{
AppenderSkeleton::setOption(option, value);
}
}
void SyslogAppender::setSyslogHost(const LogString& syslogHost1)
{
if (this->sw != 0)
{
delete this->sw;
this->sw = 0;
}
LogString slHost = syslogHost1;
int slHostPort = -1;
LogString::size_type colonPos = 0;
colonPos = slHost.rfind(':');
if (colonPos != LogString::npos)
{
slHostPort = StringHelper::toInt(slHost.substr(colonPos + 1));
// Erase the :port part of the host name
slHost.erase( colonPos );
}
// On the local host, we can directly use the system function 'syslog'
// if it is available (cf. append)
#if LOG4CXX_HAVE_SYSLOG
if (syslogHost1 != LOG4CXX_STR("localhost") && syslogHost1 != LOG4CXX_STR("127.0.0.1")
&& !syslogHost1.empty())
#endif
if (slHostPort >= 0)
{
this->sw = new SyslogWriter(slHost, slHostPort);
}
else
{
this->sw = new SyslogWriter(slHost);
}
this->syslogHost = slHost;
this->syslogHostPort = slHostPort;
}
void SyslogAppender::setFacility(const LogString& facilityName)
{
if (facilityName.empty())
{
return;
}
syslogFacility = getFacility(facilityName);
if (syslogFacility == LOG_UNDEF)
{
LogLog::error(LOG4CXX_STR("[") + facilityName +
LOG4CXX_STR("] is an unknown syslog facility. Defaulting to [USER]."));
syslogFacility = LOG_USER;
}
this->initSyslogFacilityStr();
}