code : https://github.com/EomTaeWook/Cpp-Util/tree/master/Util/Logger
C#처럼 Parallel 환경 테스트가 안되어서 싱글쓰레드 테스트
Default Message 입력된 시간으로 정렬됨.
2번째 인자로 시간을 넘겨주면 시간 순대로 정렬
사용예제
1000000개 11초 걸림
int count = 0;
auto now = std::chrono::system_clock::now();
try
{
while (count++ < 5)
{
for (int i = 0; i < 200000; i++)
{
auto now = std::chrono::system_clock::now();
auto second = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
Util::Logger::FileLogger::Instance()->Write(L"한글 테스트" + std::to_wstring(second.count()), now);
}
}
}
catch (const std::exception ex)
{
printf("%s", ex.what());
}
catch (...)
{
}
Util::Logger::FileLogger::Instance()->~FileLogger();
std::chrono::duration<double> span = std::chrono::system_clock::now() - now;
printf("%lf", span);
FileLogger.h
#pragma once
#include "NS.h"
#include "EnumLogger.h"
#include "../Threading/CriticalSection.h"
#include "LogMessage.h"
#include "../Common/Singleton.h"
#include "../Common/Finally.h"
#include "../Collections/DoublePriorityQueue.h"
#include "../Threading/Thread.h"
#include <fstream>
#include <condition_variable>
#include <mutex>
#include <codecvt>
NS_LOGGER_BEGIN
class FileLogger : public Common::Singleton<FileLogger>
{
public :
FileLogger();
~FileLogger();
private:
LoggerPeriod _period;
std::string _path;
std::string _moduleName;
Threading::CriticalSection _appand;
Collections::DoublePriorityQueue<LogMessage, LogMessage::Compare> _queue;
std::wfstream _fs;
std::function<void()> _periodCompare;
Threading::Thread _thread;
bool _isStart, _doWork;
std::mutex _write;
std::condition_variable _trigger;
tm _time;
public:
void Init(LoggerPeriod period = LoggerPeriod::Infinitely, const std::string& moduleName="", const std::string& path = "");
void Write(const std::wstring& message, const std::chrono::system_clock::time_point& timeStamp = std::chrono::system_clock::now());
private:
void WriteMessage(const LogMessage& message);
void Invoke();
void DayCompare();
void HourCompare();
void CreateLogFile();
};
NS_LOGGER_END
FileLogger.cpp
#include "FileLogger.h"
#include <ctime>
#include "../Threading/IOCPThreadPool.h"
#include "../Common/Trace.h"
NS_LOGGER_BEGIN
FileLogger::FileLogger() : _periodCompare(nullptr),
_thread(std::bind(&FileLogger::Invoke, this), nullptr),
_isStart(false)
{
}
FileLogger::~FileLogger()
{
_isStart = false;
_trigger.notify_all();
_thread.Join();
if(_fs.is_open())
_fs.close();
}
void FileLogger::Init(LoggerPeriod period, const std::string& moduleName, const std::string& path)
{
if (_isStart)
return;
_period = period;
_path = path;
_moduleName = moduleName;
char moduleFileName[MAX_PATH];
::GetModuleFileName(NULL, moduleFileName, MAX_PATH);
if (_path.size() == 0)
{
_path.append(moduleFileName);
_path = _path.substr(0, _path.find_last_of("\\")).append("\\Log");
}
if (_moduleName.size() == 0)
{
_moduleName.append(moduleFileName);
_moduleName = _moduleName.substr(_moduleName.find_last_of("\\") + 1, _moduleName.find_last_of(".") - _moduleName.find_last_of("\\") - 1);
}
DWORD attribs = ::GetFileAttributesA(_path.c_str());
if (attribs == INVALID_FILE_ATTRIBUTES)
::CreateDirectory(_path.c_str(), NULL);
switch (_period)
{
case Util::Logger::LoggerPeriod::Hour:
_periodCompare = std::bind(&FileLogger::HourCompare, this);
break;
case Util::Logger::LoggerPeriod::Day:
_periodCompare = std::bind(&FileLogger::DayCompare, this);
break;
}
_fs.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t, 0x10ffff, static_cast<std::codecvt_mode>(std::generate_header | std::consume_header)>));
_fs.sync_with_stdio(false);
CreateLogFile();
_isStart = true;
_thread.Start();
}
void FileLogger::Write(const std::wstring& message, const std::chrono::system_clock::time_point& timeStamp)
{
if (!_fs.is_open())
throw std::exception("Logger Not Initialization");
auto finally = Common::Finally(std::bind(&Threading::CriticalSection::LeaveCriticalSection, &_appand));
_appand.EnterCriticalSection();
_queue.Push(LogMessage(message, timeStamp));
if (!_doWork)
_trigger.notify_all();
}
void FileLogger::WriteMessage (const LogMessage& message)
{
auto now = std::chrono::system_clock::now();
auto since = now.time_since_epoch();
auto second = std::chrono::duration_cast<std::chrono::seconds>(since);
since -= second;
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(since);
auto time = std::chrono::system_clock::to_time_t(now);
tm timeInfo;
::localtime_s(&timeInfo, &time);
wchar_t timeBuff[20];
std::wcsftime(timeBuff, 20, L"%Y-%m-%d %H:%M:%S", &timeInfo);
std::wstring ms = std::to_wstring(milliseconds.count());
auto format = std::wstring(L"[")
.append(timeBuff)
.append(L"." + std::wstring().insert(0, 3 - ms.size(), '0') + ms.c_str())
.append(L"]")
.append(L" " + ((LogMessage)message).GetLogMessage());
#if _DEBUG
Common::Trace::WriteLine(format.c_str());
#endif
_fs.write(format.c_str(), format.size());
_fs.put(L'\r').put(L'\n').flush();
}
void FileLogger::Invoke()
{
std::unique_lock<std::mutex> lock(_write);
while (_isStart)
{
_doWork = false;
_trigger.wait(lock);
_doWork = true;
while (_queue.AppendCount() > 0)
{
_appand.EnterCriticalSection();
_queue.Swap();
_appand.LeaveCriticalSection();
while (_queue.ReadCount() > 0)
{
if (_periodCompare != nullptr)
_periodCompare();
WriteMessage(_queue.Pop());
}
}
}
}
void FileLogger::DayCompare()
{
auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
tm timeInfo;
::localtime_s(&timeInfo, &time);
if (timeInfo.tm_mday != _time.tm_mday)
{
_fs.close();
CreateLogFile();
}
}
void FileLogger::HourCompare()
{
auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
tm timeInfo;
::localtime_s(&timeInfo, &time);
if (timeInfo.tm_hour != _time.tm_hour)
{
_fs.close();
CreateLogFile();
}
}
void FileLogger::CreateLogFile()
{
auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
::localtime_s(&_time, &time);
std::string fileName;
fileName.resize(20);
switch (_period)
{
case LoggerPeriod::Infinitely:
fileName = "Log";
break;
case LoggerPeriod::Day:
std::strftime(&fileName.front(), fileName.size(), "%Y-%m-%d", &_time);
break;
case LoggerPeriod::Hour:
std::strftime(&fileName.front(), fileName.size(), "%Y-%m-%d-%H", &_time);
break;
default:
fileName = "";
break;
}
fileName = fileName.substr(0, fileName.find('\0'));
if (fileName.size() == 0)
throw std::exception("LoggerPeriod Not Initialization");
fileName.append(".log");
_fs.open(_path + "\\"+ _moduleName + " " + fileName, std::ios::app | std::ios::binary);
}
NS_LOGGER_END
'개발관련 > C&C++' 카테고리의 다른 글
반복자(iterator)가 포함된 Queue (0) | 2018.09.08 |
---|---|
IOCP Socket Client 구현 (0) | 2018.05.31 |
HttpClient (0) | 2018.05.25 |
MSMQ(MS MessageQueue) (0) | 2018.05.15 |
Functional 이용한 델리게이트 (0) | 2018.04.26 |