sylar
发布时间 2023-09-08 18:05:55作者: yytarget
# 日志模块
* 这里要实现通过g_logger打印一条INFO级别的消息。那么,首先判断INFO级别是否高于g_logger本身的日志级别(这里的设计与原版sylar相反,数字越小,优先级越高),如果不高于,那if语句执行不到,这条日志也不会打印,否则,临时构造一个LogEventWrap对象,传入日志器g_logger,以及现场构造的日志事件。通过LogEventWrap的getLogEvent()方法拿到日志事件,再用日志事件的流式日志消息成员输出日志消息。由于LogEventWrap是在if语句内部构建的,一旦if语句执行结束,LogEventWrap对象就会析构,日志事件也就会被g_logger进行输出,这个设计可以说是非常巧妙。
## 待补充与完善
* 目前来看,sylar日志模块已经实现了一个完整的日志框架,并且配合后面的配置模块,可用性很高,待补充与完善的地方主要存在于LogAppender,目前只提供了输出到终端与输出到文件两类LogAppender,但从实际项目来看,以下几种类型的LogAppender都是非常有必要的:
1. Rolling File Appender,循环覆盖写文件
2. Rolling Memory Appender,循环覆盖写内存缓冲区
3. 支持日志文件按大小分片或是按日期分片
4. 支持网络日志服务器,比如syslog
# 配置模块
* 简单来说,约定优于配置的背景条件是,一般来说,程序所依赖的配置项都有一个公认的默认值,也就是所谓的约定。这点有可许多可以参考的例子,比如对于一个http网络服务器,服务端口通常都是80端口,对于配置文件夹路径,一般都是conf文件夹,对于数据库目录,一般都是db或data文件夹。对于这些具有公认约定的配置,就不需要麻烦程序员在程序跑起来后再一项一项地指定了,而是可以初始时就将配置项设置成对应的值。这样,程序员就可以只修改那些约定之外的配置项,然后以最小的代价让程序跑起来。
* 约定优于配置的方式可以减少程序员做决定的数量,获得简单的好处,同时兼顾灵活性。
* 在代码上,约定优于配置的思路体现为所有的配置项在定义时都带一个的默认值,以下是一个sylar配置项的示例,这是一个int类型的配置项,名称为tcp.connect.timeout,初始值为5000。
```
static sylar::ConfigVar::ptr g_tcp_connect_timeout = sylar::Config::Lookup("tcp.connect.timeout", 5000, "tcp connect timeout");
```
* sylar的配置模块实现的一大难点是类型转换类(仿函数)的偏特化实现。对于每种类型的配置,在对应的ConfigVar模板类实例化时都要提供其FromStr和ToStr两个仿函数,用于实现该类型和YAML字符串的相互转换。由于配置项的类型众多,包括全部的基本数据类型(int, float, double, string等),以及vector/list/set/unordered_set/map/unordered_map这几个复杂数据类型,还有用户自定义的类型。为了简化代码编写,sylar从一个基本类型的转换类开始,特化出了剩余类型的转换类,这个基本类型如下:
```
/**
* @brief 类型转换模板类(F 源类型, T 目标类型)
*/
template
class LexicalCast {
public:
/**
* @brief 类型转换
* @param[in] v 源类型值
* @return 返回v转换后的目标类型
* @exception 当类型不可转换时抛出异常
*/
T operator()(const F &v) {
return boost::lexical_cast(v);
}
};
```
* 这里的LexicalCast类是一个仿函数,它支持`LexicalCast()(const F &v)`调用,可将传入的F类型的参数v进行转换,并返回T类型的结果。实际的转换语句是`boost::lexical_cast(v)`。但是,受限于boost::lexical_cast, LexicalCast当前只能实现基本数据类型和std::string的相互转换,不能实现复杂类型的转换,下面的代码可用于演示当前LexicalCast的功能:
```
std::string str1 = LexicalCast()(123); // ok, str1等于"123"
int int1 = LexicalCast()("123"); // ok, int1等于123
std::string str2 = LexicalCast()(3.14); // ok,str2等于"3.14"
float float2 = LexicalCast()("3.14"); // ok,float2等于3.14
vector v = LexicalCast>()(...); // 错误,LexicalCast目前还不支持实例化T类型为vector的模板参数
std::string s = LexicalCast, std::string>()(...); // 错误,同上
```
* 为了实现YAML字符串和vector/list/set/unordered_set/map/unordered_map的相互转换,就要对每个类型都进行特化,分别实现其转换类,下面是YAML字符串和vector的相互转换实现:
```
/**
* @brief 类型转换模板类片特化(YAML String 转换成 std::vector)
*/
template
class LexicalCast> {
public:
std::vector operator()(const std::string &v) {
YAML::Node node = YAML::Load(v);
typename std::vector vec;
std::stringstream ss;
for (size_t i = 0; i < node.size(); ++i) {
ss.str("");
ss << node[i];
vec.push_back(LexicalCast()(ss.str()));
}
return vec;
}
};
/**
* @brief 类型转换模板类片特化(std::vector 转换成 YAML String)
*/
template
class LexicalCast, std::string> {
public:
std::string operator()(const std::vector &v) {
YAML::Node node(YAML::NodeType::Sequence);
for (auto &i : v) {
node.push_back(YAML::Load(LexicalCast()(i)));
}
std::stringstream ss;
ss << node;
return ss.str();
}
};
```
* 上面分别实现了LexicalCast>和LexicalCast, std::string>,其中在转换单个的数组元素时,再次用到了LexicalCast和LexicalCast,如果这里T是基本数据类型,那么就可以用最开始的基本类型的转换类进行模板实例化并完成转换了,下面是针对vector和YAML字符串相互转换的示例:
```
std::vector v = LexicalCast>()("[1, 2, 3]"); // ok, v等于[1, 2, 3]
std::string s = LexicalCast, std::string>()(std::vector{1, 2, 3}); // ok,s等于YAML格式的数组[1,2,3],如下:
// - 1
// - 2
// - 3
```
* 另外,由于这里的模板实例化是可以嵌套的,由vector和vector组合出来的全部类型都可以顺利地实现和YAML的转化,以下是一个二维数组的示例:
```
std::vector> vv = LexicalCast>>()("[[1,2,3],[4,5,6]]");
std::string ss = LexicalCast>, std::string>()(vv);
```
其他复杂类型的偏特化与vector类型,参考源码理解即可。
* 每实现一个新类型的转换,那这个类型和之前已实现的类型组合出的数据类型也可以顺利实现转换,比如vector, set,set