You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4cxx-user@logging.apache.org by Brian <za...@gmail.com> on 2007/08/21 19:15:49 UTC

multithread issue

I have an application that uses multiple loggers (15), each owned by a
different thread, that is writing to a different file.  Executing the
application starts the logger but soon the application cores.  I wrote a
test application to rule out my code and I still see the same problem.  (I
am also seeing logs with bad output)  This works fine on Windows but in
Linux it cores. This is being run on a multiprocessor box with no hyper
threading.

Has anyone else seen this problem?  I also tried the test program with
org.apache.log4j.FileAppender.

Thanks
  Brian


#include <iostream>
#include <pthread.h>
#include <sstream>
#include <log4cxx/logger.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/exception.h>

struct Data
{
    int count;
};

void* LogThread(void* args)
{
    Data* d = (Data*)args;

    // Build Logger Name
    std::ostringstream temp;
    temp << "TestLogger" << d->count;
    std::string loggerName(temp.str());
    std::cout << "getting logger = " << loggerName << std::endl;

    log4cxx::LoggerPtr logger;
    try
    {
        // Get Logger
        logger = log4cxx::Logger::getLogger(loggerName);
    }
    catch(...)
    {
        std::cout << "Failed to get logger = " << loggerName << std::endl;
        return 0;
    }

    while(1)
    {
        logger->debug(loggerName);
        logger->debug("This is a test of the emergency broadcast system.
This is only a test");
        logger->debug("This is a test of the emergency broadcast system.
This is only a test");
        logger->debug("This is a test of the emergency broadcast system.
This is only a test");
        logger->debug("This is a test of the emergency broadcast system.
This is only a test");
        logger->debug("This is a test of the emergency broadcast system.
This is only a test");
        logger->debug("This is a test of the emergency broadcast system.
This is only a test");
        logger->debug("This is a test of the emergency broadcast system.
This is only a test");

        usleep(5);
    }

    return 0;
}

int main(void)
{
    for(int count = 0; count < 10; count++)
    {
        Data* d = new Data();
        d->count = count;

        pthread_t newThread;
        pthread_create(&newThread, NULL, &LogThread, d);
        sleep(1);
    }

    std::string input;
    std::cout << "Waiting for input to exit" << std::endl;
    std::cin >> input;

    return 0;
}

####################################################
# log4j.properties for Generic Application
#
# NOTE:  It doesn't hurt anything to have superfluous
#        entries for loggers that will not be used.
####################################################
log4j.rootLogger=DEBUG
log4j.logger.TestLogger0=DEBUG,Drw.TestLogger0
log4j.logger.TestLogger1=DEBUG,Drw.TestLogger1
log4j.logger.TestLogger2=DEBUG,Drw.TestLogger2
log4j.logger.TestLogger3=DEBUG,Drw.TestLogger3
log4j.logger.TestLogger4=DEBUG ,Drw.TestLogger4
log4j.logger.TestLogger5=DEBUG,Drw.TestLogger5
log4j.logger.TestLogger6=DEBUG,Drw.TestLogger6
log4j.logger.TestLogger7=DEBUG,Drw.TestLogger7
log4j.logger.TestLogger8=DEBUG,Drw.TestLogger8
log4j.logger.TestLogger9=DEBUG ,Drw.TestLogger9

log4j.appender.Drw.TestLogger0=org.apache.log4j.RollingFileAppender
log4j.appender.Drw.TestLogger0.File=TestLogger0.log
log4j.appender.Drw.TestLogger0.Append=true
log4j.appender.Drw.TestLogger0.MaxFileSize=100mb
log4j.appender.Drw.TestLogger0.MaxBackupIndex=3
log4j.appender.Drw.TestLogger0.layout=org.apache.log4j.PatternLayout
log4j.appender.Drw.TestLogger0.layout.ConversionPattern=%d{dd/MM/yyyyHH:mm:ss,SSS}
[%p] %m%n

log4j.appender.Drw.TestLogger1=org.apache.log4j.RollingFileAppender
log4j.appender.Drw.TestLogger1.File=TestLogger1.log
log4j.appender.Drw.TestLogger1.Append=true
log4j.appender.Drw.TestLogger1.MaxFileSize=100mb
log4j.appender.Drw.TestLogger1.MaxBackupIndex=3
log4j.appender.Drw.TestLogger1.layout=org.apache.log4j.PatternLayout
log4j.appender.Drw.TestLogger1.layout.ConversionPattern=%d{dd/MM/yyyyHH:mm:ss,SSS}
[%p] %m%n

... TestLogger 0 - 9

Re: multithread issue

Posted by Curt Arnold <ca...@apache.org>.
On Aug 21, 2007, at 12:15 PM, Brian wrote:

> I have an application that uses multiple loggers (15), each owned  
> by a different thread, that is writing to a different file.   
> Executing the application starts the logger but soon the  
> application cores.  I wrote a test application to rule out my code  
> and I still see the same problem.  (I am also seeing logs with bad  
> output)  This works fine on Windows but in Linux it cores. This is  
> being run on a multiprocessor box with no hyper threading.
>
> Has anyone else seen this problem?  I also tried the test program  
> with org.apache.log4j.FileAppender.
>
> Thanks
>   Brian

Sorry about the long delay.  I finally built your test on Linux and  
got an immediate segfault in the first initialization of LoggerPtr.   
The reason this is occurring on Linux and not Windows is the  
implementation of apr_atomic_xchg32 on Windows delegates to a Windows  
API method which will work if called before APR initialization and  
the Linux implementation requires that APR be initialized.  The  
implementation of a LoggerPtr isn't sufficient to trigger APR  
initialization.

If instead of

log4cxx::LoggerPtr logger;
     try
     {
         // Get Logger
         logger = log4cxx::Logger::getLogger(loggerName);
     }
     catch(...)
     {
         std::cout << "Failed to get logger = " << loggerName <<  
std::endl;
         return 0;
     }

I did:

log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger(loggerName));


I no longer got an immediate segfault as the call to getLogger() will  
trigger the call to load APR before the LoggerPtr constructor calls  
apr_atomic_xchg32.  With that change, I still got an segfault on  
application shutdown.  I didn't trace that issue down, but it appears  
likely due to APR being de-initialized when the thread that  
originally initialized it went out of scope, leaving the other  
threads using a non-functioning APR.  That problem could be addressed  
by initializing APR in the main thread (likely by calling  
Logger::getLogger in either a constructor of a static object).

Basically, log4cxx is safe to use on multi-threads, but it isn't safe  
to initialize or configure on an arbitrary thread.  Apps near to do  
something to trigger initialization and configuration either on  
module load time (happens automatically with a static LoggerPtr  
instance) or on the main thread.