1
votes

I want to create in the code an appender for loggers but at the same time I'd like to have the configuration on the properties file. I'm creating the appender in this way:

class Foo {
private:
    LoggerPtr logger;
public:
    Foo(int id) {
        logger = LoggerPtr(Logger::getLogger("foo." + std::to_string(id)));
        RollingFileAppender* appender = new RollingFileAppender();
        appender->setAppend(true);
        appender->setMaxFileSize("1MB");
        appender->setMaxBackupIndex(1);
        appender->setFile("foo." + std::to_string(id) + ".log");
        PatternLayout* layOut = new PatternLayout();
        layOut->setConversionPattern(
                "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n");
        appender->setLayout(LayoutPtr(layOut));
        appender->setName("fooAppender");
        Pool p;
        appender->activateOptions(p);
        logger->addAppender(AppenderPtr(appender));
        logger->setAdditivity(false);
    }
    const LoggerPtr& getLogger() const {
        return logger;
    }
};

The goal here is to have different file for each instance of Foo class, and since the numer is dynamic I can't create a logger in the properties file (maybe I could from 0 to 2*1024*1024 but it's crazy). To achieve this goal I need several appenders however I'd like to have something like that in the properties file:

log4j.appender.foo=org.apache.log4j.RollingFileAppender
log4j.appender.foo.MaxFileSize=50MB
log4j.appender.foo.MaxBackupIndex=1
log4j.appender.foo.layout=org.apache.log4j.PatternLayout
log4j.appender.foo.layout.ConversionPattern=%p %t %c - %m%n

Is it possible to have a custom appender keep reading the configuration from properties file?

1

1 Answers

1
votes

I reply to myself. What you need: you need to create a new custom appender and set it as appender in the first level logger, than in the code you can clone the appender. As bonus, in your custom appender you can manage delayed file creation so no more empty files if no log are present ;) Here the code:

class MyLogger {
private:
    MyLogger() {
    }
public:
    static void cloneParentLogger(const std::string& parentName,
            const std::string& parentAppenderName, LoggerPtr& child,
            const std::string& childName) {
        std::string newName = parentName + "." + childName;
        LoggerPtr loggerMst = LoggerPtr(Logger::getLogger(parentName));
        Appender* app = loggerMst->getAppender(parentAppenderName);
        app->close();
        AppenderPtr newAppender = app->getClass().newInstance();
        if (newAppender->instanceof(FileAppender::getStaticClass())) {
            Appender* p = newAppender;
            reinterpret_cast<FileAppender*>(p)->setFile(newName + ".log");
        }
        child = LoggerPtr(Logger::getLogger(newName));
        newAppender->setName(childName);
        newAppender->setLayout(app->getLayout());
        Pool p;
        newAppender->activateOptions(p);
        child->addAppender(newAppender);
        child->setAdditivity(false);
    }
};
class Foo {
private:
    LoggerPtr logger;
public:
    Foo(int id) {
        MyLogger::cloneParentLogger("foo", "R1", logger, std::to_string(id));
    }
    const LoggerPtr& getLogger() const {
        return logger;
    }
};

Properties file

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG

log4j.logger.foo=DEBUG, R1

log4j.appender.R1=DelayedRollingFileAppender
log4j.appender.R1.MaxFileSize=50MB
log4j.appender.R1.MaxBackupIndex=1
log4j.appender.R1.layout=org.apache.log4j.PatternLayout
log4j.appender.R1.layout.ConversionPattern=%p %t %c - %m%n

Custom appender with delayed file creation:

#ifndef DELAYEDROLLINGFILEAPPENDER_H_
#define DELAYEDROLLINGFILEAPPENDER_H_

#include <log4cxx/rollingfileappender.h>
#include <log4cxx/helpers/object.h>
#include <log4cxx/helpers/pool.h>
#include <log4cxx/helpers/classregistration.h>
#include <log4cxx/logstring.h>

namespace log4cxx {

class DelayedRollingFileAppender: public RollingFileAppender {
private:
    bool firstAppend;
    LogString file;
    bool app;
    bool bufferedIo;
    size_t bufferSize;
public:
    //DECLARE_LOG4CXX_OBJECT(DelayedRollingFileAppender)
    class ClazzDelayedRollingFileAppender: public helpers::Class {
    public:
        ClazzDelayedRollingFileAppender() :
                helpers::Class() {
        }
        virtual ~ClazzDelayedRollingFileAppender() {
        }
        virtual LogString getName() const {
            return LOG4CXX_STR("DelayedRollingFileAppender");
        }
        virtual helpers::ObjectPtr newInstance() const {
            return new DelayedRollingFileAppender();
        }
    };
    virtual const helpers::Class& getClass() const;
    static const helpers::Class& getStaticClass();
    static const helpers::ClassRegistration& registerClass();

    DelayedRollingFileAppender();
    virtual ~DelayedRollingFileAppender();
    virtual void append(const spi::LoggingEventPtr& event,
            helpers::Pool& pool1);
    virtual void setFile(const LogString& file, bool append, bool bufferedIO,
            size_t bufferSize, helpers::Pool& p);
};

} /* namespace log4cxx */

#endif /* DELAYEDROLLINGFILEAPPENDER_H_ */

Cpp file:

#include "DelayedRollingFileAppender.h"

#include <log4cxx/writerappender.h>
#include <log4cxx/helpers/fileoutputstream.h>
#include <log4cxx/helpers/bufferedwriter.h>

namespace log4cxx {

using namespace helpers;

//IMPLEMENT_LOG4CXX_OBJECT(DelayedRollingFileAppender)
const ::log4cxx::helpers::Class& DelayedRollingFileAppender::getClass() const {
    return getStaticClass();
}
const ::log4cxx::helpers::Class& DelayedRollingFileAppender::getStaticClass() {
    static ClazzDelayedRollingFileAppender theClass;
    return theClass;
}
const helpers::ClassRegistration& DelayedRollingFileAppender::registerClass() {
    static helpers::ClassRegistration classReg(
            DelayedRollingFileAppender::getStaticClass);
    return classReg;
}

namespace classes {
const ::log4cxx::helpers::ClassRegistration& DelayedRollingFileAppenderRegistration =
        DelayedRollingFileAppender::registerClass();
}

DelayedRollingFileAppender::DelayedRollingFileAppender() {
    firstAppend = true;
    this->app = false;
    this->bufferSize = 0;
    this->bufferedIo = false;
}

DelayedRollingFileAppender::~DelayedRollingFileAppender() {
}

void DelayedRollingFileAppender::append(const spi::LoggingEventPtr& event,
        helpers::Pool& pool1) {
    if (firstAppend) {
        firstAppend = false;
        FileAppender::setFile(file, app, bufferedIo, bufferSize, pool1);
    }
    WriterAppender::append(event, pool1);
}

void DelayedRollingFileAppender::setFile(const LogString& file, bool append,
        bool bufferedIO, size_t bufferSize, helpers::Pool& p) {
    this->file = file;
    this->app = append;
    this->bufferSize = bufferSize;
    this->bufferedIo = bufferedIO;

    OutputStreamPtr outStream;
    try {
        outStream = new FileOutputStream("/dev/null", false);
    } catch (IOException& ex) {
        throw;
    }

    WriterPtr newWriter(createWriter(outStream));

    if (bufferedIo) {
        newWriter = new BufferedWriter(newWriter, bufferSize);
    }
    setWriter(newWriter);
}

} /* namespace log4cxx */