索引
【C++模块实现】| 【01】日志系统实现
【C++模块实现】| 【02】日志系统优化
【C++模块实现】| 【03】文件管理模块
【C++模块实现】| 【04】配置模块
【C++模块实现】| 【05】日志模块增加配置模块的功能
【C++模块实现】| 【06】日志模块添加循环覆盖写文件功能
【C++模块实现】| 【07】对于互斥、自旋锁、条件变量、信号量简介及封装
【C++模块实现】| 【08】循环覆盖写内存缓冲区(日志中多线程记录)
【C++模块实现】| 【09】线程模块及线程池的实现
【C++模块实现】| 【10】定时器的实现
【C++模块实现】| 【11】封装Ipv4、Ipv6、unix网络地址
1、简介
结合前面的配置模块,我们已经将日志系统的配置项通过配置模块进行加载;
但该日志系统中没有提供循环覆盖写文件功能,故本节将参考网上一些文章对其进行完善;
结合参考的文章,做出了一些简单的循环覆盖写文件:
首先配置中设定日志保存的期限、日志文件的内存上限以及扩张的文件数(log1.txt、log2.txt),可以有多种方式,可以按时间进行文
件划分,但本次只简单的设置按个数划分;
方法一:通过检查文件每个文件的日期,若文件数没有到达限制个数,则创建它做为日志副本;
否则直接将文件修改日志最小的(即最早生成的)作为日志的存储副本;
【如:】若限定最多生成5个文件数(log1.txt、log2.txt、log3.txt、log4.txt、log5.txt),日志只往log.txt中写入,
当log.txt写满时,将遍历这5个文件,若文件不存在,则直接移动到该文件,若存在,则移动到文件修改日期最早的那个文件;
方法二:
在配置中增添一项用来记录日志更新到第几个文件数file_num;
当log.txt文件写满时,先检查各个文件日志是否存在,若存在则检查是否过期,若过期则删除,且log.txt日志直接移动到
该日志文件中,若不存在,则log.txt直接移动到该日志文件中,若满足期限且存在,则移动到logfile_num.txt中;
该file_num若有变动,则会对配置文件进行更新记录,便于下次加载;
上述方法测试,暂时没有问题,只是简单的实现,若有其他缺漏,或方法不适用,麻烦各位指点。
2、遇到的问题
2.1 日志文件大小的限制没有达到预期的要求
每次进入该判断时,文件大小是正确的,但后续对文件进行移动的时候却显示文件的大小远大于该限制;
后面发现没有对该文件进行关闭操作,应该是缓冲区中的内容还没有写入到文件中导致的;
后续在此处对文件关闭即解决
2.2 yamlstring
当配置项的内容有更新时,需要回写到配置文件时,将YAML::Node格式的内容压入stringstream中后写入文件格式出现问题;
后续提供toYaml接口,和toYamlString分开;
以下是错误格式:
3、代码改进
struct stat st;
/* 判断文件是否满了 */
if(!stat(m_filename.c_str(), &st)) {
if(st.st_size > m_file_buf) { // 判断文件是否写满了
m_filestream.close();
std::string newFile = m_filename.substr(0, m_filename.rfind('.'));
int i;
// /**方法一: 查看日志文件的修改文件的时间,哪个最旧,即替换哪个 */
// /*uint64_t t = INT64_MAX;
// int idx=0;
// for(i=1; i<=backups_files ; ++i) {
// std::string tmp = newFile + std::to_string(i) + ".txt";
// if(!stat(tmp.c_str(), &st)) {
// idx = i;
// break;
// }
//
// if(((time(0) - st.st_mtime) / 24 * 60 * 60) > backups_days) {
// FSUtil::Rm(tmp);
// idx = i;
// break;
// }
// if(t >= st.st_mtime){
// t = st.st_mtime;
// idx=i;
// newFile = tmp;
// }
// }
// FSUtil::Mv(m_filename, newFile);
// FSUtil::Rm(m_filename);
// FSUtil::OpenForWrite(m_filestream, m_filename.c_str(), std::ios::app);
// */
//
/** 方法二:根据记录 */
for(i=1; i<=m_backups_files ; ++i) {
std::string tmp = newFile + std::to_string(i) + ".txt";
if(stat(tmp.c_str(), &st)) { // 判断文件是否存在
break;
}
/* 判断日志是否过期 */
if(((time(0) - st.st_mtime) / (24 * 60 * 60)) > m_backups_days) {
FSUtil::Rm(tmp);
}
}
FSUtil::Mv(m_filename, newFile+std::to_string(m_file_num) + ".txt");
FSUtil::Rm(m_filename);
FSUtil::OpenForWrite(m_filestream, m_filename.c_str(), std::ios::app);
m_file_num += 1;
if(m_file_num > m_backups_files) m_file_num = 1;
IsChange = true; // 若为true则需要更新yaml配置文件
}
4、测试
测试循环覆盖写文件
此时的配置文件为:
测试代码
#include <iostream>
#include "../sylar/log.h"
#include "../sylar/util.h"
#include "../sylar/config.h"
#include <unistd.h>
#include <time.h>
int main(int argc, char** argv) {
sylar::Logger::ptr g_logger = SYLAR_LOG_NAME("root");
sylar::Config::LoadFromYaml("/root/code/log_server/bin/conf/log.yml");
// 输出日志
std::cout << g_logger->toYamlString() << std::endl;
for(int i=0; i<160000; ++i) {
SYLAR_LOG_INFO(g_logger) << i;
sleep(1);
}
return 0;
}
参考文章
=========》Log4j2 RollingFileAppender《=========
=========》滚动文件附加器指南《=========
=========》RollingFileWithCleanupAppender《=========