Skip navigation links
IT services and product development
Menu
TwoLogs
IT services and product development

Source code for C_Logging

Download

The source files for this module are listed below.  You can also download the module C_Logging as a zip archive; this archive contains all the source files and documentation.

Description

This module contains a logging class, several log destination classes and an indentation controlling class.  Together they enable you to easily add logging support to your code including time stamping and thread detection.  This can be useful when you e.g. cannot use a debugger or when you want to debug applications running on a remote computer.

Information

This code module C_Logging uses our other code modules C_HiResTimer and optionally C_Lock, which are also available for download at our website.

CLog allows you to log all sorts of data to a log destination.  Log destinations can be e.g. a file, a messagebox, the debugger or a console window.  These four destination examples have already been worked out for you as a CLogDestFile, CLogDestMsgBox, CLogDestDebugger and CLogDestConsole respectively.  You can also create your own log destinations by deriving a class from CLogDest yourself.

You can instantiate a logger object of type CLogger that will be the access point for the log.  Also instantiate a CLogDest derived object that represents the logging destination, and hand over this object to the logger.  From then on you simply pass the data to the logger, and it will be appended to the log destination.  It is easiest to create a global logger and log destination object, and arrange the access to the logger via a common header file.  But you can of course also create multiple loggers, each e.g. logging to another log file.  When using only one logger in a multithreaded application, make sure you #define LOGGER_MULTITHREADED in your project settings; this way the logger will add extra code to synchronize access to the log and logging settings.  If you do, the logging code needs the functionality of our other source code module C_Lock.

CLogger uses a line-oriented approach.  You can opt to let each log entry be placed on it's own line (easy log operations), or you can opt to specify the end of each log line by hand by calling the method EndLogLine() at the end of the log line (enables you to build up the log line piece by piece).  You can also dynamically switch between these modes by calling the method SetOneLogEntryPerLine().  If a (partial) log line has already been build up then it will be written out automatically.

The logger can temporarilly be disabled by calling SetEnabled(false).  All logging actions will from then on be discarded.  To re-enable the logging again, simply call SetEnabled(true).

You can opt to add time stamps and/or thread ID's to each log line.  The time stamps are in seconds since the logger was created, and the thread ID enables you to identify which thread performs what actions in which sequence.  When you need to insert this sort of information by hand, you can also call LogTimeStamp() and LogThreadID() manually.  There's also LogGLE(), which logs the last error according to GetLastError(); it logs the error number and associated error description.

CLogger can log individual log entries by a series of overloads of the Log() method.  This way it's more convenient to log all sorts of individual data items.  CLogger also has the () operator overloaded to allow abbreviated log actions like

  myLogger.Log("something");
  myLogger.Log(23.6)

Note that the setting OneLogEntryPerLine doesn't really play well with logging short single data items this way.

There are multiple ways to log data; either build the log line piece by piece by appending the parts seperately (numbers, strings, etc), or by writing it all in one go using the Logf() method and a format mask (like using the printf function).

CIndent is a helper class that you can use to add indentation to your logs.  It will automatically expand to the right number of spaces.  Simply create an indenting object, pass it as the indentation to use to the CLogger constructor, and in- and decrease it's indentation with ++ and -- at the appropriate times.  CLogger will then let each line start with the indentation as it is defined when the line is closed.  You can also pass the indentation to the logging functions as an argument.

CLogDestFile will write the log output to a file on disk.  You can tell it to flush the log data to file after every log write; this way you never miss a log entry might your application e.g. crash.  This will decrease logging performance, though, so you can also opt to keep the log file open and keep appending to it.  You are free to use environment variables in the file's path, like e.g. %appdata%.

CLogDestMsgBox will simply pop up a messagebox when a log entry is written.  When you use this log destination, it is therefore better to put as much information on your log line as possible in stead of logging every single bit of information on seperate lines.

CLogDestDebugger will pass the log data on to the active debugger.  If there is no debugger attached to your application, nothing will happen.  When you use a global debug log viewer, you can specify a line prefix to use, to make it easier to filter out the lines emitted by your own application.

CLogDestConsole will create a console window for you in which the log data will be written.  When you place the focus on the console, you can also press a key to insert a tagline in the log to distinguish logging phases.  Note that the tagline will be added when the next logging action occurs, not when you press the key.

Files

Each file belonging to this source code module is listed below.

Logging.h

/*******************************************************************************

  Version: 15
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  https://www.twologs.com/sourcecode

  This code is freely distributable, as long as this comment remains intact.
  If you find this source useful, you may use this code in your own projects
  free of charge, but some acknowledgement to the author of this code is always
  appreciated :)
  The source is however distributed 'as is' without waranty and/or support, and
  may not be fit for each and every application.  Use it at your own discretion
  and at your own risk.
  The source already has undergone testing.  This doesn't mean however that all
  bugs are removed from this piece of code.  If you find one of them, please
  contact me about it.  I can however not guarantee when and if the bug will be
  fixed.

  More information about this module can be found in the accompanying HTML file.

*******************************************************************************/

#ifndef INCLUDE_TWOLOGS_COMMON_LOGGING_H
#define INCLUDE_TWOLOGS_COMMON_LOGGING_H

#include <windows.h>
#include <string>
#include "../C_HiResTimer/HiResTimer.h"

#ifdef LOGGER_MULTITHREADED
#include "../C_Lock/Lock.h"
#endif

// Indentation for logging
// Use ++ and -- to in- and decrease indentation
// Just pass the indentation to the log to indent the next log output
class TIndent {
public:
  // Con- & destructor
  TIndent(long numSpacesPerIndent);
  ~TIndent();

  // In- and outdents
  const TIndent& operator ++();
  const TIndent& operator --();

  // Returns the textual representation for the current indentation level
  LPCWSTR Value() const;
  operator LPCWSTR() const;

private:
  // The number of spaces per indentation
  long m_numSpacesPerIndent;

  // Single indentation
  std::wstring m_singleIndent;

  // Total indentation
  std::wstring m_totalIndent;
};

// Log destination base class
class TLogDest {
public:
  // Con- & destructor
  TLogDest();
  virtual ~TLogDest();

  // Dumps the given value to the log destination
  virtual void Dump(const std::wstring& value) = 0;
};

// Log-to-file destination
class TLogDestFile: public TLogDest {
public:
  // Con- & destructor
  TLogDestFile(const wchar_t* filePath, bool flushOnLog);
  virtual ~TLogDestFile();

  // Dumps the given value to the log destination
  virtual void Dump(const std::wstring& value);

private:
  // The file to log to
  FILE* m_file;
  bool m_fileOK;

  // The file/path to log to
  std::wstring m_filePath;

  // Whether to flush on each log
  bool m_flushOnLog;
};

// Log-to-MessageBox destination
class TLogDestMsgBox: public TLogDest {
public:
  // Con- & destructor
  TLogDestMsgBox(const wchar_t* title);
  virtual ~TLogDestMsgBox();

  // Dumps the given value to the log destination
  virtual void Dump(const std::wstring& value);

private:
  // The title of the messagebox
  std::wstring m_title;
};

// Log-to-debugger destination
class TLogDestDebugger: public TLogDest {
public:
  // Con- & destructor
  TLogDestDebugger(const wchar_t* linePrefix = L"");
  virtual ~TLogDestDebugger();

  // Dumps the given value to the log destination
  virtual void Dump(const std::wstring& value);

private:
  // The line prefix to use
  std::wstring m_linePrefix;

  // The current line buffer
  std::wstring m_lineBuffer;
};

// Log-to-Console destination
class TLogDestConsole: public TLogDest {
public:
  // Con- & destructor
  TLogDestConsole(DWORD numLinesToBuffer = 256, bool waitBeforeTerm = false, bool maximize = false, bool freeConsole = true, bool preserveForegroundWindow = false);
  virtual ~TLogDestConsole();

  // Dumps the given value to the log destination
  virtual void Dump(const std::wstring& value);

private:
  // The in/output handle to the console
  HANDLE m_consoleIn;
  HANDLE m_consoleOut;

  // Whether to wait before termination
  bool m_waitBeforeTerm;

  // Whether to close the console on termination
  bool m_freeConsole;
};

// Logging class
class TLogger {
public:
  // Con- & destructor
  TLogger(
    TLogDest* logDestPtr,
    TIndent* indentPtr = nullptr,
    bool oneLogEntryPerLine = true,
    bool logThreadID = false,
    bool logTimeStamp = false,
    int timeStampAccuracy = 4
  );
  virtual ~TLogger();

  // Enables/disables the logging
  bool GetEnabled() const;
  void SetEnabled(bool enabled);

  // Logging settings
  void SetOneLogEntryPerLine(bool newSetting);  // default = true
  void SetLogThreadID(bool newSetting);         // default = false
  void SetLogTimeStamp(bool newSetting);        // default = false
  void SetTimeStampAccuracy(int newSetting);    // default = 4

  // Logs the given log entry
  void Log(const char* logEntry);
  void Log(const wchar_t* logEntry = nullptr);
  void Log(const std::string& logEntry);
  void Log(const std::wstring& logEntry);
  void Log(long logEntry, long radix = 10);
  void Log(unsigned long logEntry, long radix = 10);
  void Log(bool logEntry);
  void operator ()(const char* logEntry = nullptr);
  void operator ()(const wchar_t* logEntry = nullptr);
  void operator ()(const std::string& logEntry);
  void operator ()(const std::wstring& logEntry);
  void operator ()(long logEntry);
  void operator ()(unsigned long logEntry);
  void operator ()(bool logEntry);

  // Logs the formatted argument list to the buffer
  void Logf(const wchar_t* format, ...);

  // Logs a time stamp
  void LogTimeStamp();

  // Logs the current thread's ID
  void LogThreadID();

  // Logs the GetLastError
  void LogGLE();

  // Ends the log's current line, adding time stamps and/or thread ID's
  void EndLogLine();

private:
  // The log destination
  TLogDest* m_logDestPtr;

  // The log indentation
  TIndent* m_indentPtr;

  // The log buffer
  std::wstring m_logBuffer;

  // Whether we're enabled
  bool m_enabled;

  // Whether to put each log entry on it's own line
  bool m_oneLogEntryPerLine;

  // Whether to log a thread ID to each line
  bool m_logThreadID;

  // Whether to log a time stamp to each line
  bool m_logTimeStamp;

  // The timestamp format
  long m_timeStampAccuracy;
  wchar_t m_timeStampFormat[20];

  // The log timer
  THiResTimer m_timer;

#ifdef LOGGER_MULTITHREADED
  // Access to the log buffer
  TLock m_bufferLock;
#endif

  // Creates a new time stamp header
  void CreateTimeStampHeader();
};

#endif // INCLUDE_TWOLOGS_COMMON_LOGGING_H

Logging.cpp

/*******************************************************************************

  Version: 15
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  https://www.twologs.com/sourcecode

  This code is freely distributable, as long as this comment remains intact.
  If you find this source useful, you may use this code in your own projects
  free of charge, but some acknowledgement to the author of this code is always
  appreciated :)
  The source is however distributed 'as is' without waranty and/or support, and
  may not be fit for each and every application.  Use it at your own discretion
  and at your own risk.
  The source already has undergone testing.  This doesn't mean however that all
  bugs are removed from this piece of code.  If you find one of them, please
  contact me about it.  I can however not guarantee when and if the bug will be
  fixed.

  More information about this module can be found in the accompanying HTML file.

*******************************************************************************/

#include "Logging.h"
#include <stdio.h>

/*******************************************************************************

  TIndent

*******************************************************************************/

// Con- & destructor
TIndent::TIndent(long numSpacesPerIndent):
 m_numSpacesPerIndent(numSpacesPerIndent) {
  // Calculate a single indentation
  m_singleIndent.assign(m_numSpacesPerIndent, L' ');
}
TIndent::~TIndent() {
}

// In- and outdents
const TIndent& TIndent::operator ++() {
  m_totalIndent += m_singleIndent;
  return *this;
}
const TIndent& TIndent::operator --() {
  if (m_totalIndent.size() > 0) {
    m_totalIndent.erase(0, m_numSpacesPerIndent);
  }
  return *this;
}

// Returns the textual representation for the current indentation level
LPCWSTR TIndent::Value() const {
  return m_totalIndent.c_str();
}
TIndent::operator LPCWSTR() const {
  return m_totalIndent.c_str();
}




/*******************************************************************************

  TLogDest and derivatives

*******************************************************************************/

// Con- & destructor
TLogDest::TLogDest() {
}
TLogDest::~TLogDest() {
}

// Constructor
TLogDestFile::TLogDestFile(const wchar_t* filePath, bool flushOnLog):
 m_flushOnLog(flushOnLog) {
  wchar_t expandedFilePath[MAX_PATH];
  ExpandEnvironmentStrings(filePath, expandedFilePath, MAX_PATH);
  m_file = _wfopen(expandedFilePath, L"w");
  m_fileOK = m_file != NULL;
  if (!m_fileOK) {
    std::wstring prompt = L"Error opening log file:\r\n  ";
    prompt += filePath;
    MessageBox(NULL, prompt.c_str(), L"Error opening log file", MB_OK | MB_ICONERROR);
  } else if (m_flushOnLog) {
    fclose(m_file);
    m_filePath = expandedFilePath;
  }
}

// Destructor
TLogDestFile::~TLogDestFile() {
  if (m_fileOK && !m_flushOnLog) {
    fclose(m_file);
  }
}

// Dumps the given value to the log destination
void TLogDestFile::Dump(const std::wstring& value) {
  if (m_fileOK) {
    if (m_flushOnLog) {
      m_file = _wfopen(m_filePath.c_str(), L"a+");
    }
    fwprintf(m_file, value.c_str());
    if (m_flushOnLog) {
      fclose(m_file);
    }
  }
}

// Constructor
TLogDestMsgBox::TLogDestMsgBox(const wchar_t* title):
 m_title(title) {
}

// Destructor
TLogDestMsgBox::~TLogDestMsgBox() {
}

// Dumps the given value to the log destination
void TLogDestMsgBox::Dump(const std::wstring& value) {
  MessageBox(NULL, value.c_str(), m_title.c_str(), MB_OK);
}

// Constructor
TLogDestDebugger::TLogDestDebugger(const wchar_t* linePrefix):
 m_linePrefix(linePrefix == nullptr? L"": linePrefix),
 m_lineBuffer(m_linePrefix) {
}

// Destructor
TLogDestDebugger::~TLogDestDebugger() {
}

// Dumps the given value to the log destination
void TLogDestDebugger::Dump(const std::wstring& value) {
  // Add the new content
  m_lineBuffer += value;

  // And emit all lines present already as separate lines
  size_t nextLinePos;
  while ((nextLinePos = m_lineBuffer.find_first_of(L"\r\n")) != m_lineBuffer.npos) {
    bool wasN = m_lineBuffer[nextLinePos] == L'\n';
    m_lineBuffer[nextLinePos] = L'\0';
    OutputDebugString(m_lineBuffer.c_str());
    if (
      !wasN &&
      nextLinePos < m_lineBuffer.size() - 1 &&
      m_lineBuffer[nextLinePos + 1] == L'\n'
    ) {
      ++nextLinePos;
    }
    m_lineBuffer.erase(0, nextLinePos + 1);
    m_lineBuffer.insert(0, m_linePrefix);
  }
}

// Constructor
TLogDestConsole::TLogDestConsole(DWORD numLinesToBuffer, bool waitBeforeTerm, bool maximize, bool freeConsole, bool preserveForegroundWindow):
 m_waitBeforeTerm(waitBeforeTerm),
 m_freeConsole(freeConsole) {
  // Look who's the foreground window now, if needed
  HWND foregroundWnd;
  if (preserveForegroundWindow) {
    foregroundWnd = GetForegroundWindow();
  }

  // Ensure we have a console
  AllocConsole();

  // Use it
  m_consoleIn = GetStdHandle(STD_INPUT_HANDLE);
  m_consoleOut = GetStdHandle(STD_OUTPUT_HANDLE);

  // Config the console
  COORD bufferSize;
  bufferSize.X = 80;
  bufferSize.Y = numLinesToBuffer;
  SetConsoleScreenBufferSize(m_consoleOut, bufferSize);
  SetConsoleTitle(L"LOG WINDOW - Press a character to insert it's mark line");

  // Make sure it shows
  HWND consoleWnd = GetConsoleWindow();
  ShowWindow(consoleWnd, maximize? SW_SHOWMAXIMIZED: SW_SHOWNORMAL);

  // But do not make us the new foreground window, if needed
  if (preserveForegroundWindow) {
    SetForegroundWindow(foregroundWnd);
  }
}

// Destructor
TLogDestConsole::~TLogDestConsole() {
  if (m_waitBeforeTerm) {
    MessageBox(NULL, L"Exit logger", L"Exit logger", MB_ICONINFORMATION);
  }
  CloseHandle(m_consoleIn);
  CloseHandle(m_consoleOut);
  if (m_freeConsole) {
    FreeConsole();
  }
}

// Dumps the given value to the log destination
void TLogDestConsole::Dump(const std::wstring& value) {
  // Look if to insert a mark line first
  DWORD numEvents;
  GetNumberOfConsoleInputEvents(m_consoleIn, &numEvents);
  if (numEvents > 0) {
    INPUT_RECORD* events = new INPUT_RECORD[numEvents];
    DWORD numEventsRead;
    ReadConsoleInput(m_consoleIn, events, numEvents, &numEventsRead);
    for (DWORD eventNr = 0; eventNr < numEventsRead; ++eventNr) {
      if (events[eventNr].EventType == KEY_EVENT) {
        if (!events[eventNr].Event.KeyEvent.bKeyDown && events[eventNr].Event.KeyEvent.uChar.AsciiChar != '\t') {
          // Yes -> do so
          std::wstring tagLine = L"\n";
          wchar_t tagLineChar = events[eventNr].Event.KeyEvent.uChar.AsciiChar;
          for (int partNr = 0; partNr < 16; ++partNr) {
            tagLine += L"= ";
            tagLine += tagLineChar;
            tagLine += L" =";
          }
          tagLine += L"\n";
          DWORD writtenSize;
          WriteConsole(
            m_consoleOut,
            tagLine.c_str(),
            tagLine.size(),
            &writtenSize,
            nullptr
          );
        }
      }
    }
    delete[] events;
  }

  // And write out the new stuff
  DWORD writtenSize;
  WriteConsole(m_consoleOut, value.c_str(), value.size(), &writtenSize, nullptr);
}




/*******************************************************************************

  TLogger

*******************************************************************************/

// Buffer access
#ifdef LOGGER_MULTITHREADED
#define LOGGER_GETLOCKACCESS \
  TLocker locker(m_bufferLock)
#else
#define LOGGER_GETLOCKACCESS
#endif

// Constructor
TLogger::TLogger(
  TLogDest* logDestPtr,
  TIndent* indentPtr,
  bool oneLogEntryPerLine,
  bool logThreadID,
  bool logTimeStamp,
  int timeStampAccuracy
):
 m_logDestPtr(logDestPtr),
 m_indentPtr(indentPtr),
 m_enabled(true),
 m_oneLogEntryPerLine(oneLogEntryPerLine),
 m_logThreadID(logThreadID),
 m_logTimeStamp(logTimeStamp),
 m_timeStampAccuracy(timeStampAccuracy) {
  // Create the time stamp header
  CreateTimeStampHeader();

  // And log the starting time of this session
  SYSTEMTIME now;
  GetLocalTime(&now);
  wchar_t todayText[256];
  GetDateFormat(LOCALE_USER_DEFAULT, 0, &now, nullptr, todayText, 255);
  wchar_t nowText[256];
  GetTimeFormat(LOCALE_USER_DEFAULT, 0, &now, nullptr, nowText, 255);
  m_timer.Start();
  Logf(L"New session started on %s at %s\n", todayText, nowText);
  if (!m_oneLogEntryPerLine) {
    EndLogLine();
  }
}

// Destructor
TLogger::~TLogger() {
  LOGGER_GETLOCKACCESS;
  if (m_logBuffer.size() > 0) {
    Log();
  }
}

// Enables/disables the logging
bool TLogger::GetEnabled() const {
  return m_enabled;
}
void TLogger::SetEnabled(bool enabled) {
  if (m_enabled != enabled) {
    m_enabled = true;
    if (enabled) {
      Log(L"Logging enabled.");
    } else {
      Log(L"Logging disabled.");
    }
    if (!m_oneLogEntryPerLine) {
      EndLogLine();
    }
    m_enabled = enabled;
  }
}

// Logging settings
void TLogger::SetOneLogEntryPerLine(bool newSetting) {
  LOGGER_GETLOCKACCESS;
  m_oneLogEntryPerLine = newSetting;
  if (m_oneLogEntryPerLine) {
    if (m_logBuffer.size() > 0) {
      EndLogLine();
    }
  }
}
void TLogger::SetLogThreadID(bool newSetting) {
  LOGGER_GETLOCKACCESS;
  m_logThreadID = newSetting;
}
void TLogger::SetLogTimeStamp(bool newSetting) {
  LOGGER_GETLOCKACCESS;
  m_logTimeStamp = newSetting;
}
void TLogger::SetTimeStampAccuracy(int newSetting) {
  LOGGER_GETLOCKACCESS;
  m_timeStampAccuracy = newSetting;
  CreateTimeStampHeader();
}

// Logs a time stamp
void TLogger::LogTimeStamp() {
  LOGGER_GETLOCKACCESS;
  double now = m_timer.GetInterval();
  wchar_t timeStamp[128];
  swprintf(timeStamp, m_timeStampFormat, now);
  Log(timeStamp);
}

// Logs the current thread's ID
void TLogger::LogThreadID() {
  DWORD threadID = GetCurrentThreadId();
  wchar_t threadIDText[30];
  _ltow(threadID, threadIDText, 16);
  Log(threadIDText);
}

// Logs the GetLastError
void TLogger::LogGLE() {
  // Log the error number
  DWORD errorCode = GetLastError();
  wchar_t errorCodeText[30];
  _ultow(errorCode, errorCodeText, 10);
  std::wstring errorDescription = errorCodeText;
  errorDescription += L" - ";

  // Get the GetLastError text
  LPWSTR error = nullptr;
  if (0 < FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER |
      FORMAT_MESSAGE_FROM_SYSTEM |
      FORMAT_MESSAGE_IGNORE_INSERTS,
    nullptr,
    errorCode,
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    (LPTSTR)&error,
    0,
    nullptr)
  ) {
    // Done -> use it
    errorDescription += error;

    // And kill it
    LocalFree(error);
  } else {
    // Couldn't find -> specify something
    errorDescription += L"<unknown error>";
  }

  // And log the error
  Log(errorDescription);
}

// Logs the buffer's content, optionally also including the
// given log entry
void TLogger::Log(const char* logEntry) {
  int numBytes = strlen(logEntry);
  size_t maxNumCharsNeeded = numBytes * 3 + 1;
  std::wstring converted(maxNumCharsNeeded, L'\0');
  int numCharsWritten = MultiByteToWideChar(
    CP_THREAD_ACP,
    MB_ERR_INVALID_CHARS,
    logEntry,
    numBytes,
    const_cast<wchar_t*>(converted.c_str()),
    maxNumCharsNeeded
  );
  converted.resize(numCharsWritten);
  Log(converted);
}
void TLogger::Log(const wchar_t* logEntry) {
  LOGGER_GETLOCKACCESS;
  if (m_enabled) {
    if (logEntry != nullptr) {
      m_logBuffer += logEntry;
    }
    if (m_oneLogEntryPerLine) {
      EndLogLine();
    }
  }
}
void TLogger::Log(const std::string& logEntry) {
  Log(logEntry.c_str());
}
void TLogger::Log(const std::wstring& logEntry) {
  LOGGER_GETLOCKACCESS;
  if (m_enabled) {
    m_logBuffer += logEntry;
    if (m_oneLogEntryPerLine) {
      EndLogLine();
    }
  }
}
void TLogger::Log(long logEntry, long radix) {
  std::wstring logEntryText(60, L'\0');
  _ltow(logEntry, const_cast<wchar_t*>(logEntryText.c_str()), radix);
  Log(logEntryText);
}
void TLogger::Log(unsigned long logEntry, long radix) {
  std::wstring logEntryText(60, L'\0');
  _ultow(logEntry, const_cast<wchar_t*>(logEntryText.c_str()), radix);
  Log(logEntryText);
}
void TLogger::Log(bool logEntry) {
  Log(logEntry? L"true": L"false");
}
void TLogger::operator ()(const wchar_t* logEntry) {
  Log(logEntry);
}
void TLogger::operator ()(const char* logEntry) {
  Log(logEntry);
}
void TLogger::operator ()(const std::string& logEntry) {
  Log(logEntry);
}
void TLogger::operator ()(const std::wstring& logEntry) {
  Log(logEntry);
}
void TLogger::operator ()(long logEntry) {
  Log(logEntry);
}
void TLogger::operator ()(unsigned long logEntry) {
  Log(logEntry);
}
void TLogger::operator ()(bool logEntry) {
  Log(logEntry);
}

// Logs the formatted argument list to the buffer
void TLogger::Logf(const wchar_t* format, ...) {
  va_list args;
  va_start(args, format);
  long bufferSize = 1023;
  std::wstring buffer(bufferSize, L'\0');
  long usedSize = -1;
  do {
    usedSize = _vsnwprintf(
      const_cast<wchar_t*>(buffer.c_str()),
      bufferSize,
      format,
      args
    );
    if (usedSize == -1) {
      bufferSize *= 2;
      buffer.resize(bufferSize);
    } else {
      buffer.resize(usedSize);
    }
  } while (usedSize == -1);
  va_end(args);
  Log(buffer);
}

// Ends the log's current line, adding time stamps and/or thread ID's
void TLogger::EndLogLine() {
  // Look if we need to log something
  LOGGER_GETLOCKACCESS;
  if (m_enabled && m_logBuffer.size() > 0) {
    // Yes -> look if a logger is available
    if (m_logDestPtr != nullptr) {
      // Yes -> log any time stamp, if needed
      std::wstring logLine;
      if (m_logTimeStamp) {
        double now = m_timer.GetInterval();
        wchar_t timeStamp[128];
        swprintf(timeStamp, m_timeStampFormat, now);
        logLine += timeStamp;
        logLine += L"\t";
      }

      // Log any thread ID, if needed
      if (m_logThreadID) {
        DWORD threadID = GetCurrentThreadId();
        wchar_t threadIDText[30];
        _ltow(threadID, threadIDText, 16);
        logLine += threadIDText;
        logLine += L"\t";
      }

      // Log any indentation
      if (m_indentPtr != nullptr) {
        logLine += m_indentPtr->Value();
      }

      // And log the contents of the buffer on it's own line
      logLine += m_logBuffer;
      logLine += L"\n";
      m_logDestPtr->Dump(logLine);
    }

    // And clear the buffer again
    m_logBuffer.clear();
  }
}

// Creates a new time stamp header
void TLogger::CreateTimeStampHeader() {
  swprintf(m_timeStampFormat, L"%%0.%df", m_timeStampAccuracy);
}