Added documentation and example on how to extend log4cxx
diff --git a/src/examples/cpp/CMakeLists.txt b/src/examples/cpp/CMakeLists.txt
index 05614b3..4677ebb 100644
--- a/src/examples/cpp/CMakeLists.txt
+++ b/src/examples/cpp/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(ALL_LOG4CXX_EXAMPLES console delayedloop stream trivial)
+set(ALL_LOG4CXX_EXAMPLES console delayedloop stream trivial custom-appender)
foreach(exampleName IN LISTS ALL_LOG4CXX_EXAMPLES)
add_executable(${exampleName} ${exampleName}.cpp)
@@ -6,3 +6,7 @@
target_include_directories(${exampleName} PRIVATE ${CMAKE_CURRENT_LIST_DIR} $<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
target_link_libraries(${exampleName} PRIVATE log4cxx ${APR_UTIL_LIBRARIES} ${XMLLIB_LIBRARIES} ${APR_LIBRARIES} ${APR_SYSTEM_LIBS})
endforeach()
+
+configure_file( custom-appender.xml
+ ${CMAKE_CURRENT_BINARY_DIR}/custom-appender.xml
+ COPYONLY )
diff --git a/src/examples/cpp/custom-appender.cpp b/src/examples/cpp/custom-appender.cpp
new file mode 100644
index 0000000..5154090
--- /dev/null
+++ b/src/examples/cpp/custom-appender.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <stdlib.h>
+#include <log4cxx/basicconfigurator.h>
+#include <log4cxx/helpers/object.h>
+#include <log4cxx/appenderskeleton.h>
+#include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/xml/domconfigurator.h>
+
+namespace log4cxx {
+
+class NullWriterAppender : public log4cxx::AppenderSkeleton {
+public:
+ DECLARE_LOG4CXX_OBJECT(NullWriterAppender)
+ BEGIN_LOG4CXX_CAST_MAP()
+ LOG4CXX_CAST_ENTRY(NullWriterAppender)
+ LOG4CXX_CAST_ENTRY_CHAIN(AppenderSkeleton)
+ END_LOG4CXX_CAST_MAP()
+
+ NullWriterAppender(){}
+
+ virtual void close(){}
+
+ virtual bool requiresLayout() const {
+ return false;
+ }
+
+ virtual void append(const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& p){
+ // This gets called whenever there is a valid event for our appender.
+ }
+
+ virtual void activateOptions(log4cxx::helpers::Pool& /* pool */) {
+ // Given all of our options, do something useful(e.g. open a file)
+ }
+
+ virtual void setOption(const LogString& option, const LogString& value){
+ if (helpers::StringHelper::equalsIgnoreCase(option,
+ LOG4CXX_STR("SOMEVALUE"), LOG4CXX_STR("somevalue"))){
+ // Do something with the 'value' here.
+ }
+ }
+};
+
+IMPLEMENT_LOG4CXX_OBJECT(NullWriterAppender)
+
+}
+
+int main( int argc, char** argv ){
+ log4cxx::xml::DOMConfigurator::configure( "custom-appender.xml" );
+
+ log4cxx::LoggerPtr rootLogger = log4cxx::Logger::getRootLogger();
+ log4cxx::LoggerPtr nullLogger = log4cxx::Logger::getLogger( "NullLogger" );
+
+ LOG4CXX_INFO( rootLogger, "This is some root message" );
+ LOG4CXX_INFO( nullLogger, "This message will be discarded" );
+}
diff --git a/src/examples/cpp/custom-appender.xml b/src/examples/cpp/custom-appender.xml
new file mode 100644
index 0000000..f7b40e8
--- /dev/null
+++ b/src/examples/cpp/custom-appender.xml
@@ -0,0 +1,22 @@
+<?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>
diff --git a/src/site/markdown/1-usage.md b/src/site/markdown/1-usage.md
index bdc3adb..97ccaef 100644
--- a/src/site/markdown/1-usage.md
+++ b/src/site/markdown/1-usage.md
@@ -24,5 +24,6 @@
See the following pages for usage information:
* @subpage usage
+* @subpage extending-log4cxx
* @subpage faq
* @subpage configuration-samples
diff --git a/src/site/markdown/extending.md b/src/site/markdown/extending.md
new file mode 100644
index 0000000..ede583f
--- /dev/null
+++ b/src/site/markdown/extending.md
@@ -0,0 +1,141 @@
+Extending Log4cxx {#extending-log4cxx}
+===
+<!--
+ Note: License header cannot be first, as doxygen does not generate
+ cleanly if it before the '==='
+-->
+<!--
+ 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.
+-->
+
+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 in the src/examples/cpp
+directory.
+
+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:
+
+~~~{.cpp}
+namespace log4cxx {
+
+class NullWriterAppender : public log4cxx::AppenderSkeleton {
+};
+
+}
+~~~
+
+Next, we need to add in a few macros in order to properly register
+our new appender with log4cxx:
+
+~~~{.cpp}
+namespace log4cxx {
+
+class NullWriterAppender : public log4cxx::AppenderSkeleton {
+public:
+ DECLARE_LOG4CXX_OBJECT(NullWriterAppender)
+ BEGIN_LOG4CXX_CAST_MAP()
+ LOG4CXX_CAST_ENTRY(NullWriterAppender)
+ LOG4CXX_CAST_ENTRY_CHAIN(AppenderSkeleton)
+ END_LOG4CXX_CAST_MAP()
+
+};
+
+IMPLEMENT_LOG4CXX_OBJECT(NullWriterAppender)
+
+}
+~~~
+
+These macros tell log4cxx what the name of our class is, as well as giving
+log4cxx a way of instansiating our class. Without these macros, the custom
+class will not work.
+
+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:
+
+~~~{.cpp}
+ NullWriterAppender(){}
+
+ virtual void close(){}
+
+ virtual bool requiresLayout() const {
+ return false;
+ }
+
+ virtual void append(const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& p){
+ // This gets called whenever there is a valid event for our appender.
+ }
+
+ virtual void activateOptions(log4cxx::helpers::Pool& /* pool */) {
+ // Given all of our options, do something useful(e.g. open a file)
+ }
+
+ virtual void setOption(const LogString& option, const LogString& value){
+ if (helpers::StringHelper::equalsIgnoreCase(option,
+ LOG4CXX_STR("SOMEVALUE"), LOG4CXX_STR("somevalue"))){
+ // Do something with the 'value' here.
+ }
+ }
+~~~
+
+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}
+<?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):
+* [Appenders](@ref log4cxx.Appender)
+* [Layouts](@ref log4cxx.Layout)
+* [Filters](@ref log4cxx.spi.Filter)