在sylar的配置模块设计中,采用约定优于配置的思想,其常规使用方法如下:
sylar::ConfigVar::ptr g_int_value_config =
sylar::Config::Lookup("system.port", (int)8080, "system port");
// 定义了system.port未int类型的8090,可通过g_int_value_config->getValue()获得当前参数值
其中“约定优于配置”,也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。
本质是说,开发人员仅需规定应用中不符约定的部分。例如,如果模型中有个名为Sale的类,那么数据库中对应的表就会默认命名为sales。只有在偏离这一约定时,例如将该表命名为"products_sold",才需写有关这个名字的配置。
* @brief 配置变量的基类
/**
* @brief 配置变量的基类
*/
class ConfigVarBase {};
/**
* @brief 类型转换模板类(F 源类型, T 目标类型)
*/
template
class LexicalCast {};
/**
* @brief 配置参数模板子类,保存对应类型的参数值
* @details T 参数的具体类型
* FromStr 从std::string转换成T类型的仿函数
* ToStr 从T转换成std::string的仿函数
* std::string 为YAML格式的字符串
*/
template
,class ToStr = LexicalCast >
class ConfigVar : public ConfigVarBase {};
/**
* @brief ConfigVar的管理类
* @details 提供便捷的方法创建/访问ConfigVar
*/
class Config {};
toString()
方法和fromString()
方法,其分别负责将配置信息转化字符串和从字符串中解析出配置。YAML是一种简洁的非标记语言。YAML以数据为中心,使用空白,缩进,分行组织数据,从而使得表示更加简洁易读。其实例如下:
# yaml测试样例
# null 或 NULL 为关键字,不能写
# 名称
# 字符串
name: conf file
# 版本
# 如按浮点,2.0会转换成2
# 如按字符串,保留原样
version: 2.0
# 布尔类,转换为1或0
need: true
# 时间
time: 2020-10-03T09:21:13
empty: nul
# 对象
# 加双引号会转义\n,即会换行
my:
name: late \n lee
name1: "late \n lee"
age: 99
# 块
text: |
hello
world!
# 数组
fruit:
- apple
- apple1
- apple2
- apple3
- apple4
- apple5
# 多级数组
multi:
sta:
- 110 210 ddd 99
- 133 135 1 2 1588 1509
- 310-410
- 333-444
在C++中,可使用YAML-CPP开源库进行YAML文件的读写,其配置可自行百度。其中,在windows平台使用VScode配置Yaml-cpp可参考本人之前做的一个记录:Windows平台使用VSCode配置Yaml-cpp,相关测试可在上文中也有。
在sylar的配置模块中,后续需要基于自己需求添加的便是对类型转换函数的偏特化实现,其实现了基本类型和Yaml字符串的相互转换。C++中的偏特化介于主版本模板和模板全特化之间,其只明确定义了部分类型,而非将模板唯一化,注意函数模板不能被偏特化。
sylar::ConfigVar >::ptr g_log_defines =
sylar::Config::Lookup("logs", std::set(), "logs config");
//定义LogDefine LogAppenderDefine, 偏特化 LexicalCast,
//实现日志配置解析
struct LogIniter {
LogIniter() {
g_log_defines->addListener([](const std::set& old_value,
const std::set& new_value){
SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "on_logger_conf_changed";
for(auto& i : new_value) {
auto it = old_value.find(i);
sylar::Logger::ptr logger;
if(it == old_value.end()) {
//新增logger
logger = SYLAR_LOG_NAME(i.name);
} else {
if(!(i == *it)) {
//修改的logger
logger = SYLAR_LOG_NAME(i.name);
} else {
continue;
}
}
logger->setLevel(i.level);
//std::cout << "** " << i.name << " level=" << i.level
//<< " " << logger << std::endl;
if(!i.formatter.empty()) {
logger->setFormatter(i.formatter);
}
logger->clearAppenders();
for(auto& a : i.appenders) {
sylar::LogAppender::ptr ap;
if(a.type == 1) {
ap.reset(new FileLogAppender(a.file));
}
else if(a.type == 2) {
ap.reset(new StdoutLogAppender);
}
ap->setLevel(a.level);
if(!a.formatter.empty()) {
LogFormatter::ptr fmt(new LogFormatter(a.formatter));
if(!fmt->isError()) {
ap->setFormatter(fmt);
} else {
std::cout << "log.name=" << i.name << " appender type=" << a.type
<< " formatter=" << a.formatter << " is invalid" << std::endl;
}
}
logger->addAppender(ap);
}
}
for(auto& i : old_value) {
auto it = new_value.find(i);
if(it == new_value.end()) {
//删除logger
auto logger = SYLAR_LOG_NAME(i.name);
logger->setLevel((LogLevel::Level)0);
logger->clearAppenders();
}
}
});
}
};
// 定义一个LogIniter类,实例化未全局对象。
static LogIniter __log_init;
logs:
- name: root
level: (debug,info,warn,error,fatal)
formatter: '%d%T%p%T%t%m%n'
appender:
- type: (StdoutLogAppender, FileLogAppender)
level:(debug,...)
file: /logs/xxx.log
sylar::Logger g_logger = sylar::LoggerMgr::GetInstance()->getLogger(name);
SYLAR_LOG_INFO(g_logger) << "xxxx log";
void test_log() {
static sylar::Logger::ptr system_log = SYLAR_LOG_NAME("system");
SYLAR_LOG_INFO(system_log) << "hello system" << std::endl;
std::cout << sylar::LoggerMgr::GetInstance()->toYamlString() << std::endl;
YAML::Node root = YAML::LoadFile("/home/sylar/workspace/sylar/bin/conf/log.yml");
sylar::Config::LoadFromYaml(root);
std::cout << "=============" << std::endl;
std::cout << sylar::LoggerMgr::GetInstance()->toYamlString() << std::endl;
std::cout << "=============" << std::endl;
std::cout << root << std::endl;
SYLAR_LOG_INFO(system_log) << "hello system" << std::endl;
system_log->setFormatter("%d - %m%n");
SYLAR_LOG_INFO(system_log) << "hello system" << std::endl;
}