This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch next_stable
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git


The following commit(s) were added to refs/heads/next_stable by this push:
     new 1ba97dfa APR termination must be after destruction of all log4cxx 
objects (#122)
1ba97dfa is described below

commit 1ba97dfa01b9b1a73a4a9f032138c72a8a49511c
Author: Stephen Webb <[email protected]>
AuthorDate: Sat Sep 10 12:12:11 2022 +1000

    APR termination must be after destruction of all log4cxx objects (#122)
    
    * Make APR initialization/termination book-end all log4cxx object life 
times.
      As the APRInitializer class has member variables whose destructors call 
apr_* functions, apr_terminate() cannot be called
      the the APRInitializer class destructor.
    
    * Delay heirarchy destruction until other threads have exited doAppend()
    
    * Added another test for the fallback handling to ensure that multiple 
appenders on the same logger will not segfault if replaced while iterating.
    
    * Stop using APR on other threads once exit() has been called
    
    Co-authored-by: Robert Middleton <[email protected]>
---
 src/main/cpp/appenderattachableimpl.cpp            | 43 +++++++-----------
 src/main/cpp/aprinitializer.cpp                    | 18 ++++++--
 src/main/cpp/asyncappender.cpp                     |  2 -
 src/main/cpp/asyncappender_nonblocking.cpp         | 12 -----
 src/main/cpp/hierarchy.cpp                         |  4 ++
 src/main/cpp/logger.cpp                            | 24 ++++++++--
 .../log4cxx/helpers/appenderattachableimpl.h       |  2 -
 src/main/include/log4cxx/logger.h                  |  5 --
 src/test/cpp/varia/errorhandlertestcase.cpp        | 53 ++++++++++++++++++++--
 src/test/resources/input/xml/fallback1.xml         |  2 +-
 .../input/xml/{fallback1.xml => fallback2.xml}     |  9 +++-
 11 files changed, 115 insertions(+), 59 deletions(-)

diff --git a/src/main/cpp/appenderattachableimpl.cpp 
b/src/main/cpp/appenderattachableimpl.cpp
index 3de8ccf4..d936d9c6 100644
--- a/src/main/cpp/appenderattachableimpl.cpp
+++ b/src/main/cpp/appenderattachableimpl.cpp
@@ -32,7 +32,7 @@ struct AppenderAttachableImpl::priv_data
 {
        /** Array of appenders. */
        AppenderList  appenderList;
-       mutable std::mutex m_mutex;
+       mutable std::recursive_mutex m_mutex;
 };
 
 
@@ -54,7 +54,7 @@ void AppenderAttachableImpl::addAppender(const AppenderPtr 
newAppender)
                return;
        }
 
-       std::unique_lock<std::mutex> lock( m_priv->m_mutex );
+       std::lock_guard<std::recursive_mutex> lock( m_priv->m_mutex );
        AppenderList::iterator it = std::find(
                        m_priv->appenderList.begin(), 
m_priv->appenderList.end(), newAppender);
 
@@ -68,24 +68,16 @@ int AppenderAttachableImpl::appendLoopOnAppenders(
        const spi::LoggingEventPtr& event,
        Pool& p)
 {
-
-       AppenderList allAppenders;
        int numberAppended = 0;
+       std::lock_guard<std::recursive_mutex> lock( m_priv->m_mutex );
+       // FallbackErrorHandler::error() may modify our list of appenders
+       // while we are iterating over them (if it holds the same logger).
+       // So, make a local copy of the appenders that we want to iterate over
+       // before actually iterating over them.
+       AppenderList allAppenders = m_priv->appenderList;
+       for (auto appender : allAppenders)
        {
-               // There are occasions where we want to modify our list of 
appenders
-               // while we are iterating over them.  For example, if one of the
-               // appenders fails, we may want to swap it out for a new one.
-               // So, make a local copy of the appenders that we want to 
iterate over
-               // before actually iterating over them.
-               std::unique_lock<std::mutex> lock( m_priv->m_mutex );
-               allAppenders = m_priv->appenderList;
-       }
-
-       for (AppenderList::iterator it = allAppenders.begin();
-               it != allAppenders.end();
-               it++)
-       {
-               (*it)->doAppend(event, p);
+               appender->doAppend(event, p);
                numberAppended++;
        }
 
@@ -94,6 +86,7 @@ int AppenderAttachableImpl::appendLoopOnAppenders(
 
 AppenderList AppenderAttachableImpl::getAllAppenders() const
 {
+       std::lock_guard<std::recursive_mutex> lock( m_priv->m_mutex );
        return m_priv->appenderList;
 }
 
@@ -104,7 +97,7 @@ AppenderPtr AppenderAttachableImpl::getAppender(const 
LogString& name) const
                return 0;
        }
 
-       std::unique_lock<std::mutex> lock( m_priv->m_mutex );
+       std::lock_guard<std::recursive_mutex> lock( m_priv->m_mutex );
        AppenderList::const_iterator it, itEnd = m_priv->appenderList.end();
        AppenderPtr appender;
 
@@ -128,7 +121,7 @@ bool AppenderAttachableImpl::isAttached(const AppenderPtr 
appender) const
                return false;
        }
 
-       std::unique_lock<std::mutex> lock( m_priv->m_mutex );
+       std::lock_guard<std::recursive_mutex> lock( m_priv->m_mutex );
        AppenderList::const_iterator it = std::find(
                        m_priv->appenderList.begin(), 
m_priv->appenderList.end(), appender);
 
@@ -137,7 +130,7 @@ bool AppenderAttachableImpl::isAttached(const AppenderPtr 
appender) const
 
 void AppenderAttachableImpl::removeAllAppenders()
 {
-       std::unique_lock<std::mutex> lock( m_priv->m_mutex );
+       std::lock_guard<std::recursive_mutex> lock( m_priv->m_mutex );
        AppenderList::iterator it, itEnd = m_priv->appenderList.end();
        AppenderPtr a;
 
@@ -157,7 +150,7 @@ void AppenderAttachableImpl::removeAppender(const 
AppenderPtr appender)
                return;
        }
 
-       std::unique_lock<std::mutex> lock( m_priv->m_mutex );
+       std::lock_guard<std::recursive_mutex> lock( m_priv->m_mutex );
        AppenderList::iterator it = std::find(
                        m_priv->appenderList.begin(), 
m_priv->appenderList.end(), appender);
 
@@ -174,7 +167,7 @@ void AppenderAttachableImpl::removeAppender(const 
LogString& name)
                return;
        }
 
-       std::unique_lock<std::mutex> lock( m_priv->m_mutex );
+       std::lock_guard<std::recursive_mutex> lock( m_priv->m_mutex );
        AppenderList::iterator it, itEnd = m_priv->appenderList.end();
        AppenderPtr appender;
 
@@ -190,8 +183,4 @@ void AppenderAttachableImpl::removeAppender(const 
LogString& name)
        }
 }
 
-std::mutex& AppenderAttachableImpl::getMutex()
-{
-       return m_priv->m_mutex;
-}
 
diff --git a/src/main/cpp/aprinitializer.cpp b/src/main/cpp/aprinitializer.cpp
index 6e4214c4..47f64198 100644
--- a/src/main/cpp/aprinitializer.cpp
+++ b/src/main/cpp/aprinitializer.cpp
@@ -39,11 +39,24 @@ extern "C" void tlsDestruct(void* ptr)
 {
        delete ((ThreadSpecificData*) ptr);
 }
+
+// The first object created and the last object destroyed
+struct apr_environment
+{
+    apr_environment()
+    {
+        apr_initialize();
+    }
+    ~apr_environment()
+    {
+        apr_terminate();
+    }
+};
+
 }
 
 APRInitializer::APRInitializer() : p(0), startTime(0), tlsKey(0)
 {
-       apr_initialize();
        apr_pool_create(&p, NULL);
        apr_atomic_init(p);
        apr_atomic_init(p);
@@ -62,8 +75,6 @@ APRInitializer::~APRInitializer()
 #if APR_HAS_THREADS
        std::unique_lock<std::mutex> lock(mutex);
        apr_threadkey_private_delete(tlsKey);
-#else
-       apr_terminate();
 #endif
 }
 
@@ -87,6 +98,7 @@ void APRInitializer::unregisterAll()
 
 APRInitializer& APRInitializer::getInstance()
 {
+       static apr_environment env;
        static APRInitializer init;
        return init;
 }
diff --git a/src/main/cpp/asyncappender.cpp b/src/main/cpp/asyncappender.cpp
index 23100830..2a0cf68a 100644
--- a/src/main/cpp/asyncappender.cpp
+++ b/src/main/cpp/asyncappender.cpp
@@ -214,7 +214,6 @@ void AsyncAppender::append(const spi::LoggingEventPtr& 
event, Pool& p)
        //
        if (!priv->dispatcher.joinable() || priv->bufferSize <= 0)
        {
-               std::unique_lock<std::mutex> lock(priv->appenders->getMutex());
                priv->appenders->appendLoopOnAppenders(event, p);
                return;
        }
@@ -316,7 +315,6 @@ void AsyncAppender::close()
        }
 
        {
-               std::unique_lock<std::mutex> lock(priv->appenders->getMutex());
                AppenderList appenderList = priv->appenders->getAllAppenders();
 
                for (AppenderList::iterator iter = appenderList.begin();
diff --git a/src/main/cpp/asyncappender_nonblocking.cpp 
b/src/main/cpp/asyncappender_nonblocking.cpp
index 6c28756e..cceceba9 100644
--- a/src/main/cpp/asyncappender_nonblocking.cpp
+++ b/src/main/cpp/asyncappender_nonblocking.cpp
@@ -28,7 +28,6 @@
 #include <apr_thread_mutex.h>
 #include <apr_thread_cond.h>
 #include <log4cxx/helpers/condition.h>
-#include <log4cxx/helpers/synchronized.h>
 #include <log4cxx/helpers/stringhelper.h>
 #include <apr_atomic.h>
 #include <log4cxx/helpers/optionconverter.h>
@@ -78,7 +77,6 @@ void AsyncAppender::releaseRef() const
 
 void AsyncAppender::addAppender(const AppenderPtr& newAppender)
 {
-       synchronized sync(appenders->getMutex());
        appenders->addAppender(newAppender);
 }
 
@@ -124,7 +122,6 @@ void AsyncAppender::append(const spi::LoggingEventPtr& 
event, Pool& p)
        //
        if (!dispatcher.isAlive() || bufferSize <= 0)
        {
-               synchronized sync(appenders->getMutex());
                appenders->appendLoopOnAppenders(event, p);
                return;
        }
@@ -196,7 +193,6 @@ void AsyncAppender::append(const spi::LoggingEventPtr& 
event, Pool& p)
                }
        }
 #else
-       synchronized sync(appenders->getMutex());
        appenders->appendLoopOnAppenders(event, p);
 #endif
 }
@@ -227,7 +223,6 @@ void AsyncAppender::close()
 #endif
 
        {
-               synchronized sync(appenders->getMutex());
                AppenderList appenderList = appenders->getAllAppenders();
 
                for (AppenderList::iterator iter = appenderList.begin();
@@ -241,19 +236,16 @@ void AsyncAppender::close()
 
 AppenderList AsyncAppender::getAllAppenders() const
 {
-       synchronized sync(appenders->getMutex());
        return appenders->getAllAppenders();
 }
 
 AppenderPtr AsyncAppender::getAppender(const LogString& n) const
 {
-       synchronized sync(appenders->getMutex());
        return appenders->getAppender(n);
 }
 
 bool AsyncAppender::isAttached(const AppenderPtr& appender) const
 {
-       synchronized sync(appenders->getMutex());
        return appenders->isAttached(appender);
 }
 
@@ -264,19 +256,16 @@ bool AsyncAppender::requiresLayout() const
 
 void AsyncAppender::removeAllAppenders()
 {
-       synchronized sync(appenders->getMutex());
        appenders->removeAllAppenders();
 }
 
 void AsyncAppender::removeAppender(const AppenderPtr& appender)
 {
-       synchronized sync(appenders->getMutex());
        appenders->removeAppender(appender);
 }
 
 void AsyncAppender::removeAppender(const LogString& n)
 {
-       synchronized sync(appenders->getMutex());
        appenders->removeAppender(n);
 }
 
@@ -429,7 +418,6 @@ void* LOG4CXX_THREAD_FUNC 
AsyncAppender::dispatch(apr_thread_t* /*thread*/, void
                                iter != events.end();
                                iter++)
                        {
-                               synchronized sync(pThis->appenders->getMutex());
                                pThis->appenders->appendLoopOnAppenders(*iter, 
p);
                        }
                }
diff --git a/src/main/cpp/hierarchy.cpp b/src/main/cpp/hierarchy.cpp
index 8c399067..1e38efdd 100644
--- a/src/main/cpp/hierarchy.cpp
+++ b/src/main/cpp/hierarchy.cpp
@@ -96,9 +96,13 @@ Hierarchy::~Hierarchy()
        for (auto& item : m_priv->loggers)
        {
                if (auto& pLogger = item.second)
+               {
                        pLogger->removeHierarchy();
+                       pLogger->removeAllAppenders();
+               }
        }
        m_priv->root->removeHierarchy();
+       m_priv->root->removeAllAppenders();
 }
 
 void Hierarchy::addHierarchyEventListener(const 
spi::HierarchyEventListenerPtr& listener)
diff --git a/src/main/cpp/logger.cpp b/src/main/cpp/logger.cpp
index e3db0871..361f51bd 100644
--- a/src/main/cpp/logger.cpp
+++ b/src/main/cpp/logger.cpp
@@ -101,7 +101,7 @@ void Logger::addAppender(const AppenderPtr newAppender)
        m_priv->aai.addAppender(newAppender);
        if (auto rep = getHierarchy())
        {
-               m_priv->repositoryRaw->fireAddAppenderEvent(this, 
newAppender.get());
+               rep->fireAddAppenderEvent(this, newAppender.get());
        }
 }
 
@@ -119,7 +119,7 @@ void Logger::reconfigure( const std::vector<AppenderPtr>& 
appenders, bool additi
 
                if (auto rep = getHierarchy())
                {
-                       m_priv->repositoryRaw->fireAddAppenderEvent(this, 
it->get());
+                       rep->fireAddAppenderEvent(this, it->get());
                }
        }
 }
@@ -144,7 +144,7 @@ void Logger::callAppenders(const spi::LoggingEventPtr& 
event, Pool& p) const
 
        if (writes == 0 && rep)
        {
-               
m_priv->repositoryRaw->emitNoAppenderWarning(const_cast<Logger*>(this));
+               rep->emitNoAppenderWarning(const_cast<Logger*>(this));
        }
 }
 
@@ -162,6 +162,8 @@ void Logger::closeNestedAppenders()
 void Logger::forcedLog(const LevelPtr& level1, const std::string& message,
        const LocationInfo& location) const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LOG4CXX_DECODE_CHAR(msg, message);
        LoggingEventPtr event(new LoggingEvent(m_priv->name, level1, msg, 
location));
@@ -171,6 +173,8 @@ void Logger::forcedLog(const LevelPtr& level1, const 
std::string& message,
 
 void Logger::forcedLog(const LevelPtr& level1, const std::string& message) 
const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LOG4CXX_DECODE_CHAR(msg, message);
        LoggingEventPtr event(new LoggingEvent(m_priv->name, level1, msg,
@@ -181,6 +185,8 @@ void Logger::forcedLog(const LevelPtr& level1, const 
std::string& message) const
 void Logger::forcedLogLS(const LevelPtr& level1, const LogString& message,
        const LocationInfo& location) const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LoggingEventPtr event(new LoggingEvent(m_priv->name, level1, message, 
location));
        callAppenders(event, p);
@@ -694,6 +700,8 @@ LoggerPtr Logger::getLoggerLS(const LogString& name)
 void Logger::forcedLog(const LevelPtr& level1, const std::wstring& message,
        const LocationInfo& location) const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LOG4CXX_DECODE_WCHAR(msg, message);
        LoggingEventPtr event(new LoggingEvent(m_priv->name, level1, msg, 
location));
@@ -702,6 +710,8 @@ void Logger::forcedLog(const LevelPtr& level1, const 
std::wstring& message,
 
 void Logger::forcedLog(const LevelPtr& level1, const std::wstring& message) 
const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LOG4CXX_DECODE_WCHAR(msg, message);
        LoggingEventPtr event(new LoggingEvent(m_priv->name, level1, msg,
@@ -845,6 +855,8 @@ void Logger::warn(const std::wstring& msg) const
 void Logger::forcedLog(const LevelPtr& level1, const 
std::basic_string<UniChar>& message,
        const LocationInfo& location) const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LOG4CXX_DECODE_UNICHAR(msg, message);
        LoggingEventPtr event(new LoggingEvent(m_priv->name, level1, msg, 
location));
@@ -853,6 +865,8 @@ void Logger::forcedLog(const LevelPtr& level1, const 
std::basic_string<UniChar>&
 
 void Logger::forcedLog(const LevelPtr& level1, const 
std::basic_string<UniChar>& message) const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LOG4CXX_DECODE_UNICHAR(msg, message);
        LoggingEventPtr event(new LoggingEvent(m_priv->name, level1, msg,
@@ -993,6 +1007,8 @@ void Logger::warn(const std::basic_string<UniChar>& msg) 
const
 void Logger::forcedLog(const LevelPtr& level1, const CFStringRef& message,
        const LocationInfo& location) const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LOG4CXX_DECODE_CFSTRING(msg, message);
        LoggingEventPtr event(new LoggingEvent(name, level1, msg, location));
@@ -1001,6 +1017,8 @@ void Logger::forcedLog(const LevelPtr& level1, const 
CFStringRef& message,
 
 void Logger::forcedLog(const LevelPtr& level1, const CFStringRef& message) 
const
 {
+       if (!getHierarchy()) // Has removeHierarchy() been called?
+               return;
        Pool p;
        LOG4CXX_DECODE_CFSTRING(msg, message);
        LoggingEventPtr event(new LoggingEvent(name, level1, msg,
diff --git a/src/main/include/log4cxx/helpers/appenderattachableimpl.h 
b/src/main/include/log4cxx/helpers/appenderattachableimpl.h
index b2e39b00..2317d3a9 100644
--- a/src/main/include/log4cxx/helpers/appenderattachableimpl.h
+++ b/src/main/include/log4cxx/helpers/appenderattachableimpl.h
@@ -102,8 +102,6 @@ class LOG4CXX_EXPORT AppenderAttachableImpl :
                 */
                virtual void removeAppender(const LogString& name);
 
-               std::mutex& getMutex();
-
        private:
                LOG4CXX_DECLARE_PRIVATE_MEMBER_PTR(priv_data, m_priv)
 
diff --git a/src/main/include/log4cxx/logger.h 
b/src/main/include/log4cxx/logger.h
index 2f56de12..e4a35408 100644
--- a/src/main/include/log4cxx/logger.h
+++ b/src/main/include/log4cxx/logger.h
@@ -28,11 +28,6 @@
 namespace log4cxx
 {
 
-namespace helpers
-{
-class synchronized;
-}
-
 namespace spi
 {
 class LoggerRepository;
diff --git a/src/test/cpp/varia/errorhandlertestcase.cpp 
b/src/test/cpp/varia/errorhandlertestcase.cpp
index e86972d1..4f4007e1 100644
--- a/src/test/cpp/varia/errorhandlertestcase.cpp
+++ b/src/test/cpp/varia/errorhandlertestcase.cpp
@@ -20,6 +20,7 @@
 #include <log4cxx/fileappender.h>
 #include <log4cxx/varia/fallbackerrorhandler.h>
 #include <log4cxx/appender.h>
+#include <log4cxx/helpers/loglog.h>
 #include "../logunit.h"
 #include "../util/transformer.h"
 #include "../util/compare.h"
@@ -35,11 +36,19 @@ LOGUNIT_CLASS(ErrorHandlerTestCase)
 {
        LOGUNIT_TEST_SUITE(ErrorHandlerTestCase);
        LOGUNIT_TEST(test1);
+       LOGUNIT_TEST(test2);
        LOGUNIT_TEST_SUITE_END();
 
        LoggerPtr root;
        LoggerPtr logger;
-
+#ifdef _DEBUG
+       struct Fixture
+       {
+               Fixture() {
+                       helpers::LogLog::setInternalDebugging(true);
+               }
+       } suiteFixture;
+#endif
 
 public:
        void setUp()
@@ -85,7 +94,45 @@ public:
 
                try
                {
-                       Transformer::transform("output/fallback", 
"output/fallbackfiltered", filters);
+                       Transformer::transform("output/fallback1", 
"output/fallbackfiltered1", filters);
+               }
+               catch (UnexpectedFormatException& e)
+               {
+                       std::cout << "UnexpectedFormatException :" << e.what() 
<< std::endl;
+                       throw;
+               }
+
+
+               LOGUNIT_ASSERT(Compare::compare("output/fallbackfiltered1", 
"witness/fallback1"));
+       }
+
+       void test2()
+       {
+               DOMConfigurator::configure("input/xml/fallback2.xml");
+               AppenderPtr appender = 
root->getAppender(LOG4CXX_STR("PRIMARY"));
+               FileAppenderPtr primary = log4cxx::cast<FileAppender>(appender);
+               log4cxx::varia::FallbackErrorHandlerPtr eh;
+               log4cxx::spi::ErrorHandlerPtr errHandle = 
primary->getErrorHandler();
+               eh = 
log4cxx::cast<log4cxx::varia::FallbackErrorHandler>(errHandle);
+               LOGUNIT_ASSERT(eh != 0);
+               eh->setLogger(logger);
+               common();
+
+               std::string TEST1_PAT =
+                       "FALLBACK - (root|test) - Message {0-9}";
+
+               ControlFilter cf;
+               cf << TEST1_PAT;
+
+               LineNumberFilter lineNumberFilter;
+
+               std::vector<Filter*> filters;
+               filters.push_back(&cf);
+               filters.push_back(&lineNumberFilter);
+
+               try
+               {
+                       Transformer::transform("output/fallback2", 
"output/fallbackfiltered2", filters);
                }
                catch (UnexpectedFormatException& e)
                {
@@ -94,7 +141,7 @@ public:
                }
 
 
-               LOGUNIT_ASSERT(Compare::compare("output/fallbackfiltered", 
"witness/fallback1"));
+               LOGUNIT_ASSERT(Compare::compare("output/fallbackfiltered2", 
"witness/fallback1"));
        }
 
        void common()
diff --git a/src/test/resources/input/xml/fallback1.xml 
b/src/test/resources/input/xml/fallback1.xml
index 90072a31..83ed810e 100644
--- a/src/test/resources/input/xml/fallback1.xml
+++ b/src/test/resources/input/xml/fallback1.xml
@@ -45,7 +45,7 @@
   </appender>
 
   <appender name="FALLBACK" class="org.apache.log4j.FileAppender">
-    <param name="File" value="output/fallback" />
+    <param name="File" value="output/fallback1" />
     <param name="Append" value="false" />
     <layout class="org.apache.log4j.PatternLayout">
       <param name="ConversionPattern" value="FALLBACK - %c - %m%n"/>
diff --git a/src/test/resources/input/xml/fallback1.xml 
b/src/test/resources/input/xml/fallback2.xml
similarity index 86%
copy from src/test/resources/input/xml/fallback1.xml
copy to src/test/resources/input/xml/fallback2.xml
index 90072a31..e0f37003 100644
--- a/src/test/resources/input/xml/fallback1.xml
+++ b/src/test/resources/input/xml/fallback2.xml
@@ -45,17 +45,24 @@
   </appender>
 
   <appender name="FALLBACK" class="org.apache.log4j.FileAppender">
-    <param name="File" value="output/fallback" />
+    <param name="File" value="output/fallback2" />
     <param name="Append" value="false" />
     <layout class="org.apache.log4j.PatternLayout">
       <param name="ConversionPattern" value="FALLBACK - %c - %m%n"/>
     </layout>
   </appender>
 
+  <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+    <param name="Target" value="System.out"/>
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="STDOUT - %m%n"/>
+    </layout>
+  </appender>
 
   <root>
     <priority value ="debug" />
     <appender-ref ref="PRIMARY" />
+    <appender-ref ref="STDOUT" />
   </root>
 
 </log4j:configuration>

Reply via email to