blob: 9e8f398afed6118a00cb5b2aaecf664d270a2f1e [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 <apr_strings.h>
#include <log4cxx/private/syslogappender_priv.h>
#define LOG_UNDEF -1
using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::net;
IMPLEMENT_LOG4CXX_OBJECT(SyslogAppender)
#define _priv static_cast<SyslogAppenderPriv*>(m_priv.get())
SyslogAppender::SyslogAppender()
: AppenderSkeleton (std::make_unique<SyslogAppenderPriv>())
{
this->initSyslogFacilityStr();
}
SyslogAppender::SyslogAppender(const LayoutPtr& layout1,
int syslogFacility1)
: AppenderSkeleton (std::make_unique<SyslogAppenderPriv>(layout1, syslogFacility1))
{
this->initSyslogFacilityStr();
}
SyslogAppender::SyslogAppender(const LayoutPtr& layout1,
const LogString& syslogHost1, int syslogFacility1)
: AppenderSkeleton (std::make_unique<SyslogAppenderPriv>(layout1, syslogHost1, syslogFacility1))
{
this->initSyslogFacilityStr();
setSyslogHost(syslogHost1);
}
SyslogAppender::~SyslogAppender()
{
finalize();
}
/** Release any resources held by this SyslogAppender.*/
void SyslogAppender::close()
{
_priv->closed = true;
if (_priv->sw)
{
_priv->sw = nullptr;
}
}
void SyslogAppender::initSyslogFacilityStr()
{
_priv->facilityStr = getFacilityString(_priv->syslogFacility);
if (_priv->facilityStr.empty())
{
Pool p;
LogString msg(LOG4CXX_STR("\""));
StringHelper::toString(_priv->syslogFacility, p, msg);
msg.append(LOG4CXX_STR("\" is an unknown syslog facility. Defaulting to \"USER\"."));
LogLog::error(msg);
_priv->syslogFacility = LOG_USER;
_priv->facilityStr = LOG4CXX_STR("user:");
}
else
{
_priv->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;
_priv->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() > _priv->maxMessageLength )
{
LogString::iterator start = msg.begin();
while ( start != msg.end() )
{
LogString::iterator end = start + _priv->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() );
LOG4CXX_DECODE_CHAR(str, buf);
it->append( str );
}
}
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 (_priv->sw == 0)
{
for ( std::vector<LogString>::iterator it = packets.begin();
it != packets.end();
it++ )
{
// use of "%s" to avoid a security hole
::syslog(_priv->syslogFacility | event->getLevel()->getSyslogEquivalent(),
"%s", it->c_str());
}
return;
}
#endif
// We must not attempt to append if sw is null.
if (_priv->sw == 0)
{
_priv->errorHandler->error(LOG4CXX_STR("No syslog host is set for SyslogAppedender named \"") +
_priv->name + LOG4CXX_STR("\"."));
return;
}
for ( std::vector<LogString>::iterator it = packets.begin();
it != packets.end();
it++ )
{
LogString sbuf(1, 0x3C /* '<' */);
StringHelper::toString((_priv->syslogFacility | event->getLevel()->getSyslogEquivalent()), p, sbuf);
sbuf.append(1, (logchar) 0x3E /* '>' */);
if (_priv->facilityPrinting)
{
sbuf.append(_priv->facilityStr);
}
sbuf.append(*it);
_priv->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 (_priv->sw != 0)
{
_priv->sw = nullptr;
}
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)
{
_priv->sw = std::make_unique<SyslogWriter>(slHost, slHostPort);
}
else
{
_priv->sw = std::make_unique<SyslogWriter>(slHost);
}
_priv->syslogHost = slHost;
_priv->syslogHostPort = slHostPort;
}
void SyslogAppender::setFacility(const LogString& facilityName)
{
if (facilityName.empty())
{
return;
}
_priv->syslogFacility = getFacility(facilityName);
if (_priv->syslogFacility == LOG_UNDEF)
{
LogLog::error(LOG4CXX_STR("[") + facilityName +
LOG4CXX_STR("] is an unknown syslog facility. Defaulting to [USER]."));
_priv->syslogFacility = LOG_USER;
}
this->initSyslogFacilityStr();
}
const LogString& SyslogAppender::getSyslogHost() const
{
return _priv->syslogHost;
}
LogString SyslogAppender::getFacility() const
{
return getFacilityString(_priv->syslogFacility);
}
void SyslogAppender::setFacilityPrinting(bool facilityPrinting1)
{
_priv->facilityPrinting = facilityPrinting1;
}
bool SyslogAppender::getFacilityPrinting() const
{
return _priv->facilityPrinting;
}
void SyslogAppender::setMaxMessageLength(int maxMessageLength1)
{
_priv->maxMessageLength = maxMessageLength1;
}
int SyslogAppender::getMaxMessageLength() const
{
return _priv->maxMessageLength;
}