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.
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.
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.
Each file belonging to this source code module is listed below.
/*******************************************************************************
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
/*******************************************************************************
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);
}