Apache Log4cxx  Version 1.3.0
Loading...
Searching...
No Matches
Extending Log4cxx

Sometimes, you may want to extend Log4cxx, for example by creating a new appender to write out data in a new format. The following guide shows how you can extend Log4cxx in order to add a new appender.

The full sample application can be found at custom-appender.cpp and custom-appender.xml .

The first thing for our example is to create a class that extends log4cxx.AppenderSkeleton so that we don't need to implement all of the virtual methods that are defined in log4cxx.Appender:

using namespace log4cxx;
namespace com::foo {
class NullWriterAppender : public AppenderSkeleton {
};
}
Implementation base class for all appenders.
Definition: appenderskeleton.h:41
Definition: configuration.h:25

Next, we need to add in a few macros in order to properly register our new appender with Log4cxx:

using namespace log4cxx;
namespace com::foo {
class NullWriterAppender : public AppenderSkeleton {
public:
DECLARE_LOG4CXX_OBJECT(NullWriterAppender)
LOG4CXX_CAST_ENTRY(NullWriterAppender)
};
IMPLEMENT_LOG4CXX_OBJECT(NullWriterAppender)
}
#define LOG4CXX_CAST_ENTRY(Interface)
Definition: object.h:158
#define IMPLEMENT_LOG4CXX_OBJECT(object)
Definition: object.h:66
#define END_LOG4CXX_CAST_MAP()
Definition: object.h:152
#define DECLARE_LOG4CXX_OBJECT(object)
Definition: object.h:43
#define LOG4CXX_CAST_ENTRY_CHAIN(Interface)
Definition: object.h:164
#define BEGIN_LOG4CXX_CAST_MAP()
Definition: object.h:146

These macros allow Log4cxx to instantiate your class and provide log4cxx::cast. The DECLARE_LOG4CXX_OBJECT macro adds method declarations and the IMPLEMENT_LOG4CXX_OBJECT macro adds implementations. The IMPLEMENT_LOG4CXX_OBJECT also adds a static reference value which is initialized during program startup by calling, for example, NullWriterAppender::registerClass().

To avoid Static Initialization Order Fiasco when configuring Log4cxx during static initialization (as in the recommended runtime configuration technique) you should put your extension classes (and the IMPLEMENT_LOG4CXX_OBJECT macro) in a dynamic library (DSO/DLL) that is loaded before loading the Log4cxx configuration. Alternatively, you could call NullWriterAppender::registerClass() before loading the configuration. Multiple registerClass() calls do no harm.

Now, let's add some basic functionality to our class. As the name of our class implies, we are going to do nothing with our appender here, as this is just an example. To that end, we need to implement the following:

  1. Default constructor
  2. close method
  3. requiresLayout method
  4. append method, which does the actual writing of the log event
  5. activateOptions, which causes the class to reconfigure itself
  6. setOption, which gets called whenever we get an option in a config file

These are basically stub methods, with a few comments on their use:

NullWriterAppender(){}
void close() override {}
bool requiresLayout() const override {
return false;
}
void append(const spi::LoggingEventPtr& event, helpers::Pool& p) override {
// This gets called whenever there is a valid event for our appender.
}
void activateOptions(helpers::Pool& pool) override {
// Given all of our options, do something useful(e.g. open a file)
}
void setOption(const LogString& option, const LogString& value) override {
if (helpers::StringHelper::equalsIgnoreCase
( option
, LOG4CXX_STR("SOMEVALUE")
, LOG4CXX_STR("somevalue")
)
)
{
// Do something with the 'value' here.
}
}
Definition: pool.h:33
std::shared_ptr< LoggingEvent > LoggingEventPtr
Definition: appender.h:32
std::basic_string< logchar > LogString
Definition: logstring.h:60

At this point, we now have a fully functioning(if useless) custom appender. We can now refer to this appender in our configuration file like so:

<?xml version="1.0" encoding="UTF-8" ?>
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %c %-5p - %m%n"/>
</layout>
</appender>
<appender name="NullAppender" class="NullWriterAppender">
<param name="SomeValue" value="Nothing"/>
</appender>
<root>
<priority value="info" />
<appender-ref ref="ConsoleAppender"/>
</root>
<logger name="NullLogger" additivity="false">
<appender-ref ref="NullAppender"/>
</logger>
</log4j:configuration>

When Log4cxx is configured, any messages that go to the NullLogger will then be forwarded on to the NullWriterAppender.

This same technique can be used to add new classes of many different kinds to Log4cxx, including(but not limited to):