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 />![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 />与配置开发(二)分析预测的结果相同。
<a name="XpLo6"></a>
## 2.3 对*.yaml文件中的基本数据类型进行一个序列化
<a name="iWDgQ"></a>
#### 2.3.1 调用如下:
```cpp
using 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: root
level: info
formatter: "%d%T%m%n"
appender:
- type: FileLogAppender
path: log.txt
- type: StdOutLogAppender
system:
port: 9999
float: 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: 9999
float: 20.36
int_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: 9999
float: 20.36
int_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)![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)
<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: 100
b: 300
c: 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: li
age: 17
sex: 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: wang
age: 38
sex: false
person2:
name: liu
age: 26
sex: 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: root
level: INFO
formatter: "%d%T%m%n"
appender:
- type: FileLogAppender
file: log.txt
- type: StdoutLogAppender
- name: system
level: INFO
formatter: "%d%T%m%n"
appender:
- type: FileLogAppender
file: 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;
}
- **运行结果如下:(正确得到结果)** ![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)
<a name="btqXl"></a>
### 3.1.2 将单独的日志器约定并且修改配置,组合成`YAML::Node`打印出来
<a name="kErvo"></a>
#### 3.1.2.1 调用如下:
```cpp
void 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: root
level: debug
formatter: "%d%T%m%n"
appender:
- type: FileLogAppender
file: log.txt
- type: StdoutLogAppender
- name: system
level: info
formatter: "[%p]%T%d%T%m%n"
appender:
- type: FileLogAppender
file: 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)