1. 配置参数传递和调用测试
调用如下:
传入参数名称name:"system.port" 参数对应的值:(int)8080 参数对应具体描述:"system port"
ConfigVar<int>::ptr q_int_value_config =Config::LookUp("system.port", (int)8080, "system port");int main(){KIT_LOG_INFO(KIT_LOG_ROOT()) << q_int_value_config->getValue();KIT_LOG_INFO(KIT_LOG_ROOT()) << q_int_value_config->toString();return 0;}
BUG:错误包含config.h文件导致宏定义失效
现象描述:在Log.h头文件中错误包含了config.h,config.h头文件中使用了一个由Log.h定义的宏函数KIT_LOG_ROOT(),由于错误包含导致config.h文件先被编译,而所需的宏函数还在后面没被编译导致了失效。

在#include "config.h"后:
2. 使用yaml-cpp库读取 *.yaml文件
注意点:需要在CMakeLists 库依赖项上加上相关依赖。
以读取logs.yaml为例
调用如下:
void test_yaml(){YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/logs.yaml");KIT_LOG_INFO(KIT_LOG_ROOT()) << root;}int main(){test_yaml();return 0;}
2.1 BUG: 读取解析 logs.yaml文件失败
现象描述:加载解析失败,似乎是.yaml文件格式错误的问题。
避免:编写校正网站:https://www.bejson.com/validators/yaml_editor/
坑点:采用YAML格式,这种格式非常简单,使用
解决bug后运行结果如下:
2.2 打印*.yaml文件的分层关系
原理:采用递归打印的方式,根据YAML::Node的不同类型NULL、Map、Scalar、Sequence进行一个分类处理。注意:list对象没有定义相关的迭代器类型,不能使用iterator来进行遍历。map对象可以使用iterator迭代器。
调用如下:(以读取
logs.yaml为例) ```cpp void show_yaml(const YAML::Node& node, int level) {//打印 Scalar 标量 if(node.IsScalar()) {
std:: cout << std::string(level * 4, '-') << node.Scalar() << " - " << node.Type() << " - "<< level << std::endl;
} else if(node.IsNull()) {
std:: cout << std::string(level * 4, '-') << "NULL" << " - " << node.Type() << " - "<< level << std::endl;
} if(node.IsMap()) {
//由迭代器打印hash对象的值for(auto it = node.begin(); it != node.end();it++){std::cout << std::string(level * 4, '-') << it->first << " - " << it->second.Type() << " - " << level<< std::endl;show_yaml(it->second, level + 1);}
} else if(node.IsSequence()) {
//由下标索引遍历list中的Node对象for(size_t i = 0;i < node.size();i++){std::cout << std::string(level * 4, '-') << "list对象索引: " << node[i].Type() <<" - "<< i << " - "<< level << std::endl;show_yaml(node[i], level + 1);}
} }
void test_yaml() { YAML::Node root = YAML::LoadFile(“/home/nmoek/kit_server_project/tests/logs.yaml”);
show_yaml(root, 0);
}
int main() {
test_yaml();return 0;
}
- **运行结果如下:**格式说明:** key** - ** Node对象类型(hash、list、scalar、null) **- **层级**<br /><br />与配置开发(二)分析预测的结果相同。<a name="XpLo6"></a>## 2.3 对*.yaml文件中的基本数据类型进行一个序列化<a name="iWDgQ"></a>#### 2.3.1 调用如下:```cppusing namespace std;using namespace kit_server;//假设原本配置了一个 int 型数据ConfigVar<int>::ptr q_int_value_config =Config::LookUp("system.port", (int)8080, "system port");//假设原本配置了一个 float 型数据ConfigVar<float>::ptr q_float_value_config =Config::LookUp("system.float", (float)63.6, "system float");void test_config(){KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_int_value_config->getValue();KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_float_value_config->getValue();//从.yaml文件导入配置信息YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/config.yaml");//将YAML::Node中的层级结构进行序列化 并且自动完成配置更迭Config::LoadFromYaml(root);KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_int_value_config->getValue();KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_float_value_config->getValue();}int main(){test_config();return 0;}
2.3.2 config.yaml文件如下:
logs:- name: rootlevel: infoformatter: "%d%T%m%n"appender:- type: FileLogAppenderpath: log.txt- type: StdOutLogAppendersystem:port: 9999float: 20.36
2.3.3 运行结果如下:


2.3.4 分析:
由于暂时只对普通基本数据类型进行了解析测试,YAML::Node中的Sequence列表对象没有做解析处理,出现了如图的效果。list<> 下标0::对应的元素,是YAML::Node中的匿名对象。红色框柱的部分正是我们所需要看到的结果:
2.4 对.yaml文件中复杂类型进行一个序列化
2.4.1 传入 STL容器 vector类型进行测试
2.4.1.1 调用如下:
//假设原本配置了一个 vector<int> 型数据ConfigVar<std::vector<int> >::ptr q_vec_value_config =Config::LookUp("system.int_vec", std::vector<int>{1, 2}, "system int_vec");void test_config(){auto v = q_vec_value_config->getValue();for(auto &x : v)KIT_LOG_INFO(KIT_LOG_ROOT()) << "before int_vec:" << x ;auto v = q_vvec_value_config->getValue();//从.yaml文件导入配置信息YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/config.yaml");// //将YAML::Node中的层级结构进行序列化 并且自动完成配置更迭Config::LoadFromYaml(root);auto t = q_vec_value_config->getValue();for(auto &x : t)KIT_LOG_INFO(KIT_LOG_ROOT()) << "after int_vec:" << x;}
2.4.1.2 config.taml文件如下:
system:port: 9999float: 20.36int_vec: [10, 20]
2.4.1.3 运行结果如下:
2.4.2 传入 STL容器 vector >复合类型进行测试
2.4.2.1 调用如下:
//假设原本配置了一个 int 型数据ConfigVar<int>::ptr q_int_value_config =Config::LookUp("system.port", (int)8080, "system port");//假设原本配置了一个 float 型数据ConfigVar<float>::ptr q_float_value_config =Config::LookUp("system.float", (float)63.6, "system float");//传入一个vector<vector< >>类型ConfigVar<std::vector<vector<int> > >::ptr q_vvec_value_config =Config::LookUp("system.int_vvec", std::vector<vector<int> >{{1, 2},{3, 4}}, "system int_vvec");void test_config(){KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_int_value_config->getValue();KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_float_value_config->getValue();auto v = q_vvec_value_config->getValue();int index = 0;for(auto &x : v){KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << index++;for(auto &k : x)KIT_LOG_INFO(KIT_LOG_ROOT()) << "before int_vvec:" << k ;}//从.yaml文件导入配置信息YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/config.yaml");//将YAML::Node中的层级结构进行序列化 并且自动完成配置更迭Config::LoadFromYaml(root);KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_int_value_config->getValue();KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_float_value_config->getValue();auto t = q_vvec_value_config->getValue();index = 0;for(auto &x : t){KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << index++;for(auto &k : x)KIT_LOG_INFO(KIT_LOG_ROOT()) << "before int_vvec:" << k ;}}int main(){test_config();return 0;}
2.4.2.2 config.yaml文件如下:
system:port: 9999float: 20.36int_vvec: [[10, 30], [20, 50]]
2.4.2.3 运行结果如下:

2.4.2.4 BUG:传入嵌套复合类型vector >解析不符合预期
原因:对YAML::Node中封装的数据类型理解有误。误把其中封装的Node::Scalar()简单认为是一个返回std::string的方法。
- 问题代码1:

- 问题代码2:

问题代码3:
void Config::LoadFromYaml(const YAML::Node &root){....ConfigVarBase::ptr v = LookUpBase(key);if(v){v->fromString(x.second.Scalar());}.....}
YAML 支持以下几种数据类型:
- 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
- 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
- 纯量(scalars):单个的、不可再分的值。包括:字符串、布尔值、整数、浮点数、Null、时间、日期
注意:如果发现从YAML::Node返回的是非纯量Scalar的话需要手动利用YAML::Node >> std::stringstream -----> .str()转化为std::string类型。否则就会有2.4.2.3的奇怪现象产生。
- 问题代码修改: ```cpp node.Scalar() ———>
std::stringstram ss; ss << node; ss.str();
- **BUG修复输出结果:**[<br />](https://blog.csdn.net/zhao_5352269/article/details/88951863)<a name="nA842"></a>####<a name="X0mvP"></a>####<a name="PP0fT"></a>### 2.4.3 传入STL容器map<>类型进行测试<a name="nvjti"></a>#### 2.4.3.1 调用如下:```cpp//假设原本有 map<char, int> 配置ConfigVar<std::map<char, int> >::ptr q_map_value_config =Config::LookUp("system.map", std::map<char, int>{{'c',1}, {'a',2}, {'b', 3}}, "system map");void test_config(){auto v = q_map_value_config->getValue();for(auto &x : v){KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << x.first << ":" << x.second;}//从.yaml文件导入配置信息YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/config.yaml");//将YAML::Node中的层级结构进行序列化 并且自动完成配置更迭Config::LoadFromYaml(root);auto t = q_map_value_config->getValue();for(auto &x : t){KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << x.first << ":" << x.second;}}int main(){test_config();return 0;}
2.4.3.2 config.yaml文件如下:
system:map:a: 100b: 300c: 500
2.4.3.3 运行结果如下:

2.4.4 传入自定义类型进行测试
2.4.4.1 调用如下:
//假设原本有 Person 类型的配置ConfigVar<Person>::ptr q_preson_config = Config::LookUp("class.person", Person("liu", 18, 0), "class person");void test_class(){KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_preson_config->getValue().toString() << " - \n" << q_preson_config->toString();YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/class.yaml");Config::LoadFromYaml(root);KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_preson_config->getValue().toString() << " - \n" << q_preson_config->toString();}int main(){test_class();return 0;}
2.4.4.2 class.yaml文件如下:
class:person:name: liage: 17sex: true
2.4.4.3 运行结果如下:

2.4.5 传入map<自定义类型>测试
2.4.5.1 调用如下:
//假设已经有 map<string, Person> 类型的配置ConfigVar<std::map<std::string, Person> >::ptr q_map_preson_config = Config::LookUp("class.map", std::map<std::string, Person>{{"p1", {"qian", 19, false}}}, "class map");void test_class(){auto t = q_map_preson_config->getValue();for(auto &x : t)KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << x.first << " - " << x.second.toString();YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/class.yaml");Config::LoadFromYaml(root);auto v = q_map_preson_config->getValue();for(auto &x : v)KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << x.first << " - " << x.second.toString();}int main(){test_class();return 0;}
2.4.5.2 class.yaml文件如下
class:map:person1:name: wangage: 38sex: falseperson2:name: liuage: 26sex: true
2.4.5.3 运行结果如下:

3. 配置系统+日志系统联调
3.1 对日志配置进行修改,并打印修改后的配置项信息
3.1.1 将日志系统的所有信息组合成YAML::Node格式打包输出
3.1.1.1 LogAppder类中增加toYamlString()函数:(分派到具体的子类中实现)

**StdoutLogAppender**类中://打印来自.yaml中appender信息std::string StdoutLogAppender::toYamlString(){YAML::Node node;node["type"] = "StdoutLogAppender";std::stringstream ss;ss << node;return ss.str();}
**FileLogAppender**类中:std::string FileLogAppender::toYamlString(){YAML::Node node;node["type"] = "FileLogAppender";node["file"] = m_filename;std::stringstream ss;ss << node;return ss.str();}
3.1.1.2 Logger类中增加toYamlString()函数:
std::string Logger::toYamlString(){YAML::Node node;node["name"] = m_name;node["level"] = LogLevel::ToString(m_level);node["formatter"] = m_formatter->getPattern();for(auto &x : m_appenders){node["appender"].push_back(YAML::Load(x->toYamlString()));}std::stringstream ss;ss << node;return ss.str();}
3.1.1.3 LogManager类中增加toYamlString()函数:
//打印当前日志配置信息YAML::Node LogManager::toYamlString(){YAML::Node node;for(auto &x : s_loggers){node.push_back(YAML::Load(x.second->toYamlString()));}std::stringstream ss;ss << node;return ss.str();return node;}
3.1.1.4 以上几个toYamlString()函数的依赖关系如图:

3.1.1.5 调用如下:
void test_log(){KIT_LOG_DEBUG(KIT_LOG_ROOT()) << "before: \n" << kit_server::LoggerMgr::GetInstance()->toYamlString();YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/logs.yaml");Config::LoadFromYaml(root);KIT_LOG_DEBUG(KIT_LOG_ROOT()) << "after: \n" << kit_server::LoggerMgr::GetInstance()->toYamlString();}int main(){test_log();return 0;}
3.1.1.6 logs.yaml文件如下:
logs:- name: rootlevel: INFOformatter: "%d%T%m%n"appender:- type: FileLogAppenderfile: log.txt- type: StdoutLogAppender- name: systemlevel: INFOformatter: "%d%T%m%n"appender:- type: FileLogAppenderfile: log.txt- type: StdoutLogAppender
3.1.1.7 运行结果如下:

3.1.1.8 小BUG:忽略了日志级别对日志显示的控制作用
从上面调用的测试代码中可以看到:由于配置项中将日志器的级别全部设置为INFO,而调用打印时候的级别仍然为DEBUG导致无法打印日志内容,即:logger->getLevel = INFO <= DEBUG 不成立。无法触发LogEventWrap的析构函数从而间接调用到Logger::log()函数。
代码修改:(使用标准输出std::cout来验证即可,或者调整相应的输出级别) ```cpp void test_log() { std::cout << “before:\n” << kit_server::LoggerMgr::GetInstance()->toYamlString() << std::endl;
YAML::Node root = YAML::LoadFile(“/home/nmoek/kit_server_project/tests/logs.yaml”);
Config::LoadFromYaml(root);
std::cout << "after:\n" << kit_server::LoggerMgr::GetInstance()->toYamlString() << std::endl;
}
- **运行结果如下:(正确得到结果)** <a name="btqXl"></a>### 3.1.2 将单独的日志器约定并且修改配置,组合成`YAML::Node`打印出来<a name="kErvo"></a>#### 3.1.2.1 调用如下:```cppvoid test_log(){auto t = KIT_LOG_NAME("system");KIT_LOG_DEBUG(t) << "before: \n" << t->toYamlString();YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/logs.yaml");Config::LoadFromYaml(root);auto v = KIT_LOG_NAME("system");KIT_LOG_DEBUG(v) << "before: \n" << v->toYamlString();}int main(){test_log();return 0;}
3.1.2.2 logs.yaml文件如下:
logs:- name: rootlevel: debugformatter: "%d%T%m%n"appender:- type: FileLogAppenderfile: log.txt- type: StdoutLogAppender- name: systemlevel: infoformatter: "[%p]%T%d%T%m%n"appender:- type: FileLogAppenderfile: log.txt- type: StdoutLogAppender
3.1.2.3 运行结果如下:

- 分析:
图中可以看到当命名为"system"的Logger日志器不存在时,默认借用的默认日志器LogManager::m_root的配置,Logger::default_root会将默认日志器的智能指针拷贝使用,因此可以看到日志级别为DEBUG,日志输出格式是默认格式。没有输出LogAppender的信息是因为"system"Logger日志器本身没有配置任何LogAppender。这与配置开发(三)中的改动相吻合。[
](https://blog.csdn.net/zhao_5352269/article/details/88951863)
