jeudi 9 avril 2020

Logger class using streams [multithreaded]

I have a logging class in C++. It's a singleton with multithreading support.

What I wanted to achieve (and mainly did):

  • have a generic logger class
  • singleton
  • use it with stream insertion operator not as a function call

The class writes in a std::stringstream and on std::endl it flushes the std::stringstream content to file. A big drawback is that std::endl is required once and onlny once per call for everything to work correctly in a multithreading app. not sure if it's a good approach, but it's ok for now and in my case I can go with this drawback

My problem is that a call to std::setfill() when logging will deadlock. It seems that this call is not sent to my Logger class and is executed outside its critical section.

Can I achieve this? A logger class with full support for manips?

Log Calls

LOGI << "Some message " << aVariable << std::endl;                // OK
LOGI << std::setw(20) << var << std::endl                         // OK
LOGI << std::setfill('-') << std::setw(50) << var << std::endl;   // NOT OK !!!!

Class Sample code

#define LOGFATAL Logger::GetLogger(LOGLEVEL_FATAL,      __FUNCTION__)
#define LOGE     Logger::GetLogger(LOGLEVEL_ERROR,      __FUNCTION__)
#define LOGW     Logger::GetLogger(LOGLEVEL_WARN,       __FUNCTION__)
#define LOGI     Logger::GetLogger(LOGLEVEL_INFO,       __FUNCTION__)
#define LOGD     Logger::GetLogger(LOGLEVEL_DEBUG,      __FUNCTION__)
#define LOGV     Logger::GetLogger(LOGLEVEL_VERBOSE,    __FUNCTION__)

class Logger
{
public:
    static void Initialize(const std::string& _logFile, const std::string& _logLevel)
    {
        isInitialized = true;
        logFileName = _logFile;
        logLevel = GetLogLevel(_logLevel);
    }
    static Logger& GetLogger(LogLevel l, const std::string& funcName)
    {
        if(isInitialized)
            csFile->lock();

        msgLogLevel = l;
        functionName = funcName;
        return GetLogger();
    }

    typedef std::ostream&  (*ManipFn)(std::ostream&);
    typedef std::ios_base& (*FlagsFn)(std::ios_base&);

    template<class T>
    Logger& operator<<(const T& output)
    {
        if (!isInitialized)
            return *this;

        m_stream << output;
        return *this;
    }

    Logger& operator<<(const ManipFn manip)
    {
        if (!isInitialized)
            return *this;

        manip(m_stream);

        if (manip == static_cast<ManipFn>(std::flush) ||
            manip == static_cast<ManipFn>(std::endl))
        {
            // flush the stream and unlock the mutex
            this->flush();
            csFile->unlock();
        }
        return *this;
    }

    Logger& operator<<(const FlagsFn manip) /// setiosflags, resetiosflags
    {
        manip(m_stream);
        return *this;
    }

Let me know if more information is needed

Aucun commentaire:

Enregistrer un commentaire