[prev in list] [next in list] [prev in thread] [next in thread]
List: log4cxx-dev
Subject: svn commit: r580508 - in /logging/log4cxx/trunk/src: main/cpp/
From: carnold () apache ! org
Date: 2007-09-28 22:57:58
Message-ID: 20070928225801.0C7011A9832 () eris ! apache ! org
[Download RAW message or body]
Author: carnold
Date: Fri Sep 28 15:57:53 2007
New Revision: 580508
URL: http://svn.apache.org/viewvc?rev=580508&view=rev
Log:
LOGCXX-129: AsyncAppender is full of race conditions
Added:
logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp
logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h
logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml (with \
props) Modified:
logging/log4cxx/trunk/src/main/cpp/Makefile.am
logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp
logging/log4cxx/trunk/src/main/cpp/condition.cpp
logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp
logging/log4cxx/trunk/src/main/cpp/exception.cpp
logging/log4cxx/trunk/src/main/cpp/file.cpp
logging/log4cxx/trunk/src/main/cpp/thread.cpp
logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h
logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h
logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h
logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h
logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h
logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp
logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp
Modified: logging/log4cxx/trunk/src/main/cpp/Makefile.am
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/Makefile.am?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/Makefile.am (original)
+++ logging/log4cxx/trunk/src/main/cpp/Makefile.am Fri Sep 28 15:57:53 2007
@@ -150,6 +150,7 @@
systemoutwriter.cpp \
telnetappender.cpp \
thread.cpp \
+ threadlocal.cpp \
threadspecificdata.cpp \
threadpatternconverter.cpp \
throwableinformationpatternconverter.cpp \
Modified: logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp Fri Sep 28 15:57:53 2007
@@ -16,6 +16,8 @@
*/
#include <log4cxx/asyncappender.h>
+
+
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/spi/loggingevent.h>
#include <apr_thread_proc.h>
@@ -23,7 +25,9 @@
#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>
using namespace log4cxx;
@@ -38,15 +42,17 @@
AsyncAppender::AsyncAppender()
: AppenderSkeleton(),
- pool(),
- queue(),
- size(DEFAULT_BUFFER_SIZE),
- available(pool),
- pending(pool),
- thread(),
- locationInfo(true),
- aai(new AppenderAttachableImpl()) {
- thread.run(dispatch, this);
+ buffer(),
+ bufferMutex(pool),
+ bufferNotFull(pool),
+ bufferNotEmpty(pool),
+ discardMap(),
+ bufferSize(DEFAULT_BUFFER_SIZE),
+ appenders(new AppenderAttachableImpl()),
+ dispatcher(),
+ locationInfo(false),
+ blocking(true) {
+ dispatcher.run(dispatch, this);
}
AsyncAppender::~AsyncAppender()
@@ -56,157 +62,283 @@
void AsyncAppender::addAppender(const AppenderPtr& newAppender)
{
- synchronized sync(aai->getMutex());
- aai->addAppender(newAppender);
+ synchronized sync(appenders->getMutex());
+ appenders->addAppender(newAppender);
}
-void AsyncAppender::append(const spi::LoggingEventPtr& event, Pool& /* p */ )
-{
+
+void AsyncAppender::setOption(const LogString& option,
+ const LogString& value) {
+ if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOCATIONINFO"), \
LOG4CXX_STR("locationinfo"))) { + \
setLocationInfo(OptionConverter::toBoolean(value, false)); + }
+ if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), \
LOG4CXX_STR("buffersize"))) { + \
setBufferSize(OptionConverter::toInt(value, DEFAULT_BUFFER_SIZE)); + }
+ if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BLOCKING"), \
LOG4CXX_STR("blocking"))) { + \
setBlocking(OptionConverter::toBoolean(value, true)); + } else {
+ AppenderSkeleton::setOption(option, value);
+ }
+}
+
+
+void AsyncAppender::append(const spi::LoggingEventPtr& event, Pool& p) {
+ //
+ // if dispatcher has died then
+ // append subsequent events synchronously
+ //
+ if (!dispatcher.isAlive() || bufferSize <= 0) {
+ synchronized sync(appenders->getMutex());
+ appenders->appendLoopOnAppenders(event, p);
+ return;
+ }
+
// Set the NDC and thread name for the calling thread as these
// LoggingEvent fields were not set at event creation time.
event->getNDC();
+ event->getThreadName();
// Get a copy of this thread's MDC.
event->getMDCCopy();
- //
- // will block if queue is full
- //
- size_t count = 0;
- bool full = false;
{
- synchronized sync(mutex);
- count = queue.size();
- full = count >= size;
- if (!full) {
- queue.push_back(event);
+ synchronized sync(bufferMutex);
+ while(true) {
+ int previousSize = buffer.size();
+ if (previousSize < bufferSize) {
+ buffer.push_back(event);
+ if (previousSize == 0) {
+ bufferNotEmpty.signalAll();
+ }
+ break;
+ }
+
+ //
+ // Following code is only reachable if buffer is full
+ //
+ //
+ // if blocking and thread is not already interrupted
+ // and not the dispatcher then
+ // wait for a buffer notification
+ bool discard = true;
+ if (blocking
+ && !Thread::interrupted()
+ && !dispatcher.isCurrentThread()) {
+ try {
+ bufferNotFull.await(bufferMutex);
+ discard = false;
+ } catch (InterruptedException& e) {
+ //
+ // reset interrupt status so
+ // calling code can see interrupt on
+ // their next wait or sleep.
+ Thread::currentThreadInterrupt();
+ }
}
- }
- //
- // if the queue had been empty then
- // notify that there are messages pending
- if (count == 0) {
- pending.broadcast();
+ //
+ // if blocking is false or thread has been interrupted
+ // add event to discard map.
+ //
+ if (discard) {
+ LogString loggerName = event->getLoggerName();
+ DiscardMap::iterator iter = discardMap.find(loggerName);
+ if (iter == discardMap.end()) {
+ DiscardSummary summary(event);
+ discardMap.insert(DiscardMap::value_type(loggerName, \
summary)); + } else {
+ (*iter).second.add(event);
+ }
+ break;
+ }
+ }
}
+ }
+
- //
- // if queue was full, wait until signalled
- //
- if (full) {
- available.wait();
+void AsyncAppender::close() {
+ {
+ synchronized sync(bufferMutex);
+ closed = true;
+ bufferNotEmpty.signalAll();
+ bufferNotFull.signalAll();
+ }
+
+ try {
+ dispatcher.join();
+ } catch(InterruptedException& e) {
+ Thread::currentThreadInterrupt();
+ LogLog::error("Got an InterruptedException while waiting for the dispatcher \
to finish,", e); + }
+
+ {
+ synchronized sync(appenders->getMutex());
+ AppenderList appenderList = appenders->getAllAppenders();
+ for (AppenderList::iterator iter = appenderList.begin();
+ iter != appenderList.end();
+ iter++) {
+ (*iter)->close();
}
+ }
+}
- //
- // if was full, add it now
- //
- if (full) {
- synchronized sync(mutex);
- queue.push_back(event);
- pending.broadcast();
- }
+AppenderList AsyncAppender::getAllAppenders() const
+{
+ synchronized sync(appenders->getMutex());
+ return appenders->getAllAppenders();
}
-void AsyncAppender::close()
+AppenderPtr AsyncAppender::getAppender(const LogString& name) const
{
- bool wasClosed = closed;
- closed = true;
- if (!wasClosed) {
- pending.broadcast();
- thread.join();
- // close and remove all appenders
- synchronized sync(mutex);
- aai->removeAllAppenders();
+ synchronized sync(appenders->getMutex());
+ return appenders->getAppender(name);
+}
- }
+bool AsyncAppender::isAttached(const AppenderPtr& appender) const
+{
+ synchronized sync(appenders->getMutex());
+ return appenders->isAttached(appender);
}
-AppenderList AsyncAppender::getAllAppenders() const
+bool AsyncAppender::requiresLayout() const {
+ return false;
+}
+
+void AsyncAppender::removeAllAppenders()
{
- synchronized sync(aai->getMutex());
- return aai->getAllAppenders();
+ synchronized sync(appenders->getMutex());
+ appenders->removeAllAppenders();
}
-AppenderPtr AsyncAppender::getAppender(const LogString& name1) const
+void AsyncAppender::removeAppender(const AppenderPtr& appender)
{
- synchronized sync(aai->getMutex());
- return aai->getAppender(name1);
+ synchronized sync(appenders->getMutex());
+ appenders->removeAppender(appender);
}
-bool AsyncAppender::isAttached(const AppenderPtr& appender) const
+void AsyncAppender::removeAppender(const LogString& name)
{
- synchronized sync(aai->getMutex());
- return aai->isAttached(appender);
+ synchronized sync(appenders->getMutex());
+ appenders->removeAppender(name);
}
-void AsyncAppender::setBufferSize(int size1)
+bool AsyncAppender::getLocationInfo() const {
+ return locationInfo;
+}
+
+void AsyncAppender::setLocationInfo(bool flag) {
+ locationInfo = flag;
+}
+
+
+void AsyncAppender::setBufferSize(int size)
{
- if (size1 < 0) {
+ if (size < 0) {
throw IllegalArgumentException("size argument must be non-negative");
}
- this->size = size1;
+ synchronized sync(bufferMutex);
+ bufferSize = (size < 1) ? 1 : size;
+ bufferNotFull.signalAll();
}
int AsyncAppender::getBufferSize() const
{
- return size;
+ return bufferSize;
}
-void AsyncAppender::removeAllAppenders()
-{
- synchronized sync(aai->getMutex());
- aai->removeAllAppenders();
+void AsyncAppender::setBlocking(bool value) {
+ synchronized sync(bufferMutex);
+ blocking = value;
+ bufferNotFull.signalAll();
}
-void AsyncAppender::removeAppender(const AppenderPtr& appender)
-{
- synchronized sync(aai->getMutex());
- aai->removeAppender(appender);
+bool AsyncAppender::getBlocking() const {
+ return blocking;
}
-void AsyncAppender::removeAppender(const LogString& name1)
-{
- synchronized sync(aai->getMutex());
- aai->removeAppender(name1);
+AsyncAppender::DiscardSummary::DiscardSummary(const LoggingEventPtr& event) :
+ maxEvent(event), count(1) {
}
-void* LOG4CXX_THREAD_FUNC AsyncAppender::dispatch(log4cxx_thread_t* /* thread */ , \
void* data) {
- AsyncAppender* pThis = (AsyncAppender*) data;
- LoggingEventPtr event;
- while(true) {
-
- size_t count = 0;
- {
- synchronized sync(pThis->mutex);
- count = pThis->queue.size();
- if (count > 0) {
- event = pThis->queue.front();
- pThis->queue.pop_front();
- }
- }
+AsyncAppender::DiscardSummary::DiscardSummary(const DiscardSummary& src) :
+ maxEvent(src.maxEvent), count(src.count) {
+}
- if (count == 0) {
- if (pThis->closed) {
- return 0;
- }
- pThis->pending.wait();
- } else {
- if(pThis->aai != 0) {
- synchronized sync(pThis->aai->getMutex());
- Pool p;
- pThis->aai->appendLoopOnAppenders(event, p);
- }
-
- if (count == (size_t) pThis->getBufferSize()) {
- pThis->available.broadcast();
- }
+AsyncAppender::DiscardSummary& AsyncAppender::DiscardSummary::operator=(const \
DiscardSummary& src) { + maxEvent = src.maxEvent;
+ count = src.count;
+ return *this;
+}
- LoggingEventPtr nullEvent;
- event = nullEvent;
- }
- }
+void AsyncAppender::DiscardSummary::add(const LoggingEventPtr& event) {
+ if (event->getLevel()->toInt() > maxEvent->getLevel()->toInt()) {
+ maxEvent = event;
+ }
+ count++;
}
-#endif
+LoggingEventPtr AsyncAppender::DiscardSummary::createEvent(Pool& p) {
+ LogString msg(LOG4CXX_STR("Discarded "));
+ StringHelper::toString(count, p, msg);
+ msg.append(LOG4CXX_STR(" messages due to a full event buffer including: "));
+ msg.append(maxEvent->getMessage());
+ return new LoggingEvent(
+ Logger::getLogger(maxEvent->getLoggerName()),
+ maxEvent->getLevel(),
+ msg,
+ LocationInfo::getLocationUnavailable());
+}
+
+void* LOG4CXX_THREAD_FUNC AsyncAppender::dispatch(log4cxx_thread_t* /* thread */ , \
void* data) { + AsyncAppender* pThis = (AsyncAppender*) data;
+ bool isActive = true;
+ try {
+ while (isActive) {
+ //
+ // process events after lock on buffer is released.
+ //
+ Pool p;
+ LoggingEventList events;
+ {
+ synchronized sync(pThis->bufferMutex);
+ size_t bufferSize = pThis->buffer.size();
+ isActive = !pThis->closed;
+
+ while((bufferSize == 0) && isActive) {
+ pThis->bufferNotEmpty.await(pThis->bufferMutex);
+ bufferSize = pThis->buffer.size();
+ isActive = !pThis->closed;
+ }
+ for(LoggingEventList::iterator eventIter = pThis->buffer.begin();
+ eventIter != pThis->buffer.end();
+ eventIter++) {
+ events.push_back(*eventIter);
+ }
+ for(DiscardMap::iterator discardIter = pThis->discardMap.begin();
+ discardIter != pThis->discardMap.end();
+ discardIter++) {
+ events.push_back(discardIter->second.createEvent(p));
+ }
+ pThis->buffer.clear();
+ pThis->discardMap.clear();
+ pThis->bufferNotFull.signalAll();
+ }
+
+ for (LoggingEventList::iterator iter = events.begin();
+ iter != events.end();
+ iter++) {
+ synchronized sync(pThis->appenders->getMutex());
+ pThis->appenders->appendLoopOnAppenders(*iter, p);
+ }
+ }
+ } catch(InterruptedException& ex) {
+ Thread::currentThreadInterrupt();
+ } catch(...) {
+ }
+ return 0;
+}
+
+#endif
Modified: logging/log4cxx/trunk/src/main/cpp/condition.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/condition.cpp?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/condition.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/condition.cpp Fri Sep 28 15:57:53 2007
@@ -21,6 +21,7 @@
#include <apr_thread_cond.h>
#include <log4cxx/helpers/synchronized.h>
#include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/thread.h>
using namespace log4cxx::helpers;
using namespace log4cxx;
@@ -31,18 +32,10 @@
Condition::Condition(Pool& p)
{
apr_pool_t* aprPool = (apr_pool_t*) p.getAPRPool();
- apr_thread_mutex_t* aprMutex = NULL;
- apr_status_t stat = apr_thread_mutex_create(&aprMutex,
- APR_THREAD_MUTEX_DEFAULT, aprPool);
- if (stat != APR_SUCCESS) {
- throw ConditionException(stat);
- }
- mutex = aprMutex;
apr_thread_cond_t* aprCondition = NULL;
- stat = apr_thread_cond_create(&aprCondition, aprPool);
+ apr_status_t stat = apr_thread_cond_create(&aprCondition, aprPool);
if (stat != APR_SUCCESS) {
- stat = apr_thread_mutex_destroy(aprMutex);
- throw ConditionException(stat);
+ throw RuntimeException(stat);
}
condition = aprCondition;
}
@@ -50,33 +43,23 @@
Condition::~Condition()
{
apr_thread_cond_destroy((apr_thread_cond_t*) condition);
- apr_thread_mutex_destroy((apr_thread_mutex_t*)mutex);
}
-void Condition::broadcast()
+log4cxx_status_t Condition::signalAll()
{
- apr_status_t stat = apr_thread_cond_broadcast((apr_thread_cond_t*) \
condition);
- if (stat != APR_SUCCESS) {
- throw ConditionException(stat);
- }
+ return apr_thread_cond_broadcast((apr_thread_cond_t*) condition);
}
-
-void Condition::wait()
+void Condition::await(Mutex& mutex)
{
- apr_status_t stat = apr_thread_mutex_lock((apr_thread_mutex_t*) mutex);
- if (stat != APR_SUCCESS) {
- throw MutexException(stat);
+ if (Thread::interrupted()) {
+ throw InterruptedException();
}
- stat = apr_thread_cond_wait(
+ apr_status_t stat = apr_thread_cond_wait(
(apr_thread_cond_t*) condition,
- (apr_thread_mutex_t*) mutex);
- if (stat != APR_SUCCESS) {
- throw ConditionException(stat);
- }
- stat = apr_thread_mutex_unlock((apr_thread_mutex_t*) mutex);
+ (apr_thread_mutex_t*) mutex.getAPRMutex());
if (stat != APR_SUCCESS) {
- throw MutexException(stat);
+ throw InterruptedException(stat);
}
}
Modified: logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp Fri Sep 28 15:57:53 2007
@@ -111,21 +111,22 @@
/**
Used internally to parse appenders by IDREF name.
*/
-AppenderPtr DOMConfigurator::findAppenderByName(apr_xml_elem* element,
+AppenderPtr DOMConfigurator::findAppenderByName(apr_xml_elem* element,
+ apr_xml_doc* doc,
const LogString& appenderName,
AppenderMap& appenders) {
AppenderPtr appender;
std::string tagName(element->name);
if (tagName == APPENDER_TAG) {
if (appenderName == getAttribute(element, NAME_ATTR)) {
- appender = parseAppender(element, 0, appenders);
+ appender = parseAppender(element, doc, appenders);
}
}
if (element->first_child && !appender) {
- appender = findAppenderByName(element->first_child, appenderName, \
appenders); + appender = findAppenderByName(element->first_child, doc, \
appenderName, appenders); }
if (element->next && !appender) {
- appender = findAppenderByName(element->next, appenderName, appenders);
+ appender = findAppenderByName(element->next, doc, appenderName, appenders);
}
return appender;
}
@@ -143,7 +144,7 @@
if (match != appenders.end()) {
appender = match->second;
} else if (doc) {
- appender = findAppenderByName(doc->root, appenderName, appenders);
+ appender = findAppenderByName(doc->root, doc, appenderName, appenders);
if (appender) {
appenders.insert(AppenderMap::value_type(appenderName, appender));
}
Modified: logging/log4cxx/trunk/src/main/cpp/exception.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/exception.cpp?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/exception.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/exception.cpp Fri Sep 28 15:57:53 2007
@@ -54,6 +54,9 @@
return msg;
}
+RuntimeException::RuntimeException(log4cxx_status_t stat)
+ : Exception(formatMessage(stat)) {
+}
RuntimeException::RuntimeException(const std::string& msg1)
: Exception(msg1) {
@@ -68,6 +71,13 @@
return *this;
}
+std::string RuntimeException::formatMessage(log4cxx_status_t stat) {
+ std::string s("RuntimeException: return code = ");
+ Pool p;
+ StringHelper::toString(stat, p, s);
+ return s;
+}
+
NullPointerException::NullPointerException(const std::string& msg1)
: RuntimeException(msg1) {
}
@@ -201,21 +211,24 @@
return s;
}
-ConditionException::ConditionException(log4cxx_status_t stat)
+InterruptedException::InterruptedException() : Exception("Thread was interrupted") {
+}
+
+InterruptedException::InterruptedException(log4cxx_status_t stat)
: Exception(formatMessage(stat)) {
}
-ConditionException::ConditionException(const ConditionException &src)
+InterruptedException::InterruptedException(const InterruptedException &src)
: Exception(src) {
}
-ConditionException& ConditionException::operator=(const MutexException& src) {
+InterruptedException& InterruptedException::operator=(const InterruptedException& \
src) { Exception::operator=(src);
return *this;
}
-std::string ConditionException::formatMessage(log4cxx_status_t stat) {
- std::string s("Condition exception: stat = ");
+std::string InterruptedException::formatMessage(log4cxx_status_t stat) {
+ std::string s("InterruptedException: stat = ");
Pool p;
StringHelper::toString(stat, p, s);
return s;
Modified: logging/log4cxx/trunk/src/main/cpp/file.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/file.cpp?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/file.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/file.cpp Fri Sep 28 15:57:53 2007
@@ -174,4 +174,4 @@
apr_status_t stat = apr_dir_make_recursive(convertBackSlashes(osName).c_str(),
APR_OS_DEFAULT, (apr_pool_t*) p.getAPRPool());
return stat == APR_SUCCESS;
-}
\ No newline at end of file
+}
Modified: logging/log4cxx/trunk/src/main/cpp/thread.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/thread.cpp?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/thread.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/thread.cpp Fri Sep 28 15:57:53 2007
@@ -19,12 +19,14 @@
#include <log4cxx/helpers/thread.h>
#include <log4cxx/helpers/exception.h>
#include <apr_thread_proc.h>
+#include <apr_atomic.h>
#include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/threadlocal.h>
using namespace log4cxx::helpers;
using namespace log4cxx;
-Thread::Thread() : thread(NULL), alive(false) {
+Thread::Thread() : thread(NULL), alive(0), interruptedStatus(0) {
}
Thread::~Thread() {
@@ -33,28 +35,69 @@
#endif
}
-#if APR_HAS_THREADS
+Thread::LaunchPackage::LaunchPackage(Thread* t, Runnable r, void* d) : thread(t), \
runnable(r), data(d) { +}
+
+Thread* Thread::LaunchPackage::getThread() const {
+ return thread;
+}
+
+Runnable Thread::LaunchPackage::getRunnable() const {
+ return runnable;
+}
+
+void* Thread::LaunchPackage::getData() const {
+ return data;
+}
+
+void* Thread::LaunchPackage::operator new(size_t sz, Pool& p) {
+ return p.palloc(sz);
+}
+
void Thread::run(Runnable start, void* data) {
- if (thread != NULL && alive) {
- throw ThreadException(0);
+ //
+ // if attempting a second run method on the same Thread object
+ // throw an exception
+ //
+ if (thread != NULL) {
+ throw IllegalStateException();
}
apr_threadattr_t* attrs;
apr_status_t stat = apr_threadattr_create(&attrs, (apr_pool_t*) \
p.getAPRPool()); if (stat != APR_SUCCESS) {
throw ThreadException(stat);
}
- alive = true;
+
+ // create LaunchPackage on the thread's memory pool
+ LaunchPackage* package = new(p) LaunchPackage(this, start, data);
stat = apr_thread_create((apr_thread_t**) &thread, attrs,
- (apr_thread_start_t) start, data, (apr_pool_t*) p.getAPRPool());
+ (apr_thread_start_t) launcher, package, (apr_pool_t*) p.getAPRPool());
if (stat != APR_SUCCESS) {
throw ThreadException(stat);
}
}
+
+Thread::LaunchStatus::LaunchStatus(volatile unsigned int* p) : alive(p) {
+ apr_atomic_set32(alive, 0xFFFFFFFF);
+}
+
+Thread::LaunchStatus::~LaunchStatus() {
+ apr_atomic_set32(alive, 0);
+}
+
+
+void* Thread::launcher(log4cxx_thread_t* thread, void* data) {
+ LaunchPackage* package = (LaunchPackage*) data;
+ ThreadLocal& tls = getThreadLocal();
+ tls.set(package->getThread());
+ LaunchStatus alive(&package->getThread()->alive);
+ return (package->getRunnable())(thread, package->getData());
+}
+
void Thread::stop() {
- if (thread != NULL && alive) {
+ if (thread != NULL) {
apr_status_t stat = apr_thread_exit((apr_thread_t*) thread, 0);
- alive = false;
thread = NULL;
if (stat != APR_SUCCESS) {
throw ThreadException(stat);
@@ -63,23 +106,59 @@
}
void Thread::join() {
- if (thread != NULL && alive) {
+ if (thread != NULL) {
apr_status_t startStat;
apr_status_t stat = apr_thread_join(&startStat, (apr_thread_t*) \
thread);
- alive = false;
thread = NULL;
if (stat != APR_SUCCESS) {
throw ThreadException(stat);
}
}
}
-#endif
+
+ThreadLocal& Thread::getThreadLocal() {
+ static ThreadLocal tls;
+ return tls;
+}
+
+void Thread::currentThreadInterrupt() {
+ void* tls = getThreadLocal().get();
+ if (tls != 0) {
+ ((Thread*) tls)->interrupt();
+ }
+}
+
+void Thread::interrupt() {
+ apr_atomic_set32(&interruptedStatus, 0xFFFFFFFF);
+}
+
+bool Thread::interrupted() {
+ void* tls = getThreadLocal().get();
+ if (tls != 0) {
+ return apr_atomic_xchg32(&(((Thread*) tls)->interruptedStatus), 0) != 0;
+ }
+ return false;
+}
+
+bool Thread::isCurrentThread() const {
+ void* tls = getThreadLocal().get();
+ return (tls == this);
+}
+
+bool Thread::isAlive() {
+ return apr_atomic_read32(&alive) != 0;
+}
void Thread::ending() {
- alive = false;
+ apr_atomic_set32(&alive, 0);
}
-void Thread::sleep(log4cxx_time_t duration) {
- apr_sleep(duration);
+void Thread::sleep(int duration) {
+ if(interrupted()) {
+ throw InterruptedException();
+ }
+ if (duration > 0) {
+ apr_sleep(duration*1000);
+ }
}
Added: logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp?rev=580508&view=auto
==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp (added)
+++ logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp Fri Sep 28 15:57:53 2007
@@ -0,0 +1,55 @@
+/*
+ * 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/helpers/threadlocal.h"
+#include "apr_thread_proc.h"
+#include "log4cxx/helpers/exception.h"
+
+using namespace log4cxx::helpers;
+using namespace log4cxx;
+
+ThreadLocal::ThreadLocal() {
+ apr_status_t stat = apr_pool_create((apr_pool_t**) &pool, 0);
+ if (stat != APR_SUCCESS) {
+ throw RuntimeException(stat);
+ }
+ stat = apr_threadkey_private_create((apr_threadkey_t**) &key, 0, (apr_pool_t*) \
pool); + if (stat != APR_SUCCESS) {
+ throw RuntimeException(stat);
+ }
+}
+
+ThreadLocal::~ThreadLocal() {
+ apr_pool_destroy((apr_pool_t*) pool);
+}
+
+void ThreadLocal::set(void* priv) {
+ apr_status_t stat = apr_threadkey_private_set(priv, (apr_threadkey_t*) key);
+ if (stat != APR_SUCCESS) {
+ throw RuntimeException(stat);
+ }
+}
+
+void* ThreadLocal::get() {
+ void* retval = 0;
+ apr_status_t stat = apr_threadkey_private_get(&retval, (apr_threadkey_t*) key);
+ if (stat != APR_SUCCESS) {
+ throw RuntimeException(stat);
+ }
+ return retval;
+}
+
Modified: logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h Fri Sep 28 \
15:57:53 2007 @@ -58,11 +58,26 @@
LOG4CXX_CAST_ENTRY(spi::AppenderAttachable)
END_LOG4CXX_CAST_MAP()
+ /**
+ * Create new instance.
+ */
AsyncAppender();
+
+ /**
+ * Destructor.
+ */
virtual ~AsyncAppender();
+ /**
+ * Add appender.
+ *
+ * @param newAppender appender to add, may not be null.
+ */
void addAppender(const AppenderPtr& newAppender);
+ /**
+ * {@inheritDoc}
+ */
void append(const spi::LoggingEventPtr& event, \
log4cxx::helpers::Pool& p);
/**
@@ -72,38 +87,61 @@
*/
void close();
+ /**
+ * Get iterator over attached appenders.
+ * @return list of all attached appenders.
+ */
AppenderList getAllAppenders() const;
+
+ /**
+ * Get appender by name.
+ *
+ * @param name name, may not be null.
+ * @return matching appender or null.
+ */
AppenderPtr getAppender(const LogString& name) const;
/**
- Returns the current value of the <b>LocationInfo</b> option.
+ * Gets whether the location of the logging request call
+ * should be captured.
+ *
+ * @return the current value of the <b>LocationInfo</b> option.
*/
- inline bool getLocationInfo() const
- { return locationInfo; }
-
+ bool getLocationInfo() const;
/**
- Is the appender passed as parameter attached to this asyncappender?
+ * Determines if specified appender is attached.
+ * @param appender appender.
+ * @return true if attached.
*/
bool isAttached(const AppenderPtr& appender) const;
+ /**
+ * {@inheritDoc}
+ */
+ virtual bool requiresLayout() const;
+
+ /**
+ * Removes and closes all attached appenders.
+ */
void removeAllAppenders();
- void removeAppender(const AppenderPtr& appender);
- void removeAppender(const LogString& name);
/**
- The <code>AsyncAppender</code> does not require a layout. Hence,
- this method always returns <code>false</code>.
+ * Removes an appender.
+ * @param appender appender to remove.
+ */
+ void removeAppender(const AppenderPtr& appender);
+ /**
+ * Remove appender by name.
+ * @param name name.
*/
- virtual bool requiresLayout() const
- { return false; }
+ void removeAppender(const LogString& name);
/**
* The <b>LocationInfo</b> attribute is provided for compatibility
- * with log4j and has no effect.
- * */
- inline void setLocationInfo(bool flag) {
- locationInfo = flag;
- }
+ * with log4j and has no effect on the log output.
+ * @param flag new value.
+ */
+ void setLocationInfo(bool flag);
/**
* The <b>BufferSize</b> option takes a non-negative integer value.
@@ -113,30 +151,129 @@
void setBufferSize(int size);
/**
- Returns the current value of the <b>BufferSize</b> option.
+ * Gets the current buffer size.
+ * @return the current value of the <b>BufferSize</b> option.
*/
int getBufferSize() const;
+ /**
+ * Sets whether appender should wait if there is no
+ * space available in the event buffer or immediately return.
+ *
+ * @param value true if appender should wait until available space \
in buffer. + */
+ void setBlocking(bool value);
+
+ /**
+ * Gets whether appender should block calling thread when buffer is \
full. + * If false, messages will be counted by logger and a summary
+ * message appended after the contents of the buffer have been \
appended. + *
+ * @return true if calling thread will be blocked when buffer is \
full. + */
+ bool getBlocking() const;
+
+
+ /**
+ * Set appender properties by name.
+ * @param option property name.
+ * @param value property value.
+ */
+ void setOption(const LogString& option, const LogString& value);
+
+
private:
- log4cxx::helpers::Pool pool;
- std::deque<log4cxx::spi::LoggingEventPtr> queue;
- size_t size;
- //
- // Condition is signaled when there is room available on the queue
- //
- log4cxx::helpers::Condition available;
- //
- // Condition is signaled when there is at least one event in the \
queue.
- //
- log4cxx::helpers::Condition pending;
+ /**
+ * The default buffer size is set to 128 events.
+ */
+ enum { DEFAULT_BUFFER_SIZE = 128 };
+
+ /**
+ * Event buffer.
+ */
+ typedef std::vector<log4cxx::spi::LoggingEventPtr> LoggingEventList;
+ LoggingEventList buffer;
- helpers::Thread thread;
+ /**
+ * Mutex used to guard access to buffer and discardMap.
+ */
+ ::log4cxx::helpers::Mutex bufferMutex;
+ ::log4cxx::helpers::Condition bufferNotFull;
+ ::log4cxx::helpers::Condition bufferNotEmpty;
+
+ class DiscardSummary {
+ private:
+ /**
+ * First event of the highest severity.
+ */
+ ::log4cxx::spi::LoggingEventPtr maxEvent;
+
+ /**
+ * Total count of messages discarded.
+ */
+ int count;
+
+ public:
+ /**
+ * Create new instance.
+ *
+ * @param event event, may not be null.
+ */
+ DiscardSummary(const ::log4cxx::spi::LoggingEventPtr& event);
+ /** Copy constructor. */
+ DiscardSummary(const DiscardSummary& src);
+ /** Assignment operator. */
+ DiscardSummary& operator=(const DiscardSummary& src);
+
+ /**
+ * Add discarded event to summary.
+ *
+ * @param event event, may not be null.
+ */
+ void add(const ::log4cxx::spi::LoggingEventPtr& event);
+
+ /**
+ * Create event with summary information.
+ *
+ * @return new event.
+ */
+ ::log4cxx::spi::LoggingEventPtr \
createEvent(::log4cxx::helpers::Pool& p); + };
+
+ /**
+ * Map of DiscardSummary objects keyed by logger name.
+ */
+ typedef std::map<LogString, DiscardSummary> DiscardMap;
+ DiscardMap discardMap;
+
+ /**
+ * Buffer size.
+ */
+ int bufferSize;
+ /**
+ * Nested appenders.
+ */
+ helpers::AppenderAttachableImplPtr appenders;
+
+ /**
+ * Dispatcher.
+ */
+ helpers::Thread dispatcher;
+
+ /**
+ * Should location info be included in dispatched messages.
+ */
bool locationInfo;
- helpers::AppenderAttachableImplPtr aai;
- enum { DEFAULT_BUFFER_SIZE = 128 };
+ /**
+ * Does appender block when buffer is full.
+ */
+ bool blocking;
+ /**
+ * Dispatch routine.
+ */
static void* LOG4CXX_THREAD_FUNC dispatch(helpers::log4cxx_thread_t* \
thread, void* data);
}; // class AsyncAppender
Modified: logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h Fri Sep 28 \
15:57:53 2007 @@ -19,7 +19,10 @@
#define _LOG4CXX_HELPERS_CONDITION_H
#include <log4cxx/log4cxx.h>
+#include <log4cxx/helpers/mutex.h>
+typedef void log4cxx_pool_t;
+typedef void log4cxx_thread_cond_t;
namespace log4cxx
{
@@ -27,17 +30,39 @@
{
class Pool;
+ /**
+ * This class provides a means for one thread to suspend exception \
until + * notified by another thread to result. This class should \
have + * similar semantics to \
java.util.concurrent.locks.Condition. + */
class LOG4CXX_EXPORT Condition
{
public:
+ /**
+ * Create new instance.
+ * @param p pool on which condition will be created. Needs \
to be + * longer-lived than created instance.
+ */
Condition(log4cxx::helpers::Pool& p);
+ /**
+ * Destructor.
+ */
~Condition();
- void broadcast();
- void wait();
+ /**
+ * Signal all waiting threads.
+ */
+ log4cxx_status_t signalAll();
+ /**
+ * Await signaling of condition.
+ * @param lock lock associated with condition, calling \
thread must + * own lock. Lock will be released while \
waiting and reacquired + * before returning from wait.
+ * @throws InterruptedException if thread is interrupted.
+ */
+ void await(Mutex& lock);
private:
- void* condition;
- void* mutex;
+ log4cxx_thread_cond_t* condition;
Condition(const Condition&);
Condition& operator=(const Condition&);
};
Modified: logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h Fri Sep 28 \
15:57:53 2007 @@ -47,9 +47,12 @@
class LOG4CXX_EXPORT RuntimeException : public Exception
{
public:
+ RuntimeException(log4cxx_status_t stat);
RuntimeException(const std::string& msg);
RuntimeException(const RuntimeException& msg);
RuntimeException& operator=(const RuntimeException& src);
+ private:
+ static std::string formatMessage(log4cxx_status_t stat);
}; // class RuntimeException
/** Thrown when an application attempts to use null in a case where \
an @@ -120,12 +123,13 @@
static std::string formatMessage(log4cxx_status_t stat);
};
- class LOG4CXX_EXPORT ConditionException : public Exception
+ class LOG4CXX_EXPORT InterruptedException : public Exception
{
public:
- ConditionException(log4cxx_status_t stat);
- ConditionException(const ConditionException &src);
- ConditionException& operator=(const MutexException&);
+ InterruptedException();
+ InterruptedException(log4cxx_status_t stat);
+ InterruptedException(const InterruptedException &src);
+ InterruptedException& operator=(const InterruptedException&);
private:
static std::string formatMessage(log4cxx_status_t stat);
};
Modified: logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h Fri Sep 28 \
15:57:53 2007 @@ -35,33 +35,138 @@
namespace helpers
{
class Pool;
+ class ThreadLocal;
typedef void log4cxx_thread_t;
typedef void* (LOG4CXX_THREAD_FUNC *Runnable)(log4cxx_thread_t* thread, void* \
data); + /**
+ * This class implements an approximation of java.util.Thread.
+ */
class LOG4CXX_EXPORT Thread
{
public:
+ /**
+ * Create new instance.
+ */
Thread();
+ /**
+ * Destructor.
+ */
~Thread();
+ /**
+ * Runs the specified method on a newly created thread.
+ */
void run(Runnable start, void* data);
void stop();
void join();
- //
- // called on the worker thread to indicate
- // immediate exit from the start method
- void ending();
inline bool isActive() { return thread != 0; }
- static void sleep(log4cxx_time_t duration);
+ /**
+ * Causes the currently executing thread to sleep for the
+ * specified number of milliseconds.
+ * @param millis milliseconds.
+ * @throws Interrupted Exception if the thread is \
interrupted. + */
+ static void sleep(int millis);
+ /**
+ * Sets interrupted status for current thread to true.
+ */
+ static void currentThreadInterrupt();
+ /**
+ * Sets interrupted status to true.
+ */
+ void interrupt();
+ /**
+ * Tests if the current thread has been interrupted and
+ * sets the interrupted status to false.
+ */
+ static bool interrupted();
+
+ bool isAlive();
+ bool isCurrentThread() const;
+ void ending();
+
private:
Pool p;
log4cxx_thread_t* thread;
- volatile bool alive;
+ volatile unsigned int alive;
+ volatile unsigned int interruptedStatus;
Thread(const Thread&);
Thread& operator=(const Thread&);
+
+ /**
+ * This class is used to encapsulate the parameters to
+ * Thread::run when they are passed to Thread::launcher.
+ *
+ */
+ class LaunchPackage {
+ public:
+ /**
+ * Placement new to create LaunchPackage in specified \
pool. + * LaunchPackage needs to be dynamically \
allocated since + * since a stack allocated instance may \
go out of scope + * before thread is launched.
+ */
+ static void* operator new(size_t, Pool& p);
+ /**
+ * Create new instance.
+ */
+ LaunchPackage(Thread* thread, Runnable runnable, void* \
data); + /**
+ * Gets thread parameter.
+ * @return thread.
+ */
+ Thread* getThread() const;
+ /**
+ * Gets runnable parameter.
+ * @return runnable.
+ */
+ Runnable getRunnable() const;
+ /**
+ * gets data parameter.
+ * @return thread.
+ */
+ void* getData() const;
+ private:
+ Thread* thread;
+ Runnable runnable;
+ void* data;
+ };
+
+ /**
+ * This object atomically sets the specified memory \
location + * to non-zero on construction and to zero on \
destruction. + * Used to maintain Thread.alive.
+ */
+ class LaunchStatus {
+ public:
+ /*
+ * Construct new instance.
+ * @param p address of memory to set to non-zero on \
construction, zero on destruction. + */
+ LaunchStatus(volatile unsigned int* p);
+ /**
+ * Destructor.
+ */
+ ~LaunchStatus();
+ private:
+ volatile unsigned int* alive;
+ };
+
+ /**
+ * This method runs on the created thread and sets up \
thread-local storage + * used to keep the reference to the \
corresponding Thread object and + * is responsible for \
maintaining Thread.alive. + */
+ static void* LOG4CXX_THREAD_FUNC launcher(log4cxx_thread_t* \
thread, void* data); + /**
+ * Get a key to the thread local storage used to hold the \
reference to + * the corresponding Thread object.
+ */
+ static ThreadLocal& getThreadLocal();
};
} // namespace helpers
} // namespace log4cxx
Added: logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h?rev=580508&view=auto
==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h (added)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h Fri Sep 28 \
15:57:53 2007 @@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef _LOG4CXX_HELPERS_THREAD_LOCAL_H
+#define _LOG4CXX_HELPERS_THREAD_LOCAL_H
+
+#include <log4cxx/log4cxx.h>
+#include <log4cxx/helpers/pool.h>
+
+#if !defined(LOG4CXX_THREAD_FUNC)
+#if defined(_WIN32)
+#define LOG4CXX_THREAD_FUNC __stdcall
+#else
+#define LOG4CXX_THREAD_FUNC
+#endif
+#endif
+
+
+namespace log4cxx
+{
+ namespace helpers
+ {
+ typedef void log4cxx_threadkey_t;
+ typedef void log4cxx_pool_t;
+
+ /**
+ * This class provides thread-local variables. This class is \
similar in function + * to java.lang.ThreadLocal.
+ */
+ class LOG4CXX_EXPORT ThreadLocal {
+ public:
+ /**
+ * Create new instance.
+ */
+ ThreadLocal();
+ /**
+ * Destructor.
+ */
+ ~ThreadLocal();
+ /**
+ * Sets the value in the current thread's copy of this \
thread-local variable. + * @param priv new value.
+ */
+ void set(void* priv);
+ /**
+ * Returns the value in the current thread's copy of this \
thread-local variable. + * @return value of thread-local \
variable for the current thread. + */
+ void* get();
+
+ private:
+ log4cxx_pool_t* pool;
+ log4cxx_threadkey_t* key;
+ };
+ } // namespace helpers
+} // namespace log4cxx
+
+#endif //_LOG4CXX_HELPERS_THREAD_LOCAL_H
Modified: logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h Fri Sep 28 \
15:57:53 2007 @@ -95,6 +95,7 @@
Used internally to parse appenders by IDREF name.
*/
AppenderPtr findAppenderByName(apr_xml_elem* elem,
+ apr_xml_doc* doc,
const LogString& appenderName,
AppenderMap& appenders);
Modified: logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp (original)
+++ logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp Fri Sep 28 15:57:53 \
2007 @@ -28,12 +28,78 @@
#include <apr_strings.h>
#include "testchar.h"
#include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/helpers/synchronized.h>
+#include <log4cxx/spi/location/locationinfo.h>
+#include <log4cxx/xml/domconfigurator.h>
+#include <log4cxx/file.h>
using namespace log4cxx;
using namespace log4cxx::helpers;
+using namespace log4cxx::spi;
+
+class NullPointerAppender : public AppenderSkeleton {
+public:
+ NullPointerAppender() {
+ }
+
+
+ /**
+ * @{inheritDoc}
+ */
+ void append(const spi::LoggingEventPtr&, log4cxx::helpers::Pool&) {
+ throw NullPointerException("Intentional NullPointerException");
+ }
+
+ void close() {
+ }
+
+ bool requiresLayout() const {
+ return false;
+ }
+};
+
+ /**
+ * Vector appender that can be explicitly blocked.
+ */
+class BlockableVectorAppender : public VectorAppender {
+private:
+ Mutex blocker;
+public:
+ /**
+ * Create new instance.
+ */
+ BlockableVectorAppender() : blocker(pool) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ void append(const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& p) {
+ synchronized sync(blocker);
+ VectorAppender::append(event, p);
+ //
+ // if fatal, echo messages for testLoggingInDispatcher
+ //
+ if (event->getLevel() == Level::getInfo()) {
+ LoggerPtr logger = Logger::getLogger(event->getLoggerName());
+ LOG4CXX_ERROR(logger, event->getMessage());
+ LOG4CXX_WARN(logger, event->getMessage());
+ LOG4CXX_INFO(logger, event->getMessage());
+ LOG4CXX_DEBUG(logger, event->getMessage());
+ }
+ }
+
+ Mutex& getBlocker() {
+ return blocker;
+ }
+
+ };
+
+typedef helpers::ObjectPtrT<BlockableVectorAppender> BlockableVectorAppenderPtr;
+
/**
- A superficial but general test of log4j.
+ * Tests of AsyncAppender.
*/
class AsyncAppenderTestCase : public AppenderSkeletonTestCase
{
@@ -46,10 +112,10 @@
CPPUNIT_TEST(closeTest);
CPPUNIT_TEST(test2);
-// TODO: deadlocks on Win32, will debug on Linux
-#if !defined(_WIN32)
CPPUNIT_TEST(test3);
-#endif
+ CPPUNIT_TEST(testBadAppender);
+ CPPUNIT_TEST(testLocationInfoTrue);
+ CPPUNIT_TEST(testConfiguration);
CPPUNIT_TEST_SUITE_END();
@@ -69,7 +135,7 @@
}
// this test checks whether it is possible to write to a closed \
AsyncAppender
- void closeTest() throw(Exception)
+ void closeTest()
{
LoggerPtr root = Logger::getRootLogger();
LayoutPtr layout = new SimpleLayout();
@@ -84,7 +150,7 @@
root->debug(LOG4CXX_TEST_STR("m2"));
const std::vector<spi::LoggingEventPtr>& v = \
vectorAppender->getVector();
- CPPUNIT_ASSERT(v.size() == 1);
+ CPPUNIT_ASSERT_EQUAL((size_t) 1, v.size());
}
// this test checks whether appenders embedded within an AsyncAppender are \
also @@ -104,7 +170,7 @@
root->debug(LOG4CXX_TEST_STR("m2"));
const std::vector<spi::LoggingEventPtr>& v = \
vectorAppender->getVector();
- CPPUNIT_ASSERT(v.size() == 1);
+ CPPUNIT_ASSERT_EQUAL((size_t) 1, v.size());
CPPUNIT_ASSERT(vectorAppender->isClosed());
}
@@ -112,23 +178,16 @@
// closed
void test3()
{
- typedef std::vector<spi::LoggingEventPtr>::size_type size_type;
size_t LEN = 200;
LoggerPtr root = Logger::getRootLogger();
- LayoutPtr layout = new SimpleLayout();
VectorAppenderPtr vectorAppender = new VectorAppender();
AsyncAppenderPtr asyncAppender = new AsyncAppender();
asyncAppender->setName(LOG4CXX_STR("async-test3"));
asyncAppender->addAppender(vectorAppender);
root->addAppender(asyncAppender);
- Pool pool;
- std::string msg("message");
-
for (size_t i = 0; i < LEN; i++) {
- msg.erase(msg.begin() + 7, msg.end());
- StringHelper::toString(i, pool, msg);
- LOG4CXX_DEBUG(root, msg);
+ LOG4CXX_DEBUG(root, "message" << i);
}
asyncAppender->close();
@@ -138,6 +197,89 @@
CPPUNIT_ASSERT_EQUAL(LEN, v.size());
CPPUNIT_ASSERT_EQUAL(true, vectorAppender->isClosed());
}
+
+ /**
+ * Tests that a bad appender will switch async back to sync.
+ */
+ void testBadAppender() {
+ AppenderPtr nullPointerAppender = new NullPointerAppender();
+ AsyncAppenderPtr asyncAppender = new AsyncAppender();
+ asyncAppender->addAppender(nullPointerAppender);
+ asyncAppender->setBufferSize(5);
+ Pool p;
+ asyncAppender->activateOptions(p);
+ LoggerPtr root = Logger::getRootLogger();
+ root->addAppender(asyncAppender);
+ LOG4CXX_INFO(root, "Message");
+ Thread::sleep(10);
+ try {
+ LOG4CXX_INFO(root, "Message");
+ CPPUNIT_FAIL("Should have thrown exception");
+ } catch(NullPointerException& ex) {
+ }
+ }
+
+ /**
+ * Tests non-blocking behavior.
+ */
+ void testLocationInfoTrue() {
+ BlockableVectorAppenderPtr blockableAppender = new \
BlockableVectorAppender(); + AsyncAppenderPtr async = new AsyncAppender();
+ async->addAppender(blockableAppender);
+ async->setBufferSize(5);
+ async->setLocationInfo(true);
+ async->setBlocking(false);
+ Pool p;
+ async->activateOptions(p);
+ LoggerPtr rootLogger = Logger::getRootLogger();
+ rootLogger->addAppender(async);
+ {
+ synchronized sync(blockableAppender->getBlocker());
+ for (int i = 0; i < 100; i++) {
+ LOG4CXX_INFO(rootLogger, "Hello, World");
+ Thread::sleep(1);
+ }
+ LOG4CXX_ERROR(rootLogger, "That's all folks.");
+ }
+ async->close();
+ const std::vector<spi::LoggingEventPtr>& events = \
blockableAppender->getVector(); + LoggingEventPtr initialEvent = events[0];
+ LoggingEventPtr discardEvent = events[events.size() - 1];
+ CPPUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("Hello, World")), \
initialEvent->getMessage()); + \
CPPUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("Discarded ")), \
discardEvent->getMessage().substr(0,10)); + \
CPPUNIT_ASSERT_EQUAL(log4cxx::spi::LocationInfo::getLocationUnavailable().getClassName(), \
+ discardEvent->getLocationInformation().getClassName());
+ }
+
+ void testConfiguration() {
+ log4cxx::xml::DOMConfigurator::configure("input/xml/asyncAppender1.xml");
+ AsyncAppenderPtr \
asyncAppender(Logger::getRootLogger()->getAppender(LOG4CXX_STR("ASYNC"))); + \
CPPUNIT_ASSERT(0 != asyncAppender); + CPPUNIT_ASSERT_EQUAL(100, \
asyncAppender->getBufferSize()); + CPPUNIT_ASSERT_EQUAL(false, \
asyncAppender->getBlocking()); + CPPUNIT_ASSERT_EQUAL(true, \
asyncAppender->getLocationInfo()); + AppenderList \
nestedAppenders(asyncAppender->getAllAppenders()); + // TODO:
+ // test seems to work okay, but have not found a working way to
+ // get a reference to the nested vector appender
+ //
+// CPPUNIT_ASSERT_EQUAL((size_t) 1, nestedAppenders.size());
+// VectorAppenderPtr vectorAppender(nestedAppenders[0]);
+// CPPUNIT_ASSERT(0 != vectorAppender);
+ LoggerPtr root(Logger::getRootLogger());
+
+ size_t LEN = 20;
+ for (size_t i = 0; i < LEN; i++) {
+ LOG4CXX_DEBUG(root, "message" << i);
+ }
+
+ asyncAppender->close();
+// const std::vector<spi::LoggingEventPtr>& v = \
vectorAppender->getVector(); +// CPPUNIT_ASSERT_EQUAL(LEN, v.size());
+// CPPUNIT_ASSERT_EQUAL(true, vectorAppender->isClosed());
+ }
+
+
};
-//CPPUNIT_TEST_SUITE_REGISTRATION(AsyncAppenderTestCase);
+CPPUNIT_TEST_SUITE_REGISTRATION(AsyncAppenderTestCase);
Modified: logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp?rev=580508&r1=580507&r2=580508&view=diff
==============================================================================
--- logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp (original)
+++ logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp Fri Sep 28 15:57:53 2007
@@ -27,7 +27,7 @@
{
try
{
- Thread::sleep(5000);
+ Thread::sleep(100);
}
catch (Exception&)
{
Added: logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml?rev=580508&view=auto
==============================================================================
--- logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml (added)
+++ logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml Fri Sep 28 \
15:57:53 2007 @@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!--
+ 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.
+
+-->
+
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' debug="true">
+
+ <appender name="VECTOR" class="org.apache.log4j.VectorAppender"/>
+
+ <appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
+ <param name="BufferSize" value="100"/>
+ <param name="Blocking" value="false"/>
+ <param name="LocationInfo" value="true"/>
+ <appender-ref ref="VECTOR"/>
+ </appender>
+
+ <root>
+ <level value="DEBUG"/>
+ <appender-ref ref="ASYNC" />
+ </root>
+</log4j:configuration>
Propchange: logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml
------------------------------------------------------------------------------
svn:executable = *
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic