Compare commits
1 commit
main
...
Include-li
Author | SHA1 | Date | |
---|---|---|---|
68a1618743 |
34 changed files with 2702 additions and 27 deletions
|
@ -2,14 +2,8 @@
|
|||
"configurations": [
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE"
|
||||
],
|
||||
"includePath": ["${workspaceFolder}/**"],
|
||||
"defines": ["_DEBUG", "UNICODE", "_UNICODE"],
|
||||
"compilerPath": "C:\\msys64\\mingw64\\bin\\gcc.exe",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++20",
|
||||
|
@ -17,4 +11,4 @@
|
|||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"name": "Debug: C/C++",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${fileDirname}\\build\\${fileBasenameNoExtension}.exe",
|
||||
"program": "${workspaceFolder}\\build\\${fileBasenameNoExtension}.exe",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${fileDirname}",
|
||||
|
@ -30,7 +30,7 @@
|
|||
"name": "Release: C/C++",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${fileDirname}\\build\\${fileBasenameNoExtension}.exe",
|
||||
"program": "${workspaceFolder}\\build\\${fileBasenameNoExtension}.exe",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${fileDirname}",
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
"command": "C:\\msys64\\mingw64\\bin\\g++.exe",
|
||||
"args": [
|
||||
"-fdiagnostics-color=always",
|
||||
"-I${workspaceFolder}\\include",
|
||||
"-ggdb",
|
||||
"-std=c++20",
|
||||
"-pedantic-errors",
|
||||
"${fileDirname}/**.cpp",
|
||||
"${workspaceFolder}/src/**.cpp",
|
||||
"-o",
|
||||
"${fileDirname}\\build\\${fileBasenameNoExtension}.exe"
|
||||
"${workspaceFolder}\\build\\${fileBasenameNoExtension}.exe"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${fileDirname}"
|
||||
|
@ -26,13 +27,14 @@
|
|||
"command": "C:\\msys64\\mingw64\\bin\\g++.exe",
|
||||
"args": [
|
||||
"-fdiagnostics-color=always",
|
||||
"-I${workspaceFolder}\\include",
|
||||
"-O2",
|
||||
"-DNDEBUG",
|
||||
"-std=c++20",
|
||||
"-pedantic-errors",
|
||||
"${fileDirname}/**.cpp",
|
||||
"${workspaceFolder}/src/**.cpp",
|
||||
"-o",
|
||||
"${fileDirname}\\build\\${fileBasenameNoExtension}.exe"
|
||||
"${workspaceFolder}\\build\\${fileBasenameNoExtension}.exe"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${fileDirname}"
|
||||
|
|
47
include/plog/Appenders/AndroidAppender.h
Normal file
47
include/plog/Appenders/AndroidAppender.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/IAppender.h>
|
||||
#include <android/log.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<class Formatter>
|
||||
class PLOG_LINKAGE_HIDDEN AndroidAppender : public IAppender
|
||||
{
|
||||
public:
|
||||
AndroidAppender(const char* tag) : m_tag(tag)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void write(const Record& record) PLOG_OVERRIDE
|
||||
{
|
||||
std::string str = Formatter::format(record);
|
||||
|
||||
__android_log_print(toPriority(record.getSeverity()), m_tag, "%s", str.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
static android_LogPriority toPriority(Severity severity)
|
||||
{
|
||||
switch (severity)
|
||||
{
|
||||
case fatal:
|
||||
return ANDROID_LOG_FATAL;
|
||||
case error:
|
||||
return ANDROID_LOG_ERROR;
|
||||
case warning:
|
||||
return ANDROID_LOG_WARN;
|
||||
case info:
|
||||
return ANDROID_LOG_INFO;
|
||||
case debug:
|
||||
return ANDROID_LOG_DEBUG;
|
||||
case verbose:
|
||||
return ANDROID_LOG_VERBOSE;
|
||||
default:
|
||||
return ANDROID_LOG_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const char* const m_tag;
|
||||
};
|
||||
}
|
23
include/plog/Appenders/ArduinoAppender.h
Normal file
23
include/plog/Appenders/ArduinoAppender.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/IAppender.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<class Formatter>
|
||||
class PLOG_LINKAGE_HIDDEN ArduinoAppender : public IAppender
|
||||
{
|
||||
public:
|
||||
ArduinoAppender(Stream &stream) : m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void write(const Record &record) PLOG_OVERRIDE
|
||||
{
|
||||
m_stream.print(Formatter::format(record).c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
Stream &m_stream;
|
||||
};
|
||||
}
|
108
include/plog/Appenders/ColorConsoleAppender.h
Normal file
108
include/plog/Appenders/ColorConsoleAppender.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/ConsoleAppender.h>
|
||||
#include <plog/WinApi.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<class Formatter>
|
||||
class PLOG_LINKAGE_HIDDEN ColorConsoleAppender : public ConsoleAppender<Formatter>
|
||||
{
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
# ifdef _MSC_VER
|
||||
# pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum'
|
||||
# endif
|
||||
ColorConsoleAppender(OutputStream outStream = streamStdOut)
|
||||
: ConsoleAppender<Formatter>(outStream)
|
||||
, m_originalAttr()
|
||||
{
|
||||
if (this->m_isatty)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
|
||||
GetConsoleScreenBufferInfo(this->m_outputHandle, &csbiInfo);
|
||||
|
||||
m_originalAttr = csbiInfo.wAttributes;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ColorConsoleAppender(OutputStream outStream = streamStdOut)
|
||||
: ConsoleAppender<Formatter>(outStream)
|
||||
{}
|
||||
#endif
|
||||
|
||||
virtual void write(const Record& record) PLOG_OVERRIDE
|
||||
{
|
||||
util::nstring str = Formatter::format(record);
|
||||
util::MutexLock lock(this->m_mutex);
|
||||
|
||||
setColor(record.getSeverity());
|
||||
this->writestr(str);
|
||||
resetColor();
|
||||
}
|
||||
|
||||
protected:
|
||||
void setColor(Severity severity)
|
||||
{
|
||||
if (this->m_isatty)
|
||||
{
|
||||
switch (severity)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case fatal:
|
||||
SetConsoleTextAttribute(this->m_outputHandle, foreground::kRed | foreground::kGreen | foreground::kBlue | foreground::kIntensity | background::kRed); // white on red background
|
||||
break;
|
||||
|
||||
case error:
|
||||
SetConsoleTextAttribute(this->m_outputHandle, static_cast<WORD>(foreground::kRed | foreground::kIntensity | (m_originalAttr & 0xf0))); // red
|
||||
break;
|
||||
|
||||
case warning:
|
||||
SetConsoleTextAttribute(this->m_outputHandle, static_cast<WORD>(foreground::kRed | foreground::kGreen | foreground::kIntensity | (m_originalAttr & 0xf0))); // yellow
|
||||
break;
|
||||
|
||||
case debug:
|
||||
case verbose:
|
||||
SetConsoleTextAttribute(this->m_outputHandle, static_cast<WORD>(foreground::kGreen | foreground::kBlue | foreground::kIntensity | (m_originalAttr & 0xf0))); // cyan
|
||||
break;
|
||||
#else
|
||||
case fatal:
|
||||
this->m_outputStream << "\x1B[97m\x1B[41m"; // white on red background
|
||||
break;
|
||||
|
||||
case error:
|
||||
this->m_outputStream << "\x1B[91m"; // red
|
||||
break;
|
||||
|
||||
case warning:
|
||||
this->m_outputStream << "\x1B[93m"; // yellow
|
||||
break;
|
||||
|
||||
case debug:
|
||||
case verbose:
|
||||
this->m_outputStream << "\x1B[96m"; // cyan
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resetColor()
|
||||
{
|
||||
if (this->m_isatty)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(this->m_outputHandle, m_originalAttr);
|
||||
#else
|
||||
this->m_outputStream << "\x1B[0m\x1B[0K";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
WORD m_originalAttr;
|
||||
#endif
|
||||
};
|
||||
}
|
83
include/plog/Appenders/ConsoleAppender.h
Normal file
83
include/plog/Appenders/ConsoleAppender.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/IAppender.h>
|
||||
#include <plog/Util.h>
|
||||
#include <plog/WinApi.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
enum OutputStream
|
||||
{
|
||||
streamStdOut,
|
||||
streamStdErr
|
||||
};
|
||||
|
||||
template<class Formatter>
|
||||
class PLOG_LINKAGE_HIDDEN ConsoleAppender : public IAppender
|
||||
{
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
# ifdef _MSC_VER
|
||||
# pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum'
|
||||
# endif
|
||||
ConsoleAppender(OutputStream outStream = streamStdOut)
|
||||
: m_isatty(!!_isatty(_fileno(outStream == streamStdOut ? stdout : stderr)))
|
||||
, m_outputStream(outStream == streamStdOut ? std::cout : std::cerr)
|
||||
, m_outputHandle()
|
||||
{
|
||||
if (m_isatty)
|
||||
{
|
||||
m_outputHandle = GetStdHandle(outStream == streamStdOut ? stdHandle::kOutput : stdHandle::kErrorOutput);
|
||||
}
|
||||
}
|
||||
#else
|
||||
ConsoleAppender(OutputStream outStream = streamStdOut)
|
||||
: m_isatty(!!isatty(fileno(outStream == streamStdOut ? stdout : stderr)))
|
||||
, m_outputStream(outStream == streamStdOut ? std::cout : std::cerr)
|
||||
{}
|
||||
#endif
|
||||
|
||||
virtual void write(const Record& record) PLOG_OVERRIDE
|
||||
{
|
||||
util::nstring str = Formatter::format(record);
|
||||
util::MutexLock lock(m_mutex);
|
||||
|
||||
writestr(str);
|
||||
}
|
||||
|
||||
protected:
|
||||
void writestr(const util::nstring& str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_isatty)
|
||||
{
|
||||
const std::wstring& wstr = util::toWide(str);
|
||||
WriteConsoleW(m_outputHandle, wstr.c_str(), static_cast<DWORD>(wstr.size()), NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
# if PLOG_CHAR_IS_UTF8
|
||||
m_outputStream << str << std::flush;
|
||||
# else
|
||||
m_outputStream << util::toNarrow(str, codePage::kActive) << std::flush;
|
||||
# endif
|
||||
}
|
||||
#else
|
||||
m_outputStream << str << std::flush;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef __BORLANDC__
|
||||
static int _isatty(int fd) { return ::isatty(fd); }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
util::Mutex m_mutex;
|
||||
const bool m_isatty;
|
||||
std::ostream& m_outputStream;
|
||||
#ifdef _WIN32
|
||||
HANDLE m_outputHandle;
|
||||
#endif
|
||||
};
|
||||
}
|
16
include/plog/Appenders/DebugOutputAppender.h
Normal file
16
include/plog/Appenders/DebugOutputAppender.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/IAppender.h>
|
||||
#include <plog/WinApi.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<class Formatter>
|
||||
class PLOG_LINKAGE_HIDDEN DebugOutputAppender : public IAppender
|
||||
{
|
||||
public:
|
||||
virtual void write(const Record& record) PLOG_OVERRIDE
|
||||
{
|
||||
OutputDebugStringW(util::toWide(Formatter::format(record)).c_str());
|
||||
}
|
||||
};
|
||||
}
|
42
include/plog/Appenders/DynamicAppender.h
Normal file
42
include/plog/Appenders/DynamicAppender.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/IAppender.h>
|
||||
#include <set>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
class PLOG_LINKAGE_HIDDEN DynamicAppender : public IAppender
|
||||
{
|
||||
public:
|
||||
DynamicAppender& addAppender(IAppender* appender)
|
||||
{
|
||||
assert(appender != this);
|
||||
|
||||
util::MutexLock lock(m_mutex);
|
||||
m_appenders.insert(appender);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DynamicAppender& removeAppender(IAppender* appender)
|
||||
{
|
||||
util::MutexLock lock(m_mutex);
|
||||
m_appenders.erase(appender);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual void write(const Record& record) PLOG_OVERRIDE
|
||||
{
|
||||
util::MutexLock lock(m_mutex);
|
||||
|
||||
for (std::set<IAppender*>::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it)
|
||||
{
|
||||
(*it)->write(record);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
mutable util::Mutex m_mutex;
|
||||
std::set<IAppender*> m_appenders;
|
||||
};
|
||||
}
|
117
include/plog/Appenders/EventLogAppender.h
Normal file
117
include/plog/Appenders/EventLogAppender.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/IAppender.h>
|
||||
#include <plog/WinApi.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template <class Formatter>
|
||||
class PLOG_LINKAGE_HIDDEN EventLogAppender : public IAppender
|
||||
{
|
||||
public:
|
||||
EventLogAppender(const util::nchar* sourceName) : m_eventSource(RegisterEventSourceW(NULL, util::toWide(sourceName).c_str()))
|
||||
{
|
||||
}
|
||||
|
||||
~EventLogAppender()
|
||||
{
|
||||
DeregisterEventSource(m_eventSource);
|
||||
}
|
||||
|
||||
virtual void write(const Record& record) PLOG_OVERRIDE
|
||||
{
|
||||
util::nstring str = Formatter::format(record);
|
||||
|
||||
write(record.getSeverity(), util::toWide(str).c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
void write(Severity severity, const wchar_t* str)
|
||||
{
|
||||
const wchar_t* logMessagePtr[] = { str };
|
||||
|
||||
ReportEventW(m_eventSource, logSeverityToType(severity), static_cast<WORD>(severity), 0, NULL, 1, 0, logMessagePtr, NULL);
|
||||
}
|
||||
|
||||
static WORD logSeverityToType(plog::Severity severity)
|
||||
{
|
||||
switch (severity)
|
||||
{
|
||||
case plog::fatal:
|
||||
case plog::error:
|
||||
return eventLog::kErrorType;
|
||||
|
||||
case plog::warning:
|
||||
return eventLog::kWarningType;
|
||||
|
||||
case plog::info:
|
||||
case plog::debug:
|
||||
case plog::verbose:
|
||||
default:
|
||||
return eventLog::kInformationType;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE m_eventSource;
|
||||
};
|
||||
|
||||
class EventLogAppenderRegistry
|
||||
{
|
||||
public:
|
||||
static bool add(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application"))
|
||||
{
|
||||
std::wstring logKeyName;
|
||||
std::wstring sourceKeyName;
|
||||
getKeyNames(sourceName, logName, sourceKeyName, logKeyName);
|
||||
|
||||
HKEY sourceKey;
|
||||
if (0 != RegCreateKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, NULL, 0, regSam::kSetValue, NULL, &sourceKey, NULL))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const DWORD kTypesSupported = eventLog::kErrorType | eventLog::kWarningType | eventLog::kInformationType;
|
||||
RegSetValueExW(sourceKey, L"TypesSupported", 0, regType::kDword, reinterpret_cast<const BYTE*>(&kTypesSupported), sizeof(kTypesSupported));
|
||||
|
||||
const wchar_t kEventMessageFile[] = L"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\EventLogMessages.dll;%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\EventLogMessages.dll";
|
||||
RegSetValueExW(sourceKey, L"EventMessageFile", 0, regType::kExpandSz, reinterpret_cast<const BYTE*>(kEventMessageFile), sizeof(kEventMessageFile) - sizeof(*kEventMessageFile));
|
||||
|
||||
RegCloseKey(sourceKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool exists(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application"))
|
||||
{
|
||||
std::wstring logKeyName;
|
||||
std::wstring sourceKeyName;
|
||||
getKeyNames(sourceName, logName, sourceKeyName, logKeyName);
|
||||
|
||||
HKEY sourceKey;
|
||||
if (0 != RegOpenKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, regSam::kQueryValue, &sourceKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RegCloseKey(sourceKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void remove(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application"))
|
||||
{
|
||||
std::wstring logKeyName;
|
||||
std::wstring sourceKeyName;
|
||||
getKeyNames(sourceName, logName, sourceKeyName, logKeyName);
|
||||
|
||||
RegDeleteKeyW(hkey::kLocalMachine, sourceKeyName.c_str());
|
||||
RegDeleteKeyW(hkey::kLocalMachine, logKeyName.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
static void getKeyNames(const util::nchar* sourceName, const util::nchar* logName, std::wstring& sourceKeyName, std::wstring& logKeyName)
|
||||
{
|
||||
const std::wstring kPrefix = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
|
||||
logKeyName = kPrefix + util::toWide(logName);
|
||||
sourceKeyName = logKeyName + L"\\" + util::toWide(sourceName);
|
||||
}
|
||||
};
|
||||
}
|
16
include/plog/Appenders/IAppender.h
Normal file
16
include/plog/Appenders/IAppender.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include <plog/Record.h>
|
||||
#include <plog/Util.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
class PLOG_LINKAGE IAppender
|
||||
{
|
||||
public:
|
||||
virtual ~IAppender()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void write(const Record& record) = 0;
|
||||
};
|
||||
}
|
148
include/plog/Appenders/RollingFileAppender.h
Normal file
148
include/plog/Appenders/RollingFileAppender.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/IAppender.h>
|
||||
#include <plog/Converters/UTF8Converter.h>
|
||||
#include <plog/Converters/NativeEOLConverter.h>
|
||||
#include <plog/Util.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<class Formatter, class Converter = NativeEOLConverter<UTF8Converter> >
|
||||
class PLOG_LINKAGE_HIDDEN RollingFileAppender : public IAppender
|
||||
{
|
||||
public:
|
||||
RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
: m_fileSize()
|
||||
, m_maxFileSize()
|
||||
, m_maxFiles(maxFiles)
|
||||
, m_firstWrite(true)
|
||||
{
|
||||
setFileName(fileName);
|
||||
setMaxFileSize(maxFileSize);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !PLOG_CHAR_IS_UTF8
|
||||
RollingFileAppender(const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
: m_fileSize()
|
||||
, m_maxFileSize()
|
||||
, m_maxFiles(maxFiles)
|
||||
, m_firstWrite(true)
|
||||
{
|
||||
setFileName(fileName);
|
||||
setMaxFileSize(maxFileSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void write(const Record& record) PLOG_OVERRIDE
|
||||
{
|
||||
util::MutexLock lock(m_mutex);
|
||||
|
||||
if (m_firstWrite)
|
||||
{
|
||||
openLogFile();
|
||||
m_firstWrite = false;
|
||||
}
|
||||
else if (m_maxFiles > 0 && m_fileSize > m_maxFileSize && static_cast<size_t>(-1) != m_fileSize)
|
||||
{
|
||||
rollLogFiles();
|
||||
}
|
||||
|
||||
size_t bytesWritten = m_file.write(Converter::convert(Formatter::format(record)));
|
||||
|
||||
if (static_cast<size_t>(-1) != bytesWritten)
|
||||
{
|
||||
m_fileSize += bytesWritten;
|
||||
}
|
||||
}
|
||||
|
||||
void setFileName(const util::nchar* fileName)
|
||||
{
|
||||
util::MutexLock lock(m_mutex);
|
||||
|
||||
util::splitFileName(fileName, m_fileNameNoExt, m_fileExt);
|
||||
|
||||
m_file.close();
|
||||
m_firstWrite = true;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !PLOG_CHAR_IS_UTF8
|
||||
void setFileName(const char* fileName)
|
||||
{
|
||||
setFileName(util::toWide(fileName).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
void setMaxFiles(int maxFiles)
|
||||
{
|
||||
m_maxFiles = maxFiles;
|
||||
}
|
||||
|
||||
void setMaxFileSize(size_t maxFileSize)
|
||||
{
|
||||
m_maxFileSize = (std::max)(maxFileSize, static_cast<size_t>(1000)); // set a lower limit for the maxFileSize
|
||||
}
|
||||
|
||||
void rollLogFiles()
|
||||
{
|
||||
m_file.close();
|
||||
|
||||
util::nstring lastFileName = buildFileName(m_maxFiles - 1);
|
||||
util::File::unlink(lastFileName);
|
||||
|
||||
for (int fileNumber = m_maxFiles - 2; fileNumber >= 0; --fileNumber)
|
||||
{
|
||||
util::nstring currentFileName = buildFileName(fileNumber);
|
||||
util::nstring nextFileName = buildFileName(fileNumber + 1);
|
||||
|
||||
util::File::rename(currentFileName, nextFileName);
|
||||
}
|
||||
|
||||
openLogFile();
|
||||
m_firstWrite = false;
|
||||
}
|
||||
|
||||
private:
|
||||
void openLogFile()
|
||||
{
|
||||
m_fileSize = m_file.open(buildFileName());
|
||||
|
||||
if (0 == m_fileSize)
|
||||
{
|
||||
size_t bytesWritten = m_file.write(Converter::header(Formatter::header()));
|
||||
|
||||
if (static_cast<size_t>(-1) != bytesWritten)
|
||||
{
|
||||
m_fileSize += bytesWritten;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
util::nstring buildFileName(int fileNumber = 0)
|
||||
{
|
||||
util::nostringstream ss;
|
||||
ss << m_fileNameNoExt;
|
||||
|
||||
if (fileNumber > 0)
|
||||
{
|
||||
ss << '.' << fileNumber;
|
||||
}
|
||||
|
||||
if (!m_fileExt.empty())
|
||||
{
|
||||
ss << '.' << m_fileExt;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
util::Mutex m_mutex;
|
||||
util::File m_file;
|
||||
size_t m_fileSize;
|
||||
size_t m_maxFileSize;
|
||||
int m_maxFiles;
|
||||
util::nstring m_fileExt;
|
||||
util::nstring m_fileNameNoExt;
|
||||
bool m_firstWrite;
|
||||
};
|
||||
}
|
44
include/plog/Converters/NativeEOLConverter.h
Normal file
44
include/plog/Converters/NativeEOLConverter.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
#include <plog/Converters/UTF8Converter.h>
|
||||
#include <plog/Util.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<class NextConverter = UTF8Converter>
|
||||
class NativeEOLConverter : public NextConverter
|
||||
{
|
||||
#ifdef _WIN32
|
||||
public:
|
||||
static std::string header(const util::nstring& str)
|
||||
{
|
||||
return NextConverter::header(fixLineEndings(str));
|
||||
}
|
||||
|
||||
static std::string convert(const util::nstring& str)
|
||||
{
|
||||
return NextConverter::convert(fixLineEndings(str));
|
||||
}
|
||||
|
||||
private:
|
||||
static util::nstring fixLineEndings(const util::nstring& str)
|
||||
{
|
||||
util::nstring output;
|
||||
output.reserve(str.length() * 2); // the worst case requires 2x chars
|
||||
|
||||
for (size_t i = 0; i < str.size(); ++i)
|
||||
{
|
||||
util::nchar ch = str[i];
|
||||
|
||||
if (ch == PLOG_NSTR('\n'))
|
||||
{
|
||||
output.push_back(PLOG_NSTR('\r'));
|
||||
}
|
||||
|
||||
output.push_back(ch);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
}
|
28
include/plog/Converters/UTF8Converter.h
Normal file
28
include/plog/Converters/UTF8Converter.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
#include <plog/Util.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
class UTF8Converter
|
||||
{
|
||||
public:
|
||||
static std::string header(const util::nstring& str)
|
||||
{
|
||||
const char kBOM[] = "\xEF\xBB\xBF";
|
||||
|
||||
return std::string(kBOM) + convert(str);
|
||||
}
|
||||
|
||||
#if PLOG_CHAR_IS_UTF8
|
||||
static const std::string& convert(const util::nstring& str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
#else
|
||||
static std::string convert(const util::nstring& str)
|
||||
{
|
||||
return util::toNarrow(str, codePage::kUTF8);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
}
|
57
include/plog/Formatters/CsvFormatter.h
Normal file
57
include/plog/Formatters/CsvFormatter.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
#include <plog/Record.h>
|
||||
#include <plog/Util.h>
|
||||
#include <iomanip>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<bool useUtcTime>
|
||||
class CsvFormatterImpl
|
||||
{
|
||||
public:
|
||||
static util::nstring header()
|
||||
{
|
||||
return PLOG_NSTR("Date;Time;Severity;TID;This;Function;Message\n");
|
||||
}
|
||||
|
||||
static util::nstring format(const Record& record)
|
||||
{
|
||||
tm t;
|
||||
useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time);
|
||||
|
||||
util::nostringstream ss;
|
||||
ss << t.tm_year + 1900 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(";");
|
||||
ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast<int> (record.getTime().millitm) << PLOG_NSTR(";");
|
||||
ss << severityToString(record.getSeverity()) << PLOG_NSTR(";");
|
||||
ss << record.getTid() << PLOG_NSTR(";");
|
||||
ss << record.getObject() << PLOG_NSTR(";");
|
||||
ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(";");
|
||||
|
||||
util::nstring message = record.getMessage();
|
||||
|
||||
if (message.size() > kMaxMessageSize)
|
||||
{
|
||||
message.resize(kMaxMessageSize);
|
||||
message.append(PLOG_NSTR("..."));
|
||||
}
|
||||
|
||||
util::nistringstream split(message);
|
||||
util::nstring token;
|
||||
|
||||
while (!split.eof())
|
||||
{
|
||||
std::getline(split, token, PLOG_NSTR('"'));
|
||||
ss << PLOG_NSTR("\"") << token << PLOG_NSTR("\"");
|
||||
}
|
||||
|
||||
ss << PLOG_NSTR("\n");
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static const size_t kMaxMessageSize = 32000;
|
||||
};
|
||||
|
||||
class CsvFormatter : public CsvFormatterImpl<false> {};
|
||||
class CsvFormatterUtcTime : public CsvFormatterImpl<true> {};
|
||||
}
|
23
include/plog/Formatters/FuncMessageFormatter.h
Normal file
23
include/plog/Formatters/FuncMessageFormatter.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include <plog/Record.h>
|
||||
#include <plog/Util.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
class FuncMessageFormatter
|
||||
{
|
||||
public:
|
||||
static util::nstring header()
|
||||
{
|
||||
return util::nstring();
|
||||
}
|
||||
|
||||
static util::nstring format(const Record& record)
|
||||
{
|
||||
util::nostringstream ss;
|
||||
ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(": ") << record.getMessage() << PLOG_NSTR("\n");
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
}
|
23
include/plog/Formatters/MessageOnlyFormatter.h
Normal file
23
include/plog/Formatters/MessageOnlyFormatter.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include <plog/Record.h>
|
||||
#include <plog/Util.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
class MessageOnlyFormatter
|
||||
{
|
||||
public:
|
||||
static util::nstring header()
|
||||
{
|
||||
return util::nstring();
|
||||
}
|
||||
|
||||
static util::nstring format(const Record& record)
|
||||
{
|
||||
util::nostringstream ss;
|
||||
ss << record.getMessage() << PLOG_NSTR("\n");
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
}
|
36
include/plog/Formatters/TxtFormatter.h
Normal file
36
include/plog/Formatters/TxtFormatter.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include <plog/Record.h>
|
||||
#include <plog/Util.h>
|
||||
#include <iomanip>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<bool useUtcTime>
|
||||
class TxtFormatterImpl
|
||||
{
|
||||
public:
|
||||
static util::nstring header()
|
||||
{
|
||||
return util::nstring();
|
||||
}
|
||||
|
||||
static util::nstring format(const Record& record)
|
||||
{
|
||||
tm t;
|
||||
useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time);
|
||||
|
||||
util::nostringstream ss;
|
||||
ss << t.tm_year + 1900 << "-" << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("-") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(" ");
|
||||
ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast<int> (record.getTime().millitm) << PLOG_NSTR(" ");
|
||||
ss << std::setfill(PLOG_NSTR(' ')) << std::setw(5) << std::left << severityToString(record.getSeverity()) << PLOG_NSTR(" ");
|
||||
ss << PLOG_NSTR("[") << record.getTid() << PLOG_NSTR("] ");
|
||||
ss << PLOG_NSTR("[") << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR("] ");
|
||||
ss << record.getMessage() << PLOG_NSTR("\n");
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class TxtFormatter : public TxtFormatterImpl<false> {};
|
||||
class TxtFormatterUtcTime : public TxtFormatterImpl<true> {};
|
||||
}
|
40
include/plog/Helpers/AscDump.h
Normal file
40
include/plog/Helpers/AscDump.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
#include <plog/Util.h>
|
||||
#include <cctype>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
class AscDump
|
||||
{
|
||||
public:
|
||||
AscDump(const void* ptr, size_t size)
|
||||
: m_ptr(static_cast<const char*>(ptr))
|
||||
, m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
friend util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump);
|
||||
|
||||
private:
|
||||
const char* m_ptr;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
inline util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump)
|
||||
{
|
||||
for (size_t i = 0; i < ascDump.m_size; ++i)
|
||||
{
|
||||
stream << (std::isprint(ascDump.m_ptr[i]) ? ascDump.m_ptr[i] : '.');
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline AscDump ascdump(const void* ptr, size_t size) { return AscDump(ptr, size); }
|
||||
|
||||
template<class Container>
|
||||
inline AscDump ascdump(const Container& container) { return AscDump(container.data(), container.size() * sizeof(*container.data())); }
|
||||
|
||||
template<class T, size_t N>
|
||||
inline AscDump ascdump(const T (&arr)[N]) { return AscDump(arr, N * sizeof(*arr)); }
|
||||
}
|
79
include/plog/Helpers/HexDump.h
Normal file
79
include/plog/Helpers/HexDump.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
#include <plog/Util.h>
|
||||
#include <iomanip>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
class HexDump
|
||||
{
|
||||
public:
|
||||
HexDump(const void* ptr, size_t size)
|
||||
: m_ptr(static_cast<const unsigned char*>(ptr))
|
||||
, m_size(size)
|
||||
, m_group(8)
|
||||
, m_digitSeparator(" ")
|
||||
, m_groupSeparator(" ")
|
||||
{
|
||||
}
|
||||
|
||||
HexDump& group(size_t group)
|
||||
{
|
||||
m_group = group;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HexDump& separator(const char* digitSeparator)
|
||||
{
|
||||
m_digitSeparator = digitSeparator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HexDump& separator(const char* digitSeparator, const char* groupSeparator)
|
||||
{
|
||||
m_digitSeparator = digitSeparator;
|
||||
m_groupSeparator = groupSeparator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend util::nostringstream& operator<<(util::nostringstream& stream, const HexDump&);
|
||||
|
||||
private:
|
||||
const unsigned char* m_ptr;
|
||||
size_t m_size;
|
||||
size_t m_group;
|
||||
const char* m_digitSeparator;
|
||||
const char* m_groupSeparator;
|
||||
};
|
||||
|
||||
inline util::nostringstream& operator<<(util::nostringstream& stream, const HexDump& hexDump)
|
||||
{
|
||||
stream << std::hex << std::setfill(PLOG_NSTR('0'));
|
||||
|
||||
for (size_t i = 0; i < hexDump.m_size;)
|
||||
{
|
||||
stream << std::setw(2) << static_cast<int>(hexDump.m_ptr[i]);
|
||||
|
||||
if (++i < hexDump.m_size)
|
||||
{
|
||||
if (hexDump.m_group > 0 && i % hexDump.m_group == 0)
|
||||
{
|
||||
stream << hexDump.m_groupSeparator;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << hexDump.m_digitSeparator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline HexDump hexdump(const void* ptr, size_t size) { return HexDump(ptr, size); }
|
||||
|
||||
template<class Container>
|
||||
inline HexDump hexdump(const Container& container) { return HexDump(container.data(), container.size() * sizeof(*container.data())); }
|
||||
|
||||
template<class T, size_t N>
|
||||
inline HexDump hexdump(const T (&arr)[N]) { return HexDump(arr, N * sizeof(*arr)); }
|
||||
}
|
24
include/plog/Helpers/PrintVar.h
Normal file
24
include/plog/Helpers/PrintVar.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#define PLOG_IMPL_PRINT_VAR_1(a1) #a1 ": " << a1
|
||||
#define PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_1(a1) PLOG_IMPL_PRINT_VAR_TAIL(a2)
|
||||
#define PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_TAIL(a3)
|
||||
#define PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_TAIL(a4)
|
||||
#define PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_TAIL(a5)
|
||||
#define PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_TAIL(a6)
|
||||
#define PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_TAIL(a7)
|
||||
#define PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_TAIL(a8)
|
||||
#define PLOG_IMPL_PRINT_VAR_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_TAIL(a9)
|
||||
#define PLOG_IMPL_PRINT_VAR_TAIL(a) << ", " PLOG_IMPL_PRINT_VAR_1(a)
|
||||
|
||||
#define PLOG_IMPL_PRINT_VAR_EXPAND(x) x
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC system_header // disable warning: variadic macros are a C99 feature
|
||||
#endif
|
||||
|
||||
#define PLOG_IMPL_PRINT_VAR_GET_MACRO(x1, x2, x3, x4, x5, x6, x7, x8, x9, NAME, ...) NAME
|
||||
|
||||
#define PLOG_PRINT_VAR(...) PLOG_IMPL_PRINT_VAR_EXPAND(PLOG_IMPL_PRINT_VAR_GET_MACRO(__VA_ARGS__,\
|
||||
PLOG_IMPL_PRINT_VAR_9, PLOG_IMPL_PRINT_VAR_8, PLOG_IMPL_PRINT_VAR_7, PLOG_IMPL_PRINT_VAR_6, PLOG_IMPL_PRINT_VAR_5,\
|
||||
PLOG_IMPL_PRINT_VAR_4, PLOG_IMPL_PRINT_VAR_3, PLOG_IMPL_PRINT_VAR_2, PLOG_IMPL_PRINT_VAR_1, UNUSED)(__VA_ARGS__))
|
17
include/plog/Init.h
Normal file
17
include/plog/Init.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#include <plog/Logger.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<int instanceId>
|
||||
PLOG_LINKAGE_HIDDEN inline Logger<instanceId>& init(Severity maxSeverity = none, IAppender* appender = NULL)
|
||||
{
|
||||
static Logger<instanceId> logger(maxSeverity);
|
||||
return appender ? logger.addAppender(appender) : logger;
|
||||
}
|
||||
|
||||
inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity = none, IAppender* appender = NULL)
|
||||
{
|
||||
return init<PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, appender);
|
||||
}
|
||||
}
|
22
include/plog/Initializers/ConsoleInitializer.h
Normal file
22
include/plog/Initializers/ConsoleInitializer.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/ColorConsoleAppender.h>
|
||||
#include <plog/Init.h>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ColorConsoleAppender with any Formatter
|
||||
|
||||
template<class Formatter, int instanceId>
|
||||
PLOG_LINKAGE_HIDDEN inline Logger<instanceId>& init(Severity maxSeverity, OutputStream outputStream)
|
||||
{
|
||||
static ColorConsoleAppender<Formatter> appender(outputStream);
|
||||
return init<instanceId>(maxSeverity, &appender);
|
||||
}
|
||||
|
||||
template<class Formatter>
|
||||
inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, OutputStream outputStream)
|
||||
{
|
||||
return init<Formatter, PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, outputStream);
|
||||
}
|
||||
}
|
80
include/plog/Initializers/RollingFileInitializer.h
Normal file
80
include/plog/Initializers/RollingFileInitializer.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/RollingFileAppender.h>
|
||||
#include <plog/Formatters/TxtFormatter.h>
|
||||
#include <plog/Formatters/CsvFormatter.h>
|
||||
#include <plog/Init.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// RollingFileAppender with any Formatter
|
||||
|
||||
template<class Formatter, int instanceId>
|
||||
PLOG_LINKAGE_HIDDEN inline Logger<instanceId>& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
{
|
||||
static RollingFileAppender<Formatter> rollingFileAppender(fileName, maxFileSize, maxFiles);
|
||||
return init<instanceId>(maxSeverity, &rollingFileAppender);
|
||||
}
|
||||
|
||||
template<class Formatter>
|
||||
inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
{
|
||||
return init<Formatter, PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, fileName, maxFileSize, maxFiles);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// RollingFileAppender with TXT/CSV chosen by file extension
|
||||
|
||||
namespace
|
||||
{
|
||||
inline bool isCsv(const util::nchar* fileName)
|
||||
{
|
||||
const util::nchar* dot = util::findExtensionDot(fileName);
|
||||
#if PLOG_CHAR_IS_UTF8
|
||||
return dot && 0 == std::strcmp(dot, ".csv");
|
||||
#else
|
||||
return dot && 0 == std::wcscmp(dot, L".csv");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template<int instanceId>
|
||||
inline Logger<instanceId>& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
{
|
||||
return isCsv(fileName) ? init<CsvFormatter, instanceId>(maxSeverity, fileName, maxFileSize, maxFiles) : init<TxtFormatter, instanceId>(maxSeverity, fileName, maxFileSize, maxFiles);
|
||||
}
|
||||
|
||||
inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
{
|
||||
return init<PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, fileName, maxFileSize, maxFiles);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CHAR variants for Windows
|
||||
|
||||
#if defined(_WIN32) && !PLOG_CHAR_IS_UTF8
|
||||
template<class Formatter, int instanceId>
|
||||
inline Logger<instanceId>& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
{
|
||||
return init<Formatter, instanceId>(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles);
|
||||
}
|
||||
|
||||
template<class Formatter>
|
||||
inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
{
|
||||
return init<Formatter, PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, fileName, maxFileSize, maxFiles);
|
||||
}
|
||||
|
||||
template<int instanceId>
|
||||
inline Logger<instanceId>& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
{
|
||||
return init<instanceId>(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles);
|
||||
}
|
||||
|
||||
inline Logger<PLOG_DEFAULT_INSTANCE_ID>& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
|
||||
{
|
||||
return init<PLOG_DEFAULT_INSTANCE_ID>(maxSeverity, fileName, maxFileSize, maxFiles);
|
||||
}
|
||||
#endif
|
||||
}
|
202
include/plog/Log.h
Normal file
202
include/plog/Log.h
Normal file
|
@ -0,0 +1,202 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
// Plog - portable and simple log for C++
|
||||
// Documentation and sources: https://github.com/SergiusTheBest/plog
|
||||
// License: MIT, https://choosealicense.com/licenses/mit
|
||||
|
||||
#pragma once
|
||||
#include <plog/Logger.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Helper macros that get context info
|
||||
|
||||
#if defined(PLOG_ENABLE_GET_THIS) && defined(_MSC_VER) && _MSC_VER >= 1600 && !defined(__INTELLISENSE__) && !defined(__INTEL_COMPILER) && !defined(__llvm__) && !defined(__RESHARPER__) // >= Visual Studio 2010, skip IntelliSense, Intel Compiler, Clang Code Model and ReSharper
|
||||
# define PLOG_GET_THIS() __if_exists(this) { this } __if_not_exists(this) { 0 }
|
||||
#else
|
||||
# define PLOG_GET_THIS() reinterpret_cast<void*>(0)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define PLOG_GET_FUNC() __FUNCTION__
|
||||
#elif defined(__BORLANDC__)
|
||||
# define PLOG_GET_FUNC() __FUNC__
|
||||
#else
|
||||
# define PLOG_GET_FUNC() __PRETTY_FUNCTION__
|
||||
#endif
|
||||
|
||||
#ifdef PLOG_CAPTURE_FILE
|
||||
# define PLOG_GET_FILE() __FILE__
|
||||
#else
|
||||
# define PLOG_GET_FILE() ""
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Log severity level checker
|
||||
|
||||
#ifdef PLOG_DISABLE_LOGGING
|
||||
# ifdef _MSC_VER
|
||||
# define IF_PLOG_(instanceId, severity) __pragma(warning(push)) __pragma(warning(disable:4127)) if (true) {;} else __pragma(warning(pop)) // conditional expression is constant
|
||||
# else
|
||||
# define IF_PLOG_(instanceId, severity) if (true) {;} else
|
||||
# endif
|
||||
#else
|
||||
# define IF_PLOG_(instanceId, severity) if (!plog::get<instanceId>() || !plog::get<instanceId>()->checkSeverity(severity)) {;} else
|
||||
#endif
|
||||
|
||||
#define IF_PLOG(severity) IF_PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Main logging macros
|
||||
|
||||
#define PLOG_(instanceId, severity) IF_PLOG_(instanceId, severity) (*plog::get<instanceId>()) += plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref()
|
||||
#define PLOG(severity) PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity)
|
||||
|
||||
#define PLOG_VERBOSE PLOG(plog::verbose)
|
||||
#define PLOG_DEBUG PLOG(plog::debug)
|
||||
#define PLOG_INFO PLOG(plog::info)
|
||||
#define PLOG_WARNING PLOG(plog::warning)
|
||||
#define PLOG_ERROR PLOG(plog::error)
|
||||
#define PLOG_FATAL PLOG(plog::fatal)
|
||||
#define PLOG_NONE PLOG(plog::none)
|
||||
|
||||
#define PLOG_VERBOSE_(instanceId) PLOG_(instanceId, plog::verbose)
|
||||
#define PLOG_DEBUG_(instanceId) PLOG_(instanceId, plog::debug)
|
||||
#define PLOG_INFO_(instanceId) PLOG_(instanceId, plog::info)
|
||||
#define PLOG_WARNING_(instanceId) PLOG_(instanceId, plog::warning)
|
||||
#define PLOG_ERROR_(instanceId) PLOG_(instanceId, plog::error)
|
||||
#define PLOG_FATAL_(instanceId) PLOG_(instanceId, plog::fatal)
|
||||
#define PLOG_NONE_(instanceId) PLOG_(instanceId, plog::none)
|
||||
|
||||
#define PLOGV PLOG_VERBOSE
|
||||
#define PLOGD PLOG_DEBUG
|
||||
#define PLOGI PLOG_INFO
|
||||
#define PLOGW PLOG_WARNING
|
||||
#define PLOGE PLOG_ERROR
|
||||
#define PLOGF PLOG_FATAL
|
||||
#define PLOGN PLOG_NONE
|
||||
|
||||
#define PLOGV_(instanceId) PLOG_VERBOSE_(instanceId)
|
||||
#define PLOGD_(instanceId) PLOG_DEBUG_(instanceId)
|
||||
#define PLOGI_(instanceId) PLOG_INFO_(instanceId)
|
||||
#define PLOGW_(instanceId) PLOG_WARNING_(instanceId)
|
||||
#define PLOGE_(instanceId) PLOG_ERROR_(instanceId)
|
||||
#define PLOGF_(instanceId) PLOG_FATAL_(instanceId)
|
||||
#define PLOGN_(instanceId) PLOG_NONE_(instanceId)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Conditional logging macros
|
||||
|
||||
#define PLOG_IF_(instanceId, severity, condition) if (!(condition)) {;} else PLOG_(instanceId, severity)
|
||||
#define PLOG_IF(severity, condition) PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition)
|
||||
|
||||
#define PLOG_VERBOSE_IF(condition) PLOG_IF(plog::verbose, condition)
|
||||
#define PLOG_DEBUG_IF(condition) PLOG_IF(plog::debug, condition)
|
||||
#define PLOG_INFO_IF(condition) PLOG_IF(plog::info, condition)
|
||||
#define PLOG_WARNING_IF(condition) PLOG_IF(plog::warning, condition)
|
||||
#define PLOG_ERROR_IF(condition) PLOG_IF(plog::error, condition)
|
||||
#define PLOG_FATAL_IF(condition) PLOG_IF(plog::fatal, condition)
|
||||
#define PLOG_NONE_IF(condition) PLOG_IF(plog::none, condition)
|
||||
|
||||
#define PLOG_VERBOSE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::verbose, condition)
|
||||
#define PLOG_DEBUG_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::debug, condition)
|
||||
#define PLOG_INFO_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::info, condition)
|
||||
#define PLOG_WARNING_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::warning, condition)
|
||||
#define PLOG_ERROR_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::error, condition)
|
||||
#define PLOG_FATAL_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::fatal, condition)
|
||||
#define PLOG_NONE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::none, condition)
|
||||
|
||||
#define PLOGV_IF(condition) PLOG_VERBOSE_IF(condition)
|
||||
#define PLOGD_IF(condition) PLOG_DEBUG_IF(condition)
|
||||
#define PLOGI_IF(condition) PLOG_INFO_IF(condition)
|
||||
#define PLOGW_IF(condition) PLOG_WARNING_IF(condition)
|
||||
#define PLOGE_IF(condition) PLOG_ERROR_IF(condition)
|
||||
#define PLOGF_IF(condition) PLOG_FATAL_IF(condition)
|
||||
#define PLOGN_IF(condition) PLOG_NONE_IF(condition)
|
||||
|
||||
#define PLOGV_IF_(instanceId, condition) PLOG_VERBOSE_IF_(instanceId, condition)
|
||||
#define PLOGD_IF_(instanceId, condition) PLOG_DEBUG_IF_(instanceId, condition)
|
||||
#define PLOGI_IF_(instanceId, condition) PLOG_INFO_IF_(instanceId, condition)
|
||||
#define PLOGW_IF_(instanceId, condition) PLOG_WARNING_IF_(instanceId, condition)
|
||||
#define PLOGE_IF_(instanceId, condition) PLOG_ERROR_IF_(instanceId, condition)
|
||||
#define PLOGF_IF_(instanceId, condition) PLOG_FATAL_IF_(instanceId, condition)
|
||||
#define PLOGN_IF_(instanceId, condition) PLOG_NONE_IF_(instanceId, condition)
|
||||
|
||||
// Old macro names for downward compatibility. To bypass including these macro names, add
|
||||
// #define PLOG_OMIT_LOG_DEFINES before #include <plog/Log.h>
|
||||
#ifndef PLOG_OMIT_LOG_DEFINES
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Main logging macros - can be changed later to point at macros for a different logging package
|
||||
|
||||
#define LOG_(instanceId, severity) IF_PLOG_(instanceId, severity) (*plog::get<instanceId>()) += plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref()
|
||||
#define LOG(severity) PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity)
|
||||
|
||||
#define LOG_VERBOSE PLOG(plog::verbose)
|
||||
#define LOG_DEBUG PLOG(plog::debug)
|
||||
#define LOG_INFO PLOG(plog::info)
|
||||
#define LOG_WARNING PLOG(plog::warning)
|
||||
#define LOG_ERROR PLOG(plog::error)
|
||||
#define LOG_FATAL PLOG(plog::fatal)
|
||||
#define LOG_NONE PLOG(plog::none)
|
||||
|
||||
#define LOG_VERBOSE_(instanceId) PLOG_(instanceId, plog::verbose)
|
||||
#define LOG_DEBUG_(instanceId) PLOG_(instanceId, plog::debug)
|
||||
#define LOG_INFO_(instanceId) PLOG_(instanceId, plog::info)
|
||||
#define LOG_WARNING_(instanceId) PLOG_(instanceId, plog::warning)
|
||||
#define LOG_ERROR_(instanceId) PLOG_(instanceId, plog::error)
|
||||
#define LOG_FATAL_(instanceId) PLOG_(instanceId, plog::fatal)
|
||||
#define LOG_NONE_(instanceId) PLOG_(instanceId, plog::none)
|
||||
|
||||
#define LOGV PLOG_VERBOSE
|
||||
#define LOGD PLOG_DEBUG
|
||||
#define LOGI PLOG_INFO
|
||||
#define LOGW PLOG_WARNING
|
||||
#define LOGE PLOG_ERROR
|
||||
#define LOGF PLOG_FATAL
|
||||
#define LOGN PLOG_NONE
|
||||
|
||||
#define LOGV_(instanceId) PLOG_VERBOSE_(instanceId)
|
||||
#define LOGD_(instanceId) PLOG_DEBUG_(instanceId)
|
||||
#define LOGI_(instanceId) PLOG_INFO_(instanceId)
|
||||
#define LOGW_(instanceId) PLOG_WARNING_(instanceId)
|
||||
#define LOGE_(instanceId) PLOG_ERROR_(instanceId)
|
||||
#define LOGF_(instanceId) PLOG_FATAL_(instanceId)
|
||||
#define LOGN_(instanceId) PLOG_NONE_(instanceId)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Conditional logging macros
|
||||
|
||||
#define LOG_IF_(instanceId, severity, condition) if (!(condition)) {;} else PLOG_(instanceId, severity)
|
||||
#define LOG_IF(severity, condition) PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition)
|
||||
|
||||
#define LOG_VERBOSE_IF(condition) PLOG_IF(plog::verbose, condition)
|
||||
#define LOG_DEBUG_IF(condition) PLOG_IF(plog::debug, condition)
|
||||
#define LOG_INFO_IF(condition) PLOG_IF(plog::info, condition)
|
||||
#define LOG_WARNING_IF(condition) PLOG_IF(plog::warning, condition)
|
||||
#define LOG_ERROR_IF(condition) PLOG_IF(plog::error, condition)
|
||||
#define LOG_FATAL_IF(condition) PLOG_IF(plog::fatal, condition)
|
||||
#define LOG_NONE_IF(condition) PLOG_IF(plog::none, condition)
|
||||
|
||||
#define LOG_VERBOSE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::verbose, condition)
|
||||
#define LOG_DEBUG_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::debug, condition)
|
||||
#define LOG_INFO_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::info, condition)
|
||||
#define LOG_WARNING_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::warning, condition)
|
||||
#define LOG_ERROR_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::error, condition)
|
||||
#define LOG_FATAL_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::fatal, condition)
|
||||
#define LOG_NONE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::none, condition)
|
||||
|
||||
#define LOGV_IF(condition) PLOG_VERBOSE_IF(condition)
|
||||
#define LOGD_IF(condition) PLOG_DEBUG_IF(condition)
|
||||
#define LOGI_IF(condition) PLOG_INFO_IF(condition)
|
||||
#define LOGW_IF(condition) PLOG_WARNING_IF(condition)
|
||||
#define LOGE_IF(condition) PLOG_ERROR_IF(condition)
|
||||
#define LOGF_IF(condition) PLOG_FATAL_IF(condition)
|
||||
#define LOGN_IF(condition) PLOG_NONE_IF(condition)
|
||||
|
||||
#define LOGV_IF_(instanceId, condition) PLOG_VERBOSE_IF_(instanceId, condition)
|
||||
#define LOGD_IF_(instanceId, condition) PLOG_DEBUG_IF_(instanceId, condition)
|
||||
#define LOGI_IF_(instanceId, condition) PLOG_INFO_IF_(instanceId, condition)
|
||||
#define LOGW_IF_(instanceId, condition) PLOG_WARNING_IF_(instanceId, condition)
|
||||
#define LOGE_IF_(instanceId, condition) PLOG_ERROR_IF_(instanceId, condition)
|
||||
#define LOGF_IF_(instanceId, condition) PLOG_FATAL_IF_(instanceId, condition)
|
||||
#define LOGN_IF_(instanceId, condition) PLOG_NONE_IF_(instanceId, condition)
|
||||
#endif
|
84
include/plog/Logger.h
Normal file
84
include/plog/Logger.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
#include <plog/Appenders/IAppender.h>
|
||||
#include <plog/Util.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef PLOG_DEFAULT_INSTANCE // for backward compatibility
|
||||
# define PLOG_DEFAULT_INSTANCE_ID PLOG_DEFAULT_INSTANCE
|
||||
#endif
|
||||
|
||||
#ifndef PLOG_DEFAULT_INSTANCE_ID
|
||||
# define PLOG_DEFAULT_INSTANCE_ID 0
|
||||
#endif
|
||||
|
||||
namespace plog
|
||||
{
|
||||
template<int instanceId>
|
||||
class PLOG_LINKAGE Logger : public util::Singleton<Logger<instanceId> >, public IAppender
|
||||
{
|
||||
public:
|
||||
Logger(Severity maxSeverity = none) : m_maxSeverity(maxSeverity)
|
||||
{
|
||||
}
|
||||
|
||||
Logger& addAppender(IAppender* appender)
|
||||
{
|
||||
assert(appender != this);
|
||||
m_appenders.push_back(appender);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Severity getMaxSeverity() const
|
||||
{
|
||||
return m_maxSeverity;
|
||||
}
|
||||
|
||||
void setMaxSeverity(Severity severity)
|
||||
{
|
||||
m_maxSeverity = severity;
|
||||
}
|
||||
|
||||
bool checkSeverity(Severity severity) const
|
||||
{
|
||||
return severity <= m_maxSeverity;
|
||||
}
|
||||
|
||||
virtual void write(const Record& record) PLOG_OVERRIDE
|
||||
{
|
||||
if (checkSeverity(record.getSeverity()))
|
||||
{
|
||||
*this += record;
|
||||
}
|
||||
}
|
||||
|
||||
void operator+=(const Record& record)
|
||||
{
|
||||
for (std::vector<IAppender*>::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it)
|
||||
{
|
||||
(*it)->write(record);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Severity m_maxSeverity;
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable:4251) // needs to have dll-interface to be used by clients of class
|
||||
#endif
|
||||
std::vector<IAppender*> m_appenders;
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
};
|
||||
|
||||
template<int instanceId>
|
||||
inline Logger<instanceId>* get()
|
||||
{
|
||||
return Logger<instanceId>::getInstance();
|
||||
}
|
||||
|
||||
inline Logger<PLOG_DEFAULT_INSTANCE_ID>* get()
|
||||
{
|
||||
return Logger<PLOG_DEFAULT_INSTANCE_ID>::getInstance();
|
||||
}
|
||||
}
|
465
include/plog/Record.h
Normal file
465
include/plog/Record.h
Normal file
|
@ -0,0 +1,465 @@
|
|||
#pragma once
|
||||
#include <cstdarg>
|
||||
#include <plog/Severity.h>
|
||||
#include <plog/Util.h>
|
||||
|
||||
#ifdef __cplusplus_cli
|
||||
#include <vcclr.h> // For PtrToStringChars
|
||||
#endif
|
||||
|
||||
namespace plog
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
#if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
|
||||
namespace meta
|
||||
{
|
||||
template<class T>
|
||||
inline T& declval()
|
||||
{
|
||||
#ifdef __INTEL_COMPILER
|
||||
# pragma warning(suppress: 327) // NULL reference is not allowed
|
||||
#endif
|
||||
return *reinterpret_cast<T*>(0);
|
||||
}
|
||||
|
||||
template<bool B, class T = void>
|
||||
struct enableIf {};
|
||||
|
||||
template<class T>
|
||||
struct enableIf<true, T> { typedef T type; };
|
||||
|
||||
struct No { char a[1]; };
|
||||
struct Yes { char a[2]; };
|
||||
|
||||
template <class From, class To>
|
||||
struct isConvertible
|
||||
{
|
||||
// `+ sizeof(U*)` is required for GCC 4.5-4.7
|
||||
template<class U>
|
||||
static typename enableIf<!!(sizeof(static_cast<To>(meta::declval<U>())) + sizeof(U*)), Yes>::type test(int);
|
||||
|
||||
template<class U>
|
||||
static No test(...);
|
||||
|
||||
enum { value = sizeof(test<From>(0)) == sizeof(Yes) };
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct isConvertibleToString : isConvertible<T, std::string> {};
|
||||
|
||||
#if PLOG_ENABLE_WCHAR_INPUT
|
||||
template <class T>
|
||||
struct isConvertibleToWString : isConvertible<T, std::wstring> {};
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct isContainer
|
||||
{
|
||||
template<class U>
|
||||
static typename meta::enableIf<!!(sizeof(
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1700 // MSVC 2010 doesn't understand `typename T::const_iterator`
|
||||
meta::declval<U>().begin()) + sizeof(meta::declval<U>().end()
|
||||
#else
|
||||
typename U::const_iterator
|
||||
#endif
|
||||
)), Yes>::type test(int);
|
||||
|
||||
template<class U>
|
||||
static No test(...);
|
||||
|
||||
enum { value = sizeof(test<T>(0)) == sizeof(Yes) };
|
||||
};
|
||||
|
||||
// Detects `std::filesystem::path` and `boost::filesystem::path`. They look like containers
|
||||
// but we don't want to treat them as containers, so we use this detector to filter them out.
|
||||
template <class T>
|
||||
struct isFilesystemPath
|
||||
{
|
||||
template<class U>
|
||||
static typename meta::enableIf<!!(sizeof(meta::declval<U>().preferred_separator)), Yes>::type test(int);
|
||||
|
||||
template<class U>
|
||||
static No test(...);
|
||||
|
||||
enum { value = sizeof(test<T>(0)) == sizeof(Yes) };
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Stream output operators as free functions
|
||||
|
||||
#if PLOG_ENABLE_WCHAR_INPUT
|
||||
inline void operator<<(util::nostringstream& stream, const wchar_t* data)
|
||||
{
|
||||
data = data ? data : L"(null)";
|
||||
|
||||
# ifdef _WIN32
|
||||
# if PLOG_CHAR_IS_UTF8
|
||||
std::operator<<(stream, util::toNarrow(data, codePage::kUTF8));
|
||||
# else
|
||||
std::operator<<(stream, data);
|
||||
# endif
|
||||
# else
|
||||
std::operator<<(stream, util::toNarrow(data));
|
||||
# endif
|
||||
}
|
||||
|
||||
inline void operator<<(util::nostringstream& stream, wchar_t* data)
|
||||
{
|
||||
plog::detail::operator<<(stream, const_cast<const wchar_t*>(data));
|
||||
}
|
||||
|
||||
inline void operator<<(util::nostringstream& stream, const std::wstring& data)
|
||||
{
|
||||
plog::detail::operator<<(stream, data.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void operator<<(util::nostringstream& stream, const char* data)
|
||||
{
|
||||
data = data ? data : "(null)";
|
||||
|
||||
#if defined(_WIN32) && defined(__BORLANDC__)
|
||||
# if PLOG_CHAR_IS_UTF8
|
||||
stream << data;
|
||||
# else
|
||||
stream << util::toWide(data);
|
||||
# endif
|
||||
#elif defined(_WIN32)
|
||||
# if PLOG_CHAR_IS_UTF8
|
||||
std::operator<<(stream, data);
|
||||
# else
|
||||
std::operator<<(stream, util::toWide(data));
|
||||
# endif
|
||||
#else
|
||||
std::operator<<(stream, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void operator<<(util::nostringstream& stream, char* data)
|
||||
{
|
||||
plog::detail::operator<<(stream, const_cast<const char*>(data));
|
||||
}
|
||||
|
||||
inline void operator<<(util::nostringstream& stream, const std::string& data)
|
||||
{
|
||||
plog::detail::operator<<(stream, data.c_str());
|
||||
}
|
||||
|
||||
#ifdef __cpp_char8_t
|
||||
inline void operator<<(util::nostringstream& stream, const char8_t* data)
|
||||
{
|
||||
# if PLOG_CHAR_IS_UTF8
|
||||
plog::detail::operator<<(stream, reinterpret_cast<const char*>(data));
|
||||
# else
|
||||
plog::detail::operator<<(stream, util::toWide(reinterpret_cast<const char*>(data), codePage::kUTF8));
|
||||
# endif
|
||||
}
|
||||
#endif //__cpp_char8_t
|
||||
|
||||
// Print `std::pair`
|
||||
template<class T1, class T2>
|
||||
inline void operator<<(util::nostringstream& stream, const std::pair<T1, T2>& data)
|
||||
{
|
||||
stream << data.first;
|
||||
stream << ":";
|
||||
stream << data.second;
|
||||
}
|
||||
|
||||
#if defined(__clang__) || !defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 // skip for GCC < 4.5 due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38600
|
||||
#if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
|
||||
// Print data that can be casted to `std::string`
|
||||
template<class T>
|
||||
inline typename meta::enableIf<meta::isConvertibleToString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
|
||||
{
|
||||
plog::detail::operator<<(stream, static_cast<std::string>(data));
|
||||
}
|
||||
|
||||
#if PLOG_ENABLE_WCHAR_INPUT
|
||||
// Print data that can be casted to `std::wstring`
|
||||
template<class T>
|
||||
inline typename meta::enableIf<meta::isConvertibleToWString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
|
||||
{
|
||||
plog::detail::operator<<(stream, static_cast<std::wstring>(data));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Print std containers
|
||||
template<class T>
|
||||
inline typename meta::enableIf<meta::isContainer<T>::value &&
|
||||
!meta::isConvertibleToString<T>::value &&
|
||||
#if PLOG_ENABLE_WCHAR_INPUT
|
||||
!meta::isConvertibleToWString<T>::value &&
|
||||
#endif
|
||||
!meta::isFilesystemPath<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
|
||||
{
|
||||
stream << "[";
|
||||
|
||||
for (typename T::const_iterator it = data.begin(); it != data.end();)
|
||||
{
|
||||
stream << *it;
|
||||
|
||||
if (++it == data.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
stream << ", ";
|
||||
}
|
||||
|
||||
stream << "]";
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus_cli
|
||||
// Print managed C++ `System::String^`
|
||||
inline void operator<<(util::nostringstream& stream, System::String^ data)
|
||||
{
|
||||
cli::pin_ptr<const System::Char> ptr = PtrToStringChars(data);
|
||||
plog::detail::operator<<(stream, static_cast<const wchar_t*>(ptr));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PLOG_ENABLE_WCHAR_INPUT && (!defined(_MSC_VER) || _MSC_VER > 1400) // MSVC 2005 doesn't understand `enableIf`, so drop all `meta`
|
||||
namespace meta
|
||||
{
|
||||
template<bool Value>
|
||||
struct valueType { enum { value = Value }; };
|
||||
|
||||
template<class T, class Stream>
|
||||
inline No operator<<(Stream&, const T&);
|
||||
|
||||
template<class T, class Stream>
|
||||
struct isStreamable : valueType<sizeof(operator<<(meta::declval<Stream>(), meta::declval<const T>())) != sizeof(No)> {};
|
||||
|
||||
template<class Stream>
|
||||
struct isStreamable<std::ios_base& PLOG_CDECL (std::ios_base&), Stream> : valueType<true> {};
|
||||
|
||||
template<class Stream, size_t N>
|
||||
struct isStreamable<wchar_t[N], Stream> : valueType<false> {};
|
||||
|
||||
template<class Stream, size_t N>
|
||||
struct isStreamable<const wchar_t[N], Stream> : valueType<false> {};
|
||||
|
||||
// meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const wchar_t*) = delete` so exlicitly define it
|
||||
template<>
|
||||
struct isStreamable<const wchar_t*, std::ostream> : valueType<false> {};
|
||||
|
||||
# ifdef __cpp_char8_t
|
||||
// meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const char8_t*) = delete` so exlicitly define it
|
||||
template<class Stream, size_t N>
|
||||
struct isStreamable<char8_t[N], Stream> : valueType<false> {};
|
||||
|
||||
template<class Stream>
|
||||
struct isStreamable<const char8_t*, Stream> : valueType<false> {};
|
||||
# endif //__cpp_char8_t
|
||||
}
|
||||
|
||||
# if PLOG_CHAR_IS_UTF8
|
||||
// Print types that can be streamed into `std::owstringstream` but not into `std::ostringstream` when we use UTF-8 on Windows
|
||||
template<class T>
|
||||
inline typename meta::enableIf<meta::isStreamable<T, std::wostream>::value &&
|
||||
!meta::isStreamable<T, std::ostream>::value &&
|
||||
!meta::isConvertibleToWString<T>::value, void>::type operator<<(std::ostringstream& stream, const T& data)
|
||||
{
|
||||
std::wostringstream ss;
|
||||
ss << data;
|
||||
stream << ss.str();
|
||||
}
|
||||
# else
|
||||
// Print types that can be streamed into `std::ostringstream` but not into `std::owstringstream` when we use `wchar_t` on Windows
|
||||
template<class T>
|
||||
inline typename meta::enableIf<meta::isStreamable<T, std::ostream>::value &&
|
||||
!meta::isStreamable<T, std::wostream>::value &&
|
||||
!meta::isConvertibleToString<T>::value, void>::type operator<<(std::wostringstream& stream, const T& data)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << data;
|
||||
stream << ss.str();
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
class Record
|
||||
{
|
||||
public:
|
||||
Record(Severity severity, const char* func, size_t line, const char* file, const void* object, int instanceId)
|
||||
: m_severity(severity), m_tid(util::gettid()), m_object(object), m_line(line), m_func(func), m_file(file), m_instanceId(instanceId)
|
||||
{
|
||||
util::ftime(&m_time);
|
||||
}
|
||||
|
||||
Record& ref()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Stream output operators
|
||||
|
||||
Record& operator<<(char data)
|
||||
{
|
||||
char str[] = { data, 0 };
|
||||
return *this << str;
|
||||
}
|
||||
|
||||
#if PLOG_ENABLE_WCHAR_INPUT
|
||||
Record& operator<<(wchar_t data)
|
||||
{
|
||||
wchar_t str[] = { data, 0 };
|
||||
return *this << str;
|
||||
}
|
||||
#endif
|
||||
|
||||
Record& operator<<(util::nostream& (PLOG_CDECL *data)(util::nostream&))
|
||||
{
|
||||
m_message << data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef QT_VERSION
|
||||
Record& operator<<(const QString& data)
|
||||
{
|
||||
# if PLOG_CHAR_IS_UTF8
|
||||
return *this << data.toStdString();
|
||||
# else
|
||||
return *this << data.toStdWString();
|
||||
# endif
|
||||
}
|
||||
|
||||
# if QT_VERSION < 0x060000
|
||||
Record& operator<<(const QStringRef& data)
|
||||
{
|
||||
return *this << data.toString();
|
||||
}
|
||||
# endif
|
||||
|
||||
# ifdef QSTRINGVIEW_H
|
||||
Record& operator<<(QStringView data)
|
||||
{
|
||||
return *this << data.toString();
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
Record& operator<<(const T& data)
|
||||
{
|
||||
using namespace plog::detail;
|
||||
|
||||
m_message << data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifndef __cplusplus_cli
|
||||
Record& printf(const char* format, ...)
|
||||
{
|
||||
using namespace util;
|
||||
|
||||
char* str = NULL;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
int len = vasprintf(&str, format, ap);
|
||||
static_cast<void>(len);
|
||||
va_end(ap);
|
||||
|
||||
*this << str;
|
||||
free(str);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
Record& printf(const wchar_t* format, ...)
|
||||
{
|
||||
using namespace util;
|
||||
|
||||
wchar_t* str = NULL;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
int len = vaswprintf(&str, format, ap);
|
||||
static_cast<void>(len);
|
||||
va_end(ap);
|
||||
|
||||
*this << str;
|
||||
free(str);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
#endif //__cplusplus_cli
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
|
||||
virtual const util::Time& getTime() const
|
||||
{
|
||||
return m_time;
|
||||
}
|
||||
|
||||
virtual Severity getSeverity() const
|
||||
{
|
||||
return m_severity;
|
||||
}
|
||||
|
||||
virtual unsigned int getTid() const
|
||||
{
|
||||
return m_tid;
|
||||
}
|
||||
|
||||
virtual const void* getObject() const
|
||||
{
|
||||
return m_object;
|
||||
}
|
||||
|
||||
virtual size_t getLine() const
|
||||
{
|
||||
return m_line;
|
||||
}
|
||||
|
||||
virtual const util::nchar* getMessage() const
|
||||
{
|
||||
m_messageStr = m_message.str();
|
||||
return m_messageStr.c_str();
|
||||
}
|
||||
|
||||
virtual const char* getFunc() const
|
||||
{
|
||||
m_funcStr = util::processFuncName(m_func);
|
||||
return m_funcStr.c_str();
|
||||
}
|
||||
|
||||
virtual const char* getFile() const
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
|
||||
virtual ~Record() // virtual destructor to satisfy -Wnon-virtual-dtor warning
|
||||
{
|
||||
}
|
||||
|
||||
virtual int getInstanceId() const
|
||||
{
|
||||
return m_instanceId;
|
||||
}
|
||||
|
||||
private:
|
||||
util::Time m_time;
|
||||
const Severity m_severity;
|
||||
const unsigned int m_tid;
|
||||
const void* const m_object;
|
||||
const size_t m_line;
|
||||
util::nostringstream m_message;
|
||||
const char* const m_func;
|
||||
const char* const m_file;
|
||||
const int m_instanceId;
|
||||
mutable std::string m_funcStr;
|
||||
mutable util::nstring m_messageStr;
|
||||
};
|
||||
}
|
61
include/plog/Severity.h
Normal file
61
include/plog/Severity.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
#include <cctype>
|
||||
|
||||
namespace plog
|
||||
{
|
||||
enum Severity
|
||||
{
|
||||
none = 0,
|
||||
fatal = 1,
|
||||
error = 2,
|
||||
warning = 3,
|
||||
info = 4,
|
||||
debug = 5,
|
||||
verbose = 6
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum'
|
||||
#endif
|
||||
inline const char* severityToString(Severity severity)
|
||||
{
|
||||
switch (severity)
|
||||
{
|
||||
case fatal:
|
||||
return "FATAL";
|
||||
case error:
|
||||
return "ERROR";
|
||||
case warning:
|
||||
return "WARN";
|
||||
case info:
|
||||
return "INFO";
|
||||
case debug:
|
||||
return "DEBUG";
|
||||
case verbose:
|
||||
return "VERB";
|
||||
default:
|
||||
return "NONE";
|
||||
}
|
||||
}
|
||||
|
||||
inline Severity severityFromString(const char* str)
|
||||
{
|
||||
switch (std::toupper(str[0]))
|
||||
{
|
||||
case 'F':
|
||||
return fatal;
|
||||
case 'E':
|
||||
return error;
|
||||
case 'W':
|
||||
return warning;
|
||||
case 'I':
|
||||
return info;
|
||||
case 'D':
|
||||
return debug;
|
||||
case 'V':
|
||||
return verbose;
|
||||
default:
|
||||
return none;
|
||||
}
|
||||
}
|
||||
}
|
616
include/plog/Util.h
Normal file
616
include/plog/Util.h
Normal file
|
@ -0,0 +1,616 @@
|
|||
#pragma once
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef PLOG_ENABLE_WCHAR_INPUT
|
||||
# ifdef _WIN32
|
||||
# define PLOG_ENABLE_WCHAR_INPUT 1
|
||||
# else
|
||||
# define PLOG_ENABLE_WCHAR_INPUT 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PLOG_CHAR_IS_UTF8 specifies character encoding of `char` type. On *nix
|
||||
// systems it's set to UTF-8 while on Windows in can be ANSI or UTF-8. It
|
||||
// automatically detects `/utf-8` command line option in MSVC. Also it can
|
||||
// be set manually if required.
|
||||
// This option allows to support http://utf8everywhere.org approach.
|
||||
|
||||
#ifndef PLOG_CHAR_IS_UTF8
|
||||
# if defined(_WIN32) && !defined(_UTF8)
|
||||
# define PLOG_CHAR_IS_UTF8 0
|
||||
# else
|
||||
# define PLOG_CHAR_IS_UTF8 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# if defined(PLOG_EXPORT)
|
||||
# define PLOG_LINKAGE __declspec(dllexport)
|
||||
# elif defined(PLOG_IMPORT)
|
||||
# define PLOG_LINKAGE __declspec(dllimport)
|
||||
# endif
|
||||
# if defined(PLOG_GLOBAL)
|
||||
# error "PLOG_GLOBAL isn't supported on Windows"
|
||||
# endif
|
||||
#else
|
||||
# if defined(PLOG_GLOBAL)
|
||||
# define PLOG_LINKAGE __attribute__ ((visibility ("default")))
|
||||
# elif defined(PLOG_LOCAL)
|
||||
# define PLOG_LINKAGE __attribute__ ((visibility ("hidden")))
|
||||
# define PLOG_LINKAGE_HIDDEN PLOG_LINKAGE
|
||||
# endif
|
||||
# if defined(PLOG_EXPORT) || defined(PLOG_IMPORT)
|
||||
# error "PLOG_EXPORT/PLOG_IMPORT is supported only on Windows"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PLOG_LINKAGE
|
||||
# define PLOG_LINKAGE
|
||||
#endif
|
||||
|
||||
#ifndef PLOG_LINKAGE_HIDDEN
|
||||
# define PLOG_LINKAGE_HIDDEN
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <plog/WinApi.h>
|
||||
# include <time.h>
|
||||
# include <sys/timeb.h>
|
||||
# include <io.h>
|
||||
# include <share.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <sys/time.h>
|
||||
# if defined(__linux__) || defined(__FreeBSD__)
|
||||
# include <sys/syscall.h>
|
||||
# elif defined(__rtems__)
|
||||
# include <rtems.h>
|
||||
# endif
|
||||
# if defined(_POSIX_THREADS)
|
||||
# include <pthread.h>
|
||||
# endif
|
||||
# if PLOG_ENABLE_WCHAR_INPUT
|
||||
# include <iconv.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if PLOG_CHAR_IS_UTF8
|
||||
# define PLOG_NSTR(x) x
|
||||
#else
|
||||
# define _PLOG_NSTR(x) L##x
|
||||
# define PLOG_NSTR(x) _PLOG_NSTR(x)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# define PLOG_CDECL __cdecl
|
||||
#else
|
||||
# define PLOG_CDECL
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700
|
||||
# define PLOG_OVERRIDE override
|
||||
#else
|
||||
# define PLOG_OVERRIDE
|
||||
#endif
|
||||
|
||||
namespace plog
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
#if PLOG_CHAR_IS_UTF8
|
||||
typedef std::string nstring;
|
||||
typedef std::ostringstream nostringstream;
|
||||
typedef std::istringstream nistringstream;
|
||||
typedef std::ostream nostream;
|
||||
typedef char nchar;
|
||||
#else
|
||||
typedef std::wstring nstring;
|
||||
typedef std::wostringstream nostringstream;
|
||||
typedef std::wistringstream nistringstream;
|
||||
typedef std::wostream nostream;
|
||||
typedef wchar_t nchar;
|
||||
#endif
|
||||
|
||||
inline void localtime_s(struct tm* t, const time_t* time)
|
||||
{
|
||||
#if defined(_WIN32) && defined(__BORLANDC__)
|
||||
::localtime_s(time, t);
|
||||
#elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
*t = *::localtime(time);
|
||||
#elif defined(_WIN32)
|
||||
::localtime_s(t, time);
|
||||
#else
|
||||
::localtime_r(time, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void gmtime_s(struct tm* t, const time_t* time)
|
||||
{
|
||||
#if defined(_WIN32) && defined(__BORLANDC__)
|
||||
::gmtime_s(time, t);
|
||||
#elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
*t = *::gmtime(time);
|
||||
#elif defined(_WIN32)
|
||||
::gmtime_s(t, time);
|
||||
#else
|
||||
::gmtime_r(time, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef timeb Time;
|
||||
|
||||
inline void ftime(Time* t)
|
||||
{
|
||||
::ftime(t);
|
||||
}
|
||||
#else
|
||||
struct Time
|
||||
{
|
||||
time_t time;
|
||||
unsigned short millitm;
|
||||
};
|
||||
|
||||
inline void ftime(Time* t)
|
||||
{
|
||||
timeval tv;
|
||||
::gettimeofday(&tv, NULL);
|
||||
|
||||
t->time = tv.tv_sec;
|
||||
t->millitm = static_cast<unsigned short>(tv.tv_usec / 1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline unsigned int gettid()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return GetCurrentThreadId();
|
||||
#elif defined(__linux__)
|
||||
return static_cast<unsigned int>(::syscall(__NR_gettid));
|
||||
#elif defined(__FreeBSD__)
|
||||
long tid;
|
||||
syscall(SYS_thr_self, &tid);
|
||||
return static_cast<unsigned int>(tid);
|
||||
#elif defined(__rtems__)
|
||||
return rtems_task_self();
|
||||
#elif defined(__APPLE__)
|
||||
uint64_t tid64;
|
||||
pthread_threadid_np(NULL, &tid64);
|
||||
return static_cast<unsigned int>(tid64);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
inline int vasprintf(char** strp, const char* format, va_list ap)
|
||||
{
|
||||
va_list ap_copy;
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1600
|
||||
ap_copy = ap; // there is no va_copy on Visual Studio 2010
|
||||
#else
|
||||
va_copy(ap_copy, ap);
|
||||
#endif
|
||||
#ifndef __STDC_SECURE_LIB__
|
||||
int charCount = vsnprintf(NULL, 0, format, ap_copy);
|
||||
#else
|
||||
int charCount = _vscprintf(format, ap_copy);
|
||||
#endif
|
||||
va_end(ap_copy);
|
||||
if (charCount < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t bufferCharCount = static_cast<size_t>(charCount) + 1;
|
||||
|
||||
char* str = static_cast<char*>(malloc(bufferCharCount));
|
||||
if (!str)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef __STDC_SECURE_LIB__
|
||||
int retval = vsnprintf(str, bufferCharCount, format, ap);
|
||||
#else
|
||||
int retval = vsnprintf_s(str, bufferCharCount, static_cast<size_t>(-1), format, ap);
|
||||
#endif
|
||||
if (retval < 0)
|
||||
{
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*strp = str;
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
inline int vaswprintf(wchar_t** strp, const wchar_t* format, va_list ap)
|
||||
{
|
||||
#if defined(__BORLANDC__)
|
||||
int charCount = 0x1000; // there is no _vscwprintf on Borland/Embarcadero
|
||||
#else
|
||||
int charCount = _vscwprintf(format, ap);
|
||||
if (charCount < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t bufferCharCount = static_cast<size_t>(charCount) + 1;
|
||||
|
||||
wchar_t* str = static_cast<wchar_t*>(malloc(bufferCharCount * sizeof(wchar_t)));
|
||||
if (!str)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__BORLANDC__)
|
||||
int retval = vsnwprintf_s(str, bufferCharCount, format, ap);
|
||||
#elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
int retval = _vsnwprintf(str, bufferCharCount, format, ap);
|
||||
#else
|
||||
int retval = _vsnwprintf_s(str, bufferCharCount, charCount, format, ap);
|
||||
#endif
|
||||
if (retval < 0)
|
||||
{
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*strp = str;
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
inline std::wstring toWide(const char* str, UINT cp = codePage::kChar)
|
||||
{
|
||||
size_t len = ::strlen(str);
|
||||
std::wstring wstr(len, 0);
|
||||
|
||||
if (!wstr.empty())
|
||||
{
|
||||
int wlen = MultiByteToWideChar(cp, 0, str, static_cast<int>(len), &wstr[0], static_cast<int>(wstr.size()));
|
||||
wstr.resize(wlen);
|
||||
}
|
||||
|
||||
return wstr;
|
||||
}
|
||||
|
||||
inline std::wstring toWide(const std::string& str, UINT cp = codePage::kChar)
|
||||
{
|
||||
return toWide(str.c_str(), cp);
|
||||
}
|
||||
|
||||
inline const std::wstring& toWide(const std::wstring& str) // do nothing for already wide string
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
inline std::string toNarrow(const std::wstring& wstr, long page)
|
||||
{
|
||||
int len = WideCharToMultiByte(page, 0, wstr.c_str(), static_cast<int>(wstr.size()), 0, 0, 0, 0);
|
||||
std::string str(len, 0);
|
||||
|
||||
if (!str.empty())
|
||||
{
|
||||
WideCharToMultiByte(page, 0, wstr.c_str(), static_cast<int>(wstr.size()), &str[0], len, 0, 0);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
#elif PLOG_ENABLE_WCHAR_INPUT
|
||||
inline std::string toNarrow(const wchar_t* wstr)
|
||||
{
|
||||
size_t wlen = ::wcslen(wstr);
|
||||
std::string str(wlen * sizeof(wchar_t), 0);
|
||||
|
||||
if (!str.empty())
|
||||
{
|
||||
const char* in = reinterpret_cast<const char*>(&wstr[0]);
|
||||
char* out = &str[0];
|
||||
size_t inBytes = wlen * sizeof(wchar_t);
|
||||
size_t outBytes = str.size();
|
||||
|
||||
iconv_t cd = ::iconv_open("UTF-8", "WCHAR_T");
|
||||
::iconv(cd, const_cast<char**>(&in), &inBytes, &out, &outBytes);
|
||||
::iconv_close(cd);
|
||||
|
||||
str.resize(str.size() - outBytes);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline std::string processFuncName(const char* func)
|
||||
{
|
||||
#if (defined(_WIN32) && !defined(__MINGW32__)) || defined(__OBJC__)
|
||||
return std::string(func);
|
||||
#else
|
||||
const char* funcBegin = func;
|
||||
const char* funcEnd = ::strchr(funcBegin, '(');
|
||||
int foundTemplate = 0;
|
||||
|
||||
if (!funcEnd)
|
||||
{
|
||||
return std::string(func);
|
||||
}
|
||||
|
||||
for (const char* i = funcEnd - 1; i >= funcBegin; --i) // search backwards for the first space char
|
||||
{
|
||||
if (*i == '>')
|
||||
{
|
||||
foundTemplate++;
|
||||
}
|
||||
else if (*i == '<')
|
||||
{
|
||||
foundTemplate--;
|
||||
}
|
||||
else if (*i == ' ' && foundTemplate == 0)
|
||||
{
|
||||
funcBegin = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string(funcBegin, funcEnd);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline const nchar* findExtensionDot(const nchar* fileName)
|
||||
{
|
||||
#if PLOG_CHAR_IS_UTF8
|
||||
return std::strrchr(fileName, '.');
|
||||
#else
|
||||
return std::wcsrchr(fileName, L'.');
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void splitFileName(const nchar* fileName, nstring& fileNameNoExt, nstring& fileExt)
|
||||
{
|
||||
const nchar* dot = findExtensionDot(fileName);
|
||||
|
||||
if (dot)
|
||||
{
|
||||
fileNameNoExt.assign(fileName, dot);
|
||||
fileExt.assign(dot + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileNameNoExt.assign(fileName);
|
||||
fileExt.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class PLOG_LINKAGE NonCopyable
|
||||
{
|
||||
protected:
|
||||
NonCopyable()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
NonCopyable(const NonCopyable&);
|
||||
NonCopyable& operator=(const NonCopyable&);
|
||||
};
|
||||
|
||||
class PLOG_LINKAGE_HIDDEN File : NonCopyable
|
||||
{
|
||||
public:
|
||||
File() : m_file(-1)
|
||||
{
|
||||
}
|
||||
|
||||
~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
size_t open(const nstring& fileName)
|
||||
{
|
||||
#if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__))
|
||||
m_file = ::_wsopen(toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, SH_DENYWR, _S_IREAD | _S_IWRITE);
|
||||
#elif defined(_WIN32)
|
||||
::_wsopen_s(&m_file, toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, _SH_DENYWR, _S_IREAD | _S_IWRITE);
|
||||
#elif defined(O_CLOEXEC)
|
||||
m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
#else
|
||||
m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
#endif
|
||||
return seek(0, SEEK_END);
|
||||
}
|
||||
|
||||
size_t write(const void* buf, size_t count)
|
||||
{
|
||||
return m_file != -1 ? static_cast<size_t>(
|
||||
#ifdef _WIN32
|
||||
::_write(m_file, buf, static_cast<unsigned int>(count))
|
||||
#else
|
||||
::write(m_file, buf, count)
|
||||
#endif
|
||||
) : static_cast<size_t>(-1);
|
||||
}
|
||||
|
||||
template<class CharType>
|
||||
size_t write(const std::basic_string<CharType>& str)
|
||||
{
|
||||
return write(str.data(), str.size() * sizeof(CharType));
|
||||
}
|
||||
|
||||
size_t seek(size_t offset, int whence)
|
||||
{
|
||||
return m_file != -1 ? static_cast<size_t>(
|
||||
#if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__))
|
||||
::_lseek(m_file, static_cast<off_t>(offset), whence)
|
||||
#elif defined(_WIN32)
|
||||
::_lseeki64(m_file, static_cast<off_t>(offset), whence)
|
||||
#else
|
||||
::lseek(m_file, static_cast<off_t>(offset), whence)
|
||||
#endif
|
||||
) : static_cast<size_t>(-1);
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (m_file != -1)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::_close(m_file);
|
||||
#else
|
||||
::close(m_file);
|
||||
#endif
|
||||
m_file = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int unlink(const nstring& fileName)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ::_wunlink(toWide(fileName).c_str());
|
||||
#else
|
||||
return ::unlink(fileName.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
static int rename(const nstring& oldFilename, const nstring& newFilename)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return MoveFileW(toWide(oldFilename).c_str(), toWide(newFilename).c_str());
|
||||
#else
|
||||
return ::rename(oldFilename.c_str(), newFilename.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
int m_file;
|
||||
};
|
||||
|
||||
class PLOG_LINKAGE_HIDDEN Mutex : NonCopyable
|
||||
{
|
||||
public:
|
||||
Mutex()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
InitializeCriticalSection(&m_sync);
|
||||
#elif defined(__rtems__)
|
||||
rtems_semaphore_create(0, 1,
|
||||
RTEMS_PRIORITY |
|
||||
RTEMS_BINARY_SEMAPHORE |
|
||||
RTEMS_INHERIT_PRIORITY, 1, &m_sync);
|
||||
#elif defined(_POSIX_THREADS)
|
||||
::pthread_mutex_init(&m_sync, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
~Mutex()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DeleteCriticalSection(&m_sync);
|
||||
#elif defined(__rtems__)
|
||||
rtems_semaphore_delete(m_sync);
|
||||
#elif defined(_POSIX_THREADS)
|
||||
::pthread_mutex_destroy(&m_sync);
|
||||
#endif
|
||||
}
|
||||
|
||||
friend class MutexLock;
|
||||
|
||||
private:
|
||||
void lock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection(&m_sync);
|
||||
#elif defined(__rtems__)
|
||||
rtems_semaphore_obtain(m_sync, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
||||
#elif defined(_POSIX_THREADS)
|
||||
::pthread_mutex_lock(&m_sync);
|
||||
#endif
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection(&m_sync);
|
||||
#elif defined(__rtems__)
|
||||
rtems_semaphore_release(m_sync);
|
||||
#elif defined(_POSIX_THREADS)
|
||||
::pthread_mutex_unlock(&m_sync);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
CRITICAL_SECTION m_sync;
|
||||
#elif defined(__rtems__)
|
||||
rtems_id m_sync;
|
||||
#elif defined(_POSIX_THREADS)
|
||||
pthread_mutex_t m_sync;
|
||||
#endif
|
||||
};
|
||||
|
||||
class PLOG_LINKAGE_HIDDEN MutexLock : NonCopyable
|
||||
{
|
||||
public:
|
||||
MutexLock(Mutex& mutex) : m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
~MutexLock()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex& m_mutex;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
#ifdef _WIN32
|
||||
class Singleton : NonCopyable
|
||||
#else
|
||||
class PLOG_LINKAGE Singleton : NonCopyable
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#if (defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 8) && !defined(__BORLANDC__)
|
||||
// This constructor is called before the `T` object is fully constructed, and
|
||||
// pointers are not dereferenced anyway, so UBSan shouldn't check vptrs.
|
||||
__attribute__((no_sanitize("vptr")))
|
||||
#endif
|
||||
Singleton()
|
||||
{
|
||||
assert(!m_instance);
|
||||
m_instance = static_cast<T*>(this);
|
||||
}
|
||||
|
||||
~Singleton()
|
||||
{
|
||||
assert(m_instance);
|
||||
m_instance = 0;
|
||||
}
|
||||
|
||||
static T* getInstance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
private:
|
||||
static T* m_instance;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
T* Singleton<T>::m_instance = NULL;
|
||||
}
|
||||
}
|
175
include/plog/WinApi.h
Normal file
175
include/plog/WinApi.h
Normal file
|
@ -0,0 +1,175 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// These windows structs must be in a global namespace
|
||||
struct HKEY__;
|
||||
struct _SECURITY_ATTRIBUTES;
|
||||
struct _CONSOLE_SCREEN_BUFFER_INFO;
|
||||
struct _RTL_CRITICAL_SECTION;
|
||||
|
||||
namespace plog
|
||||
{
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned int UINT;
|
||||
typedef int BOOL;
|
||||
typedef long LSTATUS;
|
||||
typedef char* LPSTR;
|
||||
typedef wchar_t* LPWSTR;
|
||||
typedef const char* LPCSTR;
|
||||
typedef const wchar_t* LPCWSTR;
|
||||
typedef void* HANDLE;
|
||||
typedef HKEY__* HKEY;
|
||||
typedef size_t ULONG_PTR;
|
||||
|
||||
struct CRITICAL_SECTION
|
||||
{
|
||||
void* DebugInfo;
|
||||
long LockCount;
|
||||
long RecursionCount;
|
||||
HANDLE OwningThread;
|
||||
HANDLE LockSemaphore;
|
||||
ULONG_PTR SpinCount;
|
||||
};
|
||||
|
||||
struct COORD
|
||||
{
|
||||
short X;
|
||||
short Y;
|
||||
};
|
||||
|
||||
struct SMALL_RECT
|
||||
{
|
||||
short Left;
|
||||
short Top;
|
||||
short Right;
|
||||
short Bottom;
|
||||
};
|
||||
|
||||
struct CONSOLE_SCREEN_BUFFER_INFO
|
||||
{
|
||||
COORD dwSize;
|
||||
COORD dwCursorPosition;
|
||||
WORD wAttributes;
|
||||
SMALL_RECT srWindow;
|
||||
COORD dwMaximumWindowSize;
|
||||
};
|
||||
|
||||
namespace codePage
|
||||
{
|
||||
const UINT kActive = 0;
|
||||
const UINT kUTF8 = 65001;
|
||||
#if PLOG_CHAR_IS_UTF8
|
||||
const UINT kChar = kUTF8;
|
||||
#else
|
||||
const UINT kChar = kActive;
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace eventLog
|
||||
{
|
||||
const WORD kErrorType = 0x0001;
|
||||
const WORD kWarningType = 0x0002;
|
||||
const WORD kInformationType = 0x0004;
|
||||
}
|
||||
|
||||
namespace hkey
|
||||
{
|
||||
const HKEY kLocalMachine = reinterpret_cast<HKEY>(static_cast<ULONG_PTR>(0x80000002));
|
||||
}
|
||||
|
||||
namespace regSam
|
||||
{
|
||||
const DWORD kQueryValue = 0x0001;
|
||||
const DWORD kSetValue = 0x0002;
|
||||
}
|
||||
|
||||
namespace regType
|
||||
{
|
||||
const DWORD kExpandSz = 2;
|
||||
const DWORD kDword = 4;
|
||||
}
|
||||
|
||||
namespace stdHandle
|
||||
{
|
||||
const DWORD kOutput = static_cast<DWORD>(-11);
|
||||
const DWORD kErrorOutput = static_cast<DWORD>(-12);
|
||||
}
|
||||
|
||||
namespace foreground
|
||||
{
|
||||
const WORD kBlue = 0x0001;
|
||||
const WORD kGreen = 0x0002;
|
||||
const WORD kRed = 0x0004;
|
||||
const WORD kIntensity = 0x0008;
|
||||
}
|
||||
|
||||
namespace background
|
||||
{
|
||||
const WORD kBlue = 0x0010;
|
||||
const WORD kGreen = 0x0020;
|
||||
const WORD kRed = 0x0040;
|
||||
const WORD kIntensity = 0x0080;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar);
|
||||
__declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, BOOL* lpUsedDefaultChar);
|
||||
|
||||
__declspec(dllimport) DWORD __stdcall GetCurrentThreadId();
|
||||
|
||||
__declspec(dllimport) BOOL __stdcall MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName);
|
||||
|
||||
__declspec(dllimport) void __stdcall InitializeCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection);
|
||||
__declspec(dllimport) void __stdcall EnterCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection);
|
||||
__declspec(dllimport) void __stdcall LeaveCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection);
|
||||
__declspec(dllimport) void __stdcall DeleteCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection);
|
||||
|
||||
__declspec(dllimport) HANDLE __stdcall RegisterEventSourceW(LPCWSTR lpUNCServerName, LPCWSTR lpSourceName);
|
||||
__declspec(dllimport) BOOL __stdcall DeregisterEventSource(HANDLE hEventLog);
|
||||
__declspec(dllimport) BOOL __stdcall ReportEventW(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, void* lpUserSid, WORD wNumStrings, DWORD dwDataSize, LPCWSTR* lpStrings, void* lpRawData);
|
||||
|
||||
__declspec(dllimport) LSTATUS __stdcall RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, DWORD samDesired, _SECURITY_ATTRIBUTES* lpSecurityAttributes, HKEY* phkResult, DWORD* lpdwDisposition);
|
||||
__declspec(dllimport) LSTATUS __stdcall RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE* lpData, DWORD cbData);
|
||||
__declspec(dllimport) LSTATUS __stdcall RegCloseKey(HKEY hKey);
|
||||
__declspec(dllimport) LSTATUS __stdcall RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult);
|
||||
__declspec(dllimport) LSTATUS __stdcall RegDeleteKeyW(HKEY hKey, LPCWSTR lpSubKey);
|
||||
|
||||
__declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle);
|
||||
|
||||
__declspec(dllimport) BOOL __stdcall WriteConsoleW(HANDLE hConsoleOutput, const void* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD* lpNumberOfCharsWritten, void* lpReserved);
|
||||
__declspec(dllimport) BOOL __stdcall GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, _CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo);
|
||||
__declspec(dllimport) BOOL __stdcall SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes);
|
||||
|
||||
__declspec(dllimport) void __stdcall OutputDebugStringW(LPCWSTR lpOutputString);
|
||||
}
|
||||
|
||||
inline void InitializeCriticalSection(CRITICAL_SECTION* criticalSection)
|
||||
{
|
||||
plog::InitializeCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection));
|
||||
}
|
||||
|
||||
inline void EnterCriticalSection(CRITICAL_SECTION* criticalSection)
|
||||
{
|
||||
plog::EnterCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection));
|
||||
}
|
||||
|
||||
inline void LeaveCriticalSection(CRITICAL_SECTION* criticalSection)
|
||||
{
|
||||
plog::LeaveCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection));
|
||||
}
|
||||
|
||||
inline void DeleteCriticalSection(CRITICAL_SECTION* criticalSection)
|
||||
{
|
||||
plog::DeleteCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection));
|
||||
}
|
||||
|
||||
inline BOOL GetConsoleScreenBufferInfo(HANDLE consoleOutput, CONSOLE_SCREEN_BUFFER_INFO* consoleScreenBufferInfo)
|
||||
{
|
||||
return plog::GetConsoleScreenBufferInfo(consoleOutput, reinterpret_cast<_CONSOLE_SCREEN_BUFFER_INFO*>(consoleScreenBufferInfo));
|
||||
}
|
||||
}
|
||||
#endif // _WIN32
|
12
main.cpp
12
main.cpp
|
@ -1,12 +0,0 @@
|
|||
#include <iostream> // for std::cout
|
||||
#include "io.h"
|
||||
|
||||
// Definition of function main()
|
||||
int main()
|
||||
{
|
||||
|
||||
int a{readNumber()};
|
||||
int b{readNumber()};
|
||||
writeAnswer(a + b);
|
||||
return 0;
|
||||
}
|
15
src/main.cpp
Normal file
15
src/main.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include <iostream>
|
||||
#include <plog/Log.h> // Step1: include the headers
|
||||
#include "plog/Initializers/RollingFileInitializer.h"
|
||||
#include "io.h"
|
||||
|
||||
// Definition of function main()
|
||||
int main()
|
||||
{
|
||||
plog::init(plog::debug, "../Hello.txt");
|
||||
PLOGD << "Hello log!";
|
||||
int a{readNumber()};
|
||||
int b{readNumber()};
|
||||
writeAnswer(a + b);
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue