I implemented a Logger, so it can be used like ostream. e.g, if someone wants to write to the log - he can do something like this:
LOG << "hello world " << 6 << 8.6 << "\n";
The log will be written to the screen, to log file, and to any other source the user like to (this isn't the issue).
To achieve this goal, I created a macro for LOG:
#define LOG Logger::GetInstance()(__FILENAME__, __func__, __LINE__)
and overloaded operator () and operator <<:
template <typename T>
inline Logger& operator << (const T& msg) {
std::stringstream ss;
ss << msg;
PrintToFile(ss.str());
PrintToScreen(ss.str());
return *this;
}
Logger& Logger::operator () (const std::string& sourceFile, const std::string& funcName, int lineNumber) {
std::stringstream ss;
ss << Utilities::GetFormattedTime("%d.%m.%y %H:%M:%S") << "::" << sourceFile << "(" << lineNumber << ")::" <<
funcName << "::";
PrintToFile(level, ss.str());
PrintToScreen(level, ss.str());
return *this;
}
The problem is when I have more than one thread that is running over my process, the print may be cut, because of context switch between the threads in the middle of the example line above (LOG << "hello world... ")
Using a mutex may not help, because operator << and operator () are not the same function.
Is there any brilliant or simple solution for this problem?
Just from top of my head. If you want to keep your approach with stream io operators as is, you can use sort of proxy object that locks\unlocks mutex.
Please, don't draw attention to coding style (especially swfull and dangerous Logger implementation). Below you can find brief illustration of mentioned idea.
template<class TLogger, class TLockObject>
class LoggerProxy
{
public:
LoggerProxy(TLogger &logger, TLockObject &lockObj)
: m_logger(logger),
m_lockGuard(lockObj)
{
}
template <typename T>
inline LoggerProxy& operator << (const T& msg)
{
m_logger.Write(msg);
return *this;
}
private:
TLogger & m_logger;
std::lock_guard<typename TLockObject> m_lockGuard;
};
//Purpose of code below is just an illustration of usage LoggerProxy class. Don't use it in production code.
class Logger
{
public:
static Logger& GetInstance()
{
static Logger instance;
return instance;
}
static std::mutex& GetLockObject()
{
static std::mutex lockObj;
return lockObj;
}
template <typename T>
inline void Write (const T& msg) {
std::cout << msg << std::endl;
}
};
#define LOG LoggerProxy<Logger, std::mutex>(Logger::GetInstance(), Logger::GetLockObject())
int main()
{
LOG << 10 << "HELLO" << 1.1;
LOG << 101 << "HELLO2" << 11.1;
return 0;
}