1. 配置参数传递和调用测试

调用如下:
传入参数名称name:"system.port" 参数对应的值:(int)8080 参数对应具体描述:"system port"

  1. ConfigVar<int>::ptr q_int_value_config =
  2. Config::LookUp("system.port", (int)8080, "system port");
  3. int main()
  4. {
  5. KIT_LOG_INFO(KIT_LOG_ROOT()) << q_int_value_config->getValue();
  6. KIT_LOG_INFO(KIT_LOG_ROOT()) << q_int_value_config->toString();
  7. return 0;
  8. }

运行结果如下:
image.png

BUG:错误包含config.h文件导致宏定义失效

现象描述:在Log.h头文件中错误包含了config.hconfig.h头文件中使用了一个由Log.h定义的宏函数KIT_LOG_ROOT(),由于错误包含导致config.h文件先被编译,而所需的宏函数还在后面没被编译导致了失效。

image.png
#include "config.h"后:
image.png

2. 使用yaml-cpp库读取 *.yaml文件

注意点:需要在CMakeLists 库依赖项上加上相关依赖。
以读取logs.yaml为例
image.png
调用如下:

  1. void test_yaml()
  2. {
  3. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/logs.yaml");
  4. KIT_LOG_INFO(KIT_LOG_ROOT()) << root;
  5. }
  6. int main()
  7. {
  8. test_yaml();
  9. return 0;
  10. }

2.1 BUG: 读取解析 logs.yaml文件失败

现象描述:加载解析失败,似乎是.yaml文件格式错误的问题。
避免:编写校正网站:https://www.bejson.com/validators/yaml_editor/
image.png
坑点:采用YAML格式,这种格式非常简单,使用:表示,开头使用“空格”作为缩进。需要注意的是,“:”之后有value的话,需要紧跟一个空格,如果key只是表示层级,则无需在“:”后增加空格(比如:logs:后面不需要空格)。缩进的空格数不重要,只要相同层级的元素左对齐即可。一般来讲,按照层级,每行4个空格缩进,第二级则8个空格,依次轮推,顶层则不需要空格缩进。如果格式不正确,将会出现上面的错误。(千万千万不能使用Tab键)

解决bug后运行结果如下:
image.png

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()) {

    1. std:: cout << std::string(level * 4, '-') << node.Scalar() << " - " << node.Type() << " - "<< level << std::endl;

    } else if(node.IsNull()) {

    1. std:: cout << std::string(level * 4, '-') << "NULL" << " - " << node.Type() << " - "<< level << std::endl;

    } if(node.IsMap()) {

    1. //由迭代器打印hash对象的值
    2. for(auto it = node.begin(); it != node.end();it++)
    3. {
    4. std::cout << std::string(level * 4, '-') << it->first << " - " << it->second.Type() << " - " << level<< std::endl;
    5. show_yaml(it->second, level + 1);
    6. }

    } else if(node.IsSequence()) {

    1. //由下标索引遍历list中的Node对象
    2. for(size_t i = 0;i < node.size();i++)
    3. {
    4. std::cout << std::string(level * 4, '-') << "list对象索引: " << node[i].Type() <<" - "<< i << " - "<< level << std::endl;
    5. show_yaml(node[i], level + 1);
    6. }

    } }

void test_yaml() { YAML::Node root = YAML::LoadFile(“/home/nmoek/kit_server_project/tests/logs.yaml”);

  1. show_yaml(root, 0);

}

int main() {

  1. test_yaml();
  2. return 0;

}

  1. - **运行结果如下:**
  2. 格式说明:** key** - ** Node对象类型(hashlistscalarnull) **- **层级**<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25460685/1638029173372-d4e35d5f-b51d-486e-9b02-982b5a6ad48b.png#clientId=u1da21335-4c22-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=516&id=ub179d86a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=563&originWidth=600&originalType=binary&ratio=1&rotation=0&showTitle=false&size=28825&status=done&style=none&taskId=u4fc4d89b-250b-46e0-9a8d-2094a0dbbcf&title=&width=550)<br />与配置开发(二)分析预测的结果相同。
  3. <a name="XpLo6"></a>
  4. ## 2.3 对*.yaml文件中的基本数据类型进行一个序列化
  5. <a name="iWDgQ"></a>
  6. #### 2.3.1 调用如下:
  7. ```cpp
  8. using namespace std;
  9. using namespace kit_server;
  10. //假设原本配置了一个 int 型数据
  11. ConfigVar<int>::ptr q_int_value_config =
  12. Config::LookUp("system.port", (int)8080, "system port");
  13. //假设原本配置了一个 float 型数据
  14. ConfigVar<float>::ptr q_float_value_config =
  15. Config::LookUp("system.float", (float)63.6, "system float");
  16. void test_config()
  17. {
  18. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_int_value_config->getValue();
  19. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_float_value_config->getValue();
  20. //从.yaml文件导入配置信息
  21. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/config.yaml");
  22. //将YAML::Node中的层级结构进行序列化 并且自动完成配置更迭
  23. Config::LoadFromYaml(root);
  24. KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_int_value_config->getValue();
  25. KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_float_value_config->getValue();
  26. }
  27. int main()
  28. {
  29. test_config();
  30. return 0;
  31. }

2.3.2 config.yaml文件如下:

  1. logs:
  2. - name: root
  3. level: info
  4. formatter: "%d%T%m%n"
  5. appender:
  6. - type: FileLogAppender
  7. path: log.txt
  8. - type: StdOutLogAppender
  9. system:
  10. port: 9999
  11. float: 20.36

2.3.3 运行结果如下:

image.png
image.png

2.3.4 分析:

由于暂时只对普通基本数据类型进行了解析测试,YAML::Node中的Sequence列表对象没有做解析处理,出现了如图的效果。list<> 下标0::对应的元素,是YAML::Node中的匿名对象。红色框柱的部分正是我们所需要看到的结果:
配置系统测试 - 图13

2.4 对.yaml文件中复杂类型进行一个序列化

2.4.1 传入 STL容器 vector类型进行测试

2.4.1.1 调用如下:

  1. //假设原本配置了一个 vector<int> 型数据
  2. ConfigVar<std::vector<int> >::ptr q_vec_value_config =
  3. Config::LookUp("system.int_vec", std::vector<int>{1, 2}, "system int_vec");
  4. void test_config()
  5. {
  6. auto v = q_vec_value_config->getValue();
  7. for(auto &x : v)
  8. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before int_vec:" << x ;
  9. auto v = q_vvec_value_config->getValue();
  10. //从.yaml文件导入配置信息
  11. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/config.yaml");
  12. // //将YAML::Node中的层级结构进行序列化 并且自动完成配置更迭
  13. Config::LoadFromYaml(root);
  14. auto t = q_vec_value_config->getValue();
  15. for(auto &x : t)
  16. KIT_LOG_INFO(KIT_LOG_ROOT()) << "after int_vec:" << x;
  17. }

2.4.1.2 config.taml文件如下:

  1. system:
  2. port: 9999
  3. float: 20.36
  4. int_vec: [10, 20]

2.4.1.3 运行结果如下:

image.png

2.4.2 传入 STL容器 vector >复合类型进行测试

2.4.2.1 调用如下:

  1. //假设原本配置了一个 int 型数据
  2. ConfigVar<int>::ptr q_int_value_config =
  3. Config::LookUp("system.port", (int)8080, "system port");
  4. //假设原本配置了一个 float 型数据
  5. ConfigVar<float>::ptr q_float_value_config =
  6. Config::LookUp("system.float", (float)63.6, "system float");
  7. //传入一个vector<vector< >>类型
  8. ConfigVar<std::vector<vector<int> > >::ptr q_vvec_value_config =
  9. Config::LookUp("system.int_vvec", std::vector<vector<int> >{{1, 2},{3, 4}}, "system int_vvec");
  10. void test_config()
  11. {
  12. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_int_value_config->getValue();
  13. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_float_value_config->getValue();
  14. auto v = q_vvec_value_config->getValue();
  15. int index = 0;
  16. for(auto &x : v)
  17. {
  18. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << index++;
  19. for(auto &k : x)
  20. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before int_vvec:" << k ;
  21. }
  22. //从.yaml文件导入配置信息
  23. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/config.yaml");
  24. //将YAML::Node中的层级结构进行序列化 并且自动完成配置更迭
  25. Config::LoadFromYaml(root);
  26. KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_int_value_config->getValue();
  27. KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_float_value_config->getValue();
  28. auto t = q_vvec_value_config->getValue();
  29. index = 0;
  30. for(auto &x : t)
  31. {
  32. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << index++;
  33. for(auto &k : x)
  34. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before int_vvec:" << k ;
  35. }
  36. }
  37. int main()
  38. {
  39. test_config();
  40. return 0;
  41. }

2.4.2.2 config.yaml文件如下:

  1. system:
  2. port: 9999
  3. float: 20.36
  4. int_vvec: [[10, 30], [20, 50]]

2.4.2.3 运行结果如下:
image.png

2.4.2.4 BUG:传入嵌套复合类型vector >解析不符合预期

原因:对YAML::Node中封装的数据类型理解有误。误把其中封装的Node::Scalar()简单认为是一个返回
std::string的方法。

  • 问题代码1:

image.png

  • 问题代码2:

image.png

  • 问题代码3:

    1. void Config::LoadFromYaml(const YAML::Node &root)
    2. {
    3. ....
    4. ConfigVarBase::ptr v = LookUpBase(key);
    5. if(v)
    6. {
    7. v->fromString(x.second.Scalar());
    8. }
    9. .....
    10. }

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();

  1. - **BUG修复输出结果:**[<br />](https://blog.csdn.net/zhao_5352269/article/details/88951863)![image.png](https://cdn.nlark.com/yuque/0/2021/png/25460685/1638180771374-ef679afa-8165-4f91-8d09-7a190575fe64.png#clientId=u18cf2169-7637-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=354&id=u6c8661cd&margin=%5Bobject%20Object%5D&name=image.png&originHeight=354&originWidth=1101&originalType=binary&ratio=1&rotation=0&showTitle=false&size=71534&status=done&style=none&taskId=u218c91ea-b7e0-4d4a-816b-e34a88621ec&title=&width=1101)
  2. <a name="nA842"></a>
  3. ####
  4. <a name="X0mvP"></a>
  5. ####
  6. <a name="PP0fT"></a>
  7. ### 2.4.3 传入STL容器map<>类型进行测试
  8. <a name="nvjti"></a>
  9. #### 2.4.3.1 调用如下:
  10. ```cpp
  11. //假设原本有 map<char, int> 配置
  12. ConfigVar<std::map<char, int> >::ptr q_map_value_config =
  13. Config::LookUp("system.map", std::map<char, int>{{'c',1}, {'a',2}, {'b', 3}}, "system map");
  14. void test_config()
  15. {
  16. auto v = q_map_value_config->getValue();
  17. for(auto &x : v)
  18. {
  19. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << x.first << ":" << x.second;
  20. }
  21. //从.yaml文件导入配置信息
  22. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/config.yaml");
  23. //将YAML::Node中的层级结构进行序列化 并且自动完成配置更迭
  24. Config::LoadFromYaml(root);
  25. auto t = q_map_value_config->getValue();
  26. for(auto &x : t)
  27. {
  28. KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << x.first << ":" << x.second;
  29. }
  30. }
  31. int main()
  32. {
  33. test_config();
  34. return 0;
  35. }

2.4.3.2 config.yaml文件如下:

  1. system:
  2. map:
  3. a: 100
  4. b: 300
  5. c: 500

2.4.3.3 运行结果如下:

image.png

2.4.4 传入自定义类型进行测试

2.4.4.1 调用如下:

  1. //假设原本有 Person 类型的配置
  2. ConfigVar<Person>::ptr q_preson_config = Config::LookUp("class.person", Person("liu", 18, 0), "class person");
  3. void test_class()
  4. {
  5. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << q_preson_config->getValue().toString() << " - \n" << q_preson_config->toString();
  6. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/class.yaml");
  7. Config::LoadFromYaml(root);
  8. KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << q_preson_config->getValue().toString() << " - \n" << q_preson_config->toString();
  9. }
  10. int main()
  11. {
  12. test_class();
  13. return 0;
  14. }

2.4.4.2 class.yaml文件如下:

  1. class:
  2. person:
  3. name: li
  4. age: 17
  5. sex: true

2.4.4.3 运行结果如下:

image.png

2.4.5 传入map<自定义类型>测试

2.4.5.1 调用如下:

  1. //假设已经有 map<string, Person> 类型的配置
  2. 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");
  3. void test_class()
  4. {
  5. auto t = q_map_preson_config->getValue();
  6. for(auto &x : t)
  7. KIT_LOG_INFO(KIT_LOG_ROOT()) << "before:" << x.first << " - " << x.second.toString();
  8. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/class.yaml");
  9. Config::LoadFromYaml(root);
  10. auto v = q_map_preson_config->getValue();
  11. for(auto &x : v)
  12. KIT_LOG_INFO(KIT_LOG_ROOT()) << "after:" << x.first << " - " << x.second.toString();
  13. }
  14. int main()
  15. {
  16. test_class();
  17. return 0;
  18. }

2.4.5.2 class.yaml文件如下

  1. class:
  2. map:
  3. person1:
  4. name: wang
  5. age: 38
  6. sex: false
  7. person2:
  8. name: liu
  9. age: 26
  10. sex: true

2.4.5.3 运行结果如下:

image.png

3. 配置系统+日志系统联调

3.1 对日志配置进行修改,并打印修改后的配置项信息

3.1.1 将日志系统的所有信息组合成YAML::Node格式打包输出

3.1.1.1 LogAppder类中增加toYamlString()函数:(分派到具体的子类中实现)

image.png

  • **StdoutLogAppender**类中:

    1. //打印来自.yaml中appender信息
    2. std::string StdoutLogAppender::toYamlString()
    3. {
    4. YAML::Node node;
    5. node["type"] = "StdoutLogAppender";
    6. std::stringstream ss;
    7. ss << node;
    8. return ss.str();
    9. }
  • **FileLogAppender**类中:

    1. std::string FileLogAppender::toYamlString()
    2. {
    3. YAML::Node node;
    4. node["type"] = "FileLogAppender";
    5. node["file"] = m_filename;
    6. std::stringstream ss;
    7. ss << node;
    8. return ss.str();
    9. }

3.1.1.2 Logger类中增加toYamlString()函数:

  1. std::string Logger::toYamlString()
  2. {
  3. YAML::Node node;
  4. node["name"] = m_name;
  5. node["level"] = LogLevel::ToString(m_level);
  6. node["formatter"] = m_formatter->getPattern();
  7. for(auto &x : m_appenders)
  8. {
  9. node["appender"].push_back(YAML::Load(x->toYamlString()));
  10. }
  11. std::stringstream ss;
  12. ss << node;
  13. return ss.str();
  14. }

3.1.1.3 LogManager类中增加toYamlString()函数:

  1. //打印当前日志配置信息
  2. YAML::Node LogManager::toYamlString()
  3. {
  4. YAML::Node node;
  5. for(auto &x : s_loggers)
  6. {
  7. node.push_back(YAML::Load(x.second->toYamlString()));
  8. }
  9. std::stringstream ss;
  10. ss << node;
  11. return ss.str();
  12. return node;
  13. }

3.1.1.4 以上几个toYamlString()函数的依赖关系如图:

配置系统测试 - 图22

3.1.1.5 调用如下:

  1. void test_log()
  2. {
  3. KIT_LOG_DEBUG(KIT_LOG_ROOT()) << "before: \n" << kit_server::LoggerMgr::GetInstance()->toYamlString();
  4. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/logs.yaml");
  5. Config::LoadFromYaml(root);
  6. KIT_LOG_DEBUG(KIT_LOG_ROOT()) << "after: \n" << kit_server::LoggerMgr::GetInstance()->toYamlString();
  7. }
  8. int main()
  9. {
  10. test_log();
  11. return 0;
  12. }

3.1.1.6 logs.yaml文件如下:

  1. logs:
  2. - name: root
  3. level: INFO
  4. formatter: "%d%T%m%n"
  5. appender:
  6. - type: FileLogAppender
  7. file: log.txt
  8. - type: StdoutLogAppender
  9. - name: system
  10. level: INFO
  11. formatter: "%d%T%m%n"
  12. appender:
  13. - type: FileLogAppender
  14. file: log.txt
  15. - type: StdoutLogAppender

3.1.1.7 运行结果如下:
image.png

3.1.1.8 小BUG:忽略了日志级别对日志显示的控制作用

从上面调用的测试代码中可以看到:由于配置项中将日志器的级别全部设置为INFO,而调用打印时候的级别仍然为DEBUG导致无法打印日志内容,即:logger->getLevel = INFO <= DEBUG 不成立。无法触发LogEventWrap的析构函数从而间接调用到Logger::log()函数。
image.png

  • 代码修改:(使用标准输出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);

  1. std::cout << "after:\n" << kit_server::LoggerMgr::GetInstance()->toYamlString() << std::endl;

}

  1. - **运行结果如下:(正确得到结果)** ![image.png](https://cdn.nlark.com/yuque/0/2021/png/25460685/1638724105577-94c6ace5-de8f-4b78-be08-f1fd89d62b14.png#clientId=u4036002f-b368-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=254&id=uf54d67b8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=390&originWidth=1198&originalType=binary&ratio=1&rotation=0&showTitle=false&size=33239&status=done&style=none&taskId=u10680efa-c690-4440-b448-34012fe4b56&title=&width=780)
  2. <a name="btqXl"></a>
  3. ### 3.1.2 将单独的日志器约定并且修改配置,组合成`YAML::Node`打印出来
  4. <a name="kErvo"></a>
  5. #### 3.1.2.1 调用如下:
  6. ```cpp
  7. void test_log()
  8. {
  9. auto t = KIT_LOG_NAME("system");
  10. KIT_LOG_DEBUG(t) << "before: \n" << t->toYamlString();
  11. YAML::Node root = YAML::LoadFile("/home/nmoek/kit_server_project/tests/logs.yaml");
  12. Config::LoadFromYaml(root);
  13. auto v = KIT_LOG_NAME("system");
  14. KIT_LOG_DEBUG(v) << "before: \n" << v->toYamlString();
  15. }
  16. int main()
  17. {
  18. test_log();
  19. return 0;
  20. }

3.1.2.2 logs.yaml文件如下:

  1. logs:
  2. - name: root
  3. level: debug
  4. formatter: "%d%T%m%n"
  5. appender:
  6. - type: FileLogAppender
  7. file: log.txt
  8. - type: StdoutLogAppender
  9. - name: system
  10. level: info
  11. formatter: "[%p]%T%d%T%m%n"
  12. appender:
  13. - type: FileLogAppender
  14. file: log.txt
  15. - type: StdoutLogAppender

3.1.2.3 运行结果如下:

image.png

  • 分析:

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

](https://blog.csdn.net/zhao_5352269/article/details/88951863)