这个周面试一家公司,面谈中要求提供代码片段,但招聘启事上没有注明。自己也囧了下,发现自己工作职位,地点一直变动,最近一年转作程序员,但课余时间还没有自己写一些小程序。当即觉得是该开始这种小积累的时候了。不积跬步,无以至千里。
自己之前在某游戏公司项目中,负责写一个服务器的网络通信代码,当时基于iocp模型下编写的,也是那时开始学习网络编程。印象深刻的是当时调试时记录一些线程内的打印消息时,因为多线程的关系比较麻烦,原来的代码中log不好用,一直是我比较纠结的地方(不是重点却让你很不爽哈)。收拾当时的抑郁心情,把自己希望这个小log能够实现以下几个小功能:
1 支持多线程下的log记录。
2 支持向多个console或file内写入log
3 log可模块化。主项目中的不同模块都可以有自己的log模块。
4 可以方面的设置log开关。可用配置文件或使用编译条件开打开。
5 追踪函数调用。(目前版本暂不支持)
因为最近一直在看模板方面的东西,特别是loki库。就把这个logsystem作为模板设计的一个尝试。该程序参考了《游戏编程精粹3》中的基于策略的logsystem(117页)。
希望能够如下使用:
unsigned int WINAPI threadfunc1_in_module1(void* i)
{
for(int i = 0; i < 500; i++)
logx(1)<< i << "\tthreadfunc1_in_module1 " << " 11\n";
//logx(1).print("%d\tthreadfunc1_in_module1 11\n", i);
endTime = GetCurrentTime();
return 0;
}
logx(1)即为获得模块1的的全局logger。重载流操作符,并可级联使用。logx最终把字符输入文件或console中。设置不打开log开关时,该语句将跳过后边的<<输入语句,减少负担。基本的想法如此。logx为一个宏,而不是一个函数,防止了不必要的参数压栈。
1 需要一个队列。工作线程中输入的log都发送到这个队列中。然后单独用一个线程读取该队列完成IO操作。这样避免了在工作线程中进行慢速的IO操作。
2 因为需要级联输入,意味着并不是每次调用<<时,就表示使用者已经结束log输入。基于这个考虑,我们需要一个缓冲区,来储存所有级联调用输入的字符,而每次writelog前,判断缓冲区是否为空,不为空则将内容发送出去,再把缓冲区清空。
队列中应该直接保存字符串指针的话,不容易支持2,对于每一个log字符串,我们将其保存在一个LogItem里进行处理,队列保存的是LogItem。如此看上去会更舒服和方便。
LogItem还可以直接作为上层结构的缓冲区,这样就上层结构就不需要特设一个缓冲类了。当然上层结构也可以接受一个模板参数作为缓冲区的策略。
LogItem因为与数据的紧密联系,由它支持对数据的输入转换是最自然的。因此LogItem要支持对字符串,整型,浮点型等的输入输出,上层构件不需要关心这些细节了。
LogItem的功能结构:
1 显然需要提供一个储存字符串的buffer。
2 提供对标准输出和文件输出的支持。
3 因为上层结构会操纵它,重载操作符=也是方便的。
4 重载<<操作符支持泛型输入。
LogItem作为上层结构的一个策略,可以自己定制然后作为策略传入上层结构。
对于LogItem的字符buffer,需要考虑其内存是在运行期或编译期分配。我个人通过传入一个size_t的模板参数在编译期分配好该buffer,一般项目中每次log的字符长度基本自己都有数。若是使用运行期分配,建议使用一个好的内存池,推荐Loki中的SmallObject。
#ifndef __LogItem_H__
#define __LogItem_H__
#include "stdafx.h"
using namespace std;//为示例方便,直接使用std名字空间
template<size_t ItemSize_ = 128>
class LogItem
{
public:
typedef LogItem<ItemSize_> ItemType;
static const size_t ItemSize = ItemSize_;
private:
char m_logStr[ItemSize_]; //不支持动态大小
size_t m_len;
public:
explicit ItemType():m_len(0) { init(); }
explicit ItemType(ItemType& item)
{
assert(m_logStr && item.m_logStr);
assert(m_logStr != & item);
memcpy(m_logStr, item.m_logStr, m_len + 1);
m_len = item.m_len;
}
public:
void init() {
reset();
}
void reset() {
m_len = 0;
}
bool empty() {
return !m_len;
}
ItemType& operator << (const char* str)
{
size_t len = strlen(str);
//暂不支持动态分配
assert(len < ItemSize_ - m_len);
memcpy( m_logStr + m_len, str, len );
m_len += len;
m_logStr[m_len] = '\0';
return *this;
}
ItemType& operator << (const unsigned char* str)
{
size_t len = strlen(reinterpret_cast<const char*>(str));
//暂不支持动态大小
assert(len < ItemSize_ - m_len);
memcpy( m_logStr + m_len, str, len );
m_len += len;
m_logStr[m_len] = '\0';
return *this;
}
ItemType& operator << (const char& c)
{
assert(1 < ItemSize_ - m_len);
//m_len += len;
m_logStr[m_len] = c;
m_len += 1;
m_logStr[m_len] = '\0';
return *this;
}
ItemType& operator << (const int& iValue)
{
char str[24];
//_itoa(iValue, str, 10);
sprintf_s(str, 24, "%d", iValue);
size_t len = strlen(str);
//暂不支持动态分配
assert(len < ItemSize_ - m_len);
memcpy( m_logStr + m_len, str, len );
m_len += len;
m_logStr[m_len] = '\0';
return *this;
}
ItemType& operator << (const unsigned int& iValue)
{
char str[24];
//_itoa(iValue, str, 10);
sprintf_s(str, 24, "%u", iValue);
size_t len = strlen(str);
//暂不支持动态分配
assert(len < ItemSize_ - m_len);
memcpy( m_logStr + m_len, str, len );
m_len += len;
m_logStr[m_len] = '\0';
return *this;
}
ItemType& operator << (const bool& iValue)
{
return (*this) << static_cast<int>(iValue);
}
ItemType& operator << (const short& iValue)
{
return (*this) << static_cast<int>(iValue);
}
ItemType& operator << (const unsigned short& iValue)
{
return (*this) << static_cast<short>(iValue);
}
ItemType& operator << (const long& lValue)
{
char str[24];
sprintf_s(str, 24, "%ld", lValue);
size_t len = strlen(str);
//暂不支持动态分配
assert(len < ItemSize_ - m_len);
memcpy( m_logStr + m_len, str, len );
m_len += len;
m_logStr[m_len] = '\0';
return *this;
}
ItemType& operator << (const unsigned long& lValue)
{
char str[24];
sprintf_s(str, 24, "%lu", lValue);
size_t len = strlen(str);
//暂不支持动态分配
assert(len < ItemSize_ - m_len);
memcpy( m_logStr + m_len, str, len );
m_len += len;
m_logStr[m_len] = '\0';
return *this;
}
ItemType& operator << (const double& dValue)
{
char str[32];
sprintf_s(str, 32, "%f", dValue);
size_t len = strlen(str);
//暂不支持动态分配
assert(len < ItemSize_ - m_len);
memcpy( m_logStr + m_len, str, len );
m_len += len;
m_logStr[m_len] = '\0';
return *this;
}
ItemType& operator << (const float& fValue)
{
char str[32];
sprintf_s(str, 32, "%f", fValue);
size_t len = strlen(str);
//暂不支持动态分配
assert(len < ItemSize_ - m_len);
memcpy( m_logStr + m_len, str, len );
m_len += len;
m_logStr[m_len] = '\0';
return *this;
}
const ItemType& operator = (ItemType& item)
{
assert(this != &item);
assert(m_logStr && item.m_logStr);
memcpy(m_logStr, item.m_logStr, item.m_len + 1);
m_len = item.m_len;
return *this;
}
//标准输出
friend ostream& operator << (ostream& os, ItemType& item)
{
os << item.m_logStr;
return os;
}
//文件输出
friend fstream& operator << (fstream& fs, ItemType& item)
{
fs << item.m_logStr;
return fs;
}
};
#endif
上层结构于是可以通过模板参数定义一个大小为Size的LogItem:
typedef LogItem<Size = 128> ItemType;
一个Log System的基础构件如上。接下来需要实现LogQueue,作为item的集合,方便LogSystem对item的管理。
整个LogSystem基本的结构为:
LogManager -> LogQueue -> LogItem