日志系统扩展一:日志落地数据库:MySQL、SQLite3
一、设计
1.怎么落地
将日志落地到数据库,首先肯定是要建表的,而日志落地其实就是向对应表当中插入数据
那么怎么落地呢?
文件落地方式:virtual void log_fs(const char *data, size_t len) = 0;
因为文件是面向字节流的,所以这么落地日志是完全OK的
而数据库落地是向表当中插入数据,因此他的落地应该是这样的:
virtual void log_db(const LogMessage &message) = 0;
将一条LogMessage插入表当中
因此我们的LogSink日志落地基类无法满足数据库的落地需求,要不然就给他增加一个log_db函数
让子类选择性的对其中一个进行重写,另一个进行空实现,这是一种方式
不过不便于扩展,为了遵循高内聚,低耦合的程序设计原则,我们将LogSink分为两个日志基类:
FSLogSink和DBLogSink
新增加的MySQLSink和SqliteSink都继承于DBLogSink
2.落地的具体设计
我们的日志落地方式是允许用户灵活的进行格式控制的,这是为了让用户能够选择性的只记录自己想要的数据
而将日志落地到数据库,是非字符串形式的,因此格式化的意义不大
所以我们的日志落地到数据库当中是无需进行格式化的,这也印证了log_db的参数为何只需要一个LogMessage即可
virtual void log_db(const LogMessage &message) = 0;
所以我们再将日志落地到数据库时,是直接将所有字段全部都进行记录的
3.表的设计
1.MySQL
2.SQLite3
同样的,SQLite3当中LigData的创建也是如此,只不过具体细节要求不同:
二、数据库访问Helper的实现
1.需要事务,但是无需回滚,如何理解?
1.需要事务
2.无需回滚
不过我们贴心的给了大家savepoint和rollback的使用,大家可以使用
2.SqliteHelper
1.SQLite3常用接口介绍
2.实现
class SqliteHelper
{
public:
using SqliteCallback = int (*)(void *, int, char **, char **);
SqliteHelper(const std::string &dbfile)
: _dbfile(dbfile), _handler(nullptr) {
}
~SqliteHelper()
{
close();
}
bool open()
{
if (sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr) != SQLITE_OK)
{
std::cout << "打开数据库失败 " << errmsg() << " _dbfile:" << _dbfile << "\n";
return false;
}
return true;
}
void close()
{
if (_handler != nullptr)
{
sqlite3_close_v2(_handler);
_handler = nullptr;
}
}
bool begin()
{
if (sqlite3_exec(_handler, "begin;", nullptr, nullptr, nullptr) != SQLITE_OK)
{
std::cout << "SQLite3开启事务失败," << errmsg() << "\n";
return false;
}
return true;
}
bool exec(const std::string &sql, SqliteCallback cb, void *arg)
{
if (sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr) != SQLITE_OK)
{
std::cout << "执行sql语句:" << sql << " 失败," << errmsg() << std::endl;
return false;
}
return true;
}
bool commit()
{
if (sqlite3_exec(_handler, "commit;", nullptr, nullptr, nullptr) != SQLITE_OK)
{
std::cout << "SQLite3提交事务失败," << errmsg() << "\n";
return false;
}
return true;
}
bool savePoint(const std::string &point)
{
std::string savepoint_sql = "savepoint " + point + ";";
if (sqlite3_exec(_handler, savepoint_sql.c_str(), nullptr, nullptr, nullptr) != SQLITE_OK)
{
std::cout << "SQLite3 设置事务保存点失败:" << errmsg() << "\n";
return false;
}
return true;
}
bool rollback(const std::string &point = "")
{
std::string rollback_sql = "rollback";
if (!point.empty())
{
rollback_sql += " to " + point + ";";
}
if (sqlite3_exec(_handler, rollback_sql.c_str(), nullptr, nullptr, nullptr) != SQLITE_OK)
{
std::cout << "SQLite3 事务回滚失败:" << errmsg() << "\n";
return false;
}
return true;
}
std::string errmsg()
{
if (_handler != nullptr)
return sqlite3_errmsg(_handler);
else
return "sqlite3句柄为空";
}
private:
std::string _dbfile;
sqlite3 *_handler;
};
3.MySQLHelper
1.MySQL常用接口介绍
2.实现
我们要求用户将MySQL连接所需字段放到配置文件当中,将配置文件传递过来
我们内部进行解析
格式要求:
一个字段占一行
以key:value的形式进行传递
host:你MySQL服务器所在机器的IP地址
user:你的用户名
passwd:你的密码
db:你提前创建好并赋予权限了的数据库
port:你MySQL服务器的端口号
class MySQLHelper
{
public:
MySQLHelper(const std::string &conf_file) : _handler(mysql_init(nullptr)), _conf_file(conf_file){
}
bool begin()
{
if (mysql_query(_handler, "begin;") != 0)
{
std::cout << "mysql开启事务失败," << errmsg() << "\n";
return false;
}
return true;
}
bool commit()
{
if (mysql_query(_handler, "commit;") != 0)
{
std::cout << "mysql提交事务失败," << errmsg() << "\n";
return false;
}
return true;
}
bool savePoint(const std::string &point)
{
std::string savepoint_sql = "savepoint " + point + ";";
if (mysql_query(_handler, savepoint_sql.c_str()) != 0)
{
std::cout << "MySQL 设置事务保存点失败:" << errmsg() << "\n";
return false;
}
return true;
}
bool rollback(const std::string &point = "")
{
std::string rollback_sql = "rollback";
if (!point.empty())
{
rollback_sql += " to " + point + ";";
}
if (mysql_query(_handler, rollback_sql.c_str()) != 0