maverik
maverik

Reputation: 5586

C++: logger design, line/function information as function argument

I have a simple logger for my application. Here is a (simplified) interface:

class ILogger
{
public:

    virtual void LogUnsafe(const LogPriority& priority, const boost::format& message) = 0;

    virtual void Log(const LogPriority& priority, const boost::format& message)
    {
        if (priority <= logPriority_)
        {
            std::unique_lock<std::mutex> lock(loggerMutex_);
            LogUnsafe(priority, message);
        }
    }

    void Log(const LogPriority& priority, const std::string& message)
    {
        Log(priority, boost::format(message));
    }

    template<typename T, typename... Args>
    void Log(const LogPriority& priority, boost::format &format, const T &v, Args... args)
    {
        Log(priority, boost::format(format) % v, args ...);
    }

    template<typename... Args>
    void Log(const LogPriority& priority, const char *format, Args... args)
    {
        boost::format fmt(format);
        Log(priority, fmt, args ...);
    }
};

Now i need (for easy error detection) include line and function information in every log message (using __LINE__ and __FUNCTION__). Reasonably it should be passed as function argument. And, as usual, I don't want to type all those macros every time I write a log-message. So I'm in trouble here. How can I elegantly pass them to the logging function without writing it explicitly in every log message?

Thanks in advance.

Upvotes: 3

Views: 898

Answers (2)

Dennis
Dennis

Reputation: 3731

I find that the "nicest" way to do this is to use a log object that is created via a macro as Neil explained above. The "nice" thing about it is that it encapsulates all the information available at the site and so it can be passed to different loggers. For example you can have a chain of "loggers" that send it over the network, send it to disk and to a system monitor (like Windows Event Log). Also you can then use a producer-consumer logging thread to boost performance.

class LogEntry{

    LogEntry(const boost::format& traceMsg, const unsigned int lineNumber, const std::string& file){
        //set members
    }

    std::string print(){
        // Do print logic... 
    }

    // Getters... Allow the logger instance build its own log from the info.
}

then use macros:

#define LOG( format ) sendToLogQueue( LogEntry( format, __LINE__, std::string(__FILE__) ) );

Upvotes: 4

Neil
Neil

Reputation: 11889

The traditional method is to wrap your log calls in a macro and only use the macro in code.

e.g.

#ifdef NDEBUG
#define LOG(priority, message) \
   Log(priority,message)
...
#else
#define LOG(priority, message) \
   Log(priority, message, __FILE__, __LINE__)
...
#endif

void function()
{
   LOG(PRI1, "Message");
}

Then always use the LOG macro.

Upvotes: 3

Related Questions