感谢SO社区,我设法创建了一个简单的记录器框架。可以operator<<
使用它,如下所示:
*CLogger::instance(CLogger::ElogLevel::eInfo) << "Application directory " << Enviroment::appDir << "\n";
这是负责收集输入的对象:
template<typename T>
CLogger& operator<<(const T& t)
{
if (((int)m_logLine.logLevel <= (int)m_userDefinedLogLevel) && m_logStarted)
{
ostringstream stream;
stream << t;
m_logLine.logString += stream.str();
if (stream.str().find("\n") != string::npos)
{
push_back(m_logLine);
m_logLine.logString.clear();
}
}
return *this;
}
我的问题是我不知道一个实例(一个方法运行)的输入收集何时完成。正如你在代码中看到的那样,我有点讨厌它,因为这是我唯一能想到的 - 当它找到时,该方法将完成收集用户输入并发送打印数据(push_back(m_logLine);
)一个\n
标志。这就是为什么目前我必须用\n
符号完成每个日志行。我想避免这种情况,因为它很容易被遗忘。
有没有其他方法可以确定流已经完成?我将非常感谢你的帮助!
这是我的完整代码: CLogger.h:
/*
* CLogger.h
*
* Created on: 25 cze 2015
* Author: lukasz
*/
#ifndef CLOGGER_H_
#define CLOGGER_H_
#include <iostream>
#include <deque>
#include <string>
#include <mutex>
#include <condition_variable>
#include <pthread.h>
#include <ostream>
#include <fstream>
#include <sstream>
#include <ctime>
using namespace std;
class CLogger
{
public:
enum class ElogLevel { eNone = 0, eError, eWarning, eInfo, eDebug };
typedef struct
{
string logString;
ElogLevel logLevel;
} logline_t;
static CLogger* instance(ElogLevel ll = ElogLevel::eError);
bool startLog(string fileName, bool verbose);
logline_t pop_front();
void push_back(logline_t s);
void setLogLevel(ElogLevel ll);
template<typename T>
CLogger& operator<<(const T& t)
{
if (((int)m_logLine.logLevel <= (int)m_userDefinedLogLevel) && m_logStarted)
{
ostringstream stream;
stream << t;
m_logLine.logString += stream.str();
if (stream.str().find("\n") != string::npos)
{
push_back(m_logLine);
m_logLine.logString.clear();
}
}
return *this;
}
protected:
virtual void threadLoop();
private:
CLogger() {}; // Private so that it can not be called
CLogger(CLogger const&) {}; // copy constructor is private
CLogger& operator= (CLogger const&) {}; // assignment operator is private
static CLogger* mp_instance;
bool m_logStarted;
logline_t m_logLine;
ElogLevel m_userDefinedLogLevel;
ofstream m_logFileStream;
bool m_verbose;
static void * threadHelper(void* handler)
{
((CLogger*)handler)->threadLoop();
return NULL;
}
deque<logline_t> m_data;
mutex m_mutex;
condition_variable m_cv;
pthread_t thread;
};
#endif /* CLOGGER_H_ */
CLogger.cpp:
/*
* CLogger.cpp
*
* Created on: 25 cze 2015
* Author: lukasz
*/
#include "CLogger.h"
using namespace std;
// Global static pointer used to ensure a single instance of the class.
CLogger* CLogger::mp_instance = NULL;
/** This function is called to create an instance of the class.
Calling the constructor publicly is not allowed. The constructor
is private and is only called by this Instance function.
*/
CLogger* CLogger::instance(ElogLevel ll)
{
//cout << "instance run with logLevel = " << (int)ll << endl;
// Only allow one instance of class to be generated.
if (!mp_instance)
mp_instance = new CLogger;
mp_instance->m_logLine.logLevel = ll;
return mp_instance;
}
bool CLogger::startLog(string fileName, bool verbose)
{
if(remove(fileName.c_str()) != 0)
perror( "Error deleting file" );
m_logFileStream.open(fileName.c_str(), ios::out | ios::app);
if (!m_logFileStream.is_open())
{
cout << "Could not open log file " << fileName << endl;
return false;
}
m_verbose = verbose;
m_logStarted = true;
return (pthread_create(&(thread), NULL, threadHelper, this) == 0);
}
void CLogger::push_back(logline_t s)
{
unique_lock<mutex> ul(m_mutex);
m_data.emplace_back(move(s));
m_cv.notify_all();
}
CLogger::logline_t CLogger::pop_front()
{
unique_lock<mutex> ul(m_mutex);
m_cv.wait(ul, [this]() { return !m_data.empty(); });
logline_t retVal;
retVal.logString = move(m_data.front().logString);
retVal.logLevel = move(m_data.front().logLevel);
m_data.pop_front();
return retVal;
}
void CLogger::setLogLevel(ElogLevel ll)
{
m_userDefinedLogLevel = ll;
}
void CLogger::threadLoop()
{
logline_t logline;
const string logLevelsStrings[] = {"eNone", "eError", "eWarning", "eInfo", "eDebug" };
while(1)
{
logline = pop_front();
uint32_t pos;
if((pos = logline.logString.find('\n')) != string::npos)
logline.logString.erase(pos);
time_t curTime = time(0);
struct tm* now = localtime(&curTime);
m_logFileStream << "[" << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << "]"
<< "[" << logLevelsStrings[(int)logline.logLevel] << "] "
<< logline.logString << endl;
if(m_verbose)
{
cout << "[" << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << "]"
<< "[" << logLevelsStrings[(int)logline.logLevel] << "] "
<< logline.logString << endl;
}
}
}
答案 0 :(得分:3)
我假设CLogger::instance()
返回一个指向静态CLogger
对象的指针。您可以将其更改为返回代理(按值),该代理将在其析构函数中完成。像这样:
struct CLoggerProxy
{
CLogger &logger;
CLoggerProxy(CLogger &logger) : logger(logger) {}
~CLoggerProxy() { logger.finaliseLine(); }
template <class T>
CLoggerProxy& operator<< (const T &t) const
{
logger << t;
return *this;
}
};
CLoggerProxy CLogger::instance()
{
return CLoggerProxy(*theInstanceYourOriginalCodeReturned());
}
答案 1 :(得分:2)
你可以制作map = new UnfoldingMap(this, new Google.GoogleMapProvider());
等价物:
endl
并告诉人们使用它:
struct CLoggerEndl_t { };
static CLoggerEndl_t CLoggerEndl; // or constexpr, in C++11
并为它提供特殊的重载:
*CLogger::instance(CLogger::ElogLevel::eInfo)
<< "Application directory "
<< Enviroment::appDir
<< CLoggerEndl;