还有使用样例代码
和扩展样例代码(test.cc以及写的很详细了,后续补充)
以及性能测试代码
test.cc
// #include "func.hpp"
// #include "log_level.hpp"
// #include "my_format.hpp"
// #include "my_message.hpp"
// #include "log_sink.hpp"
// #include "lrt_logger.hpp"
// #include "buffer.hpp"
// #include "looper.hpp"
#include "lrt_log.h"
void global_log_test2()
{
lrtlog::lrt_logger::ptr logger_p=lrtlog::logger_manager::get_instance().get_logger("异步全局日志器");
if (logger_p == nullptr)
{
std::cout << "l1 fail";
return;
}
for (int i = 0; i < 3000; i++) // 经过极限测试,是线程安全的
{
logger_p->debug("%s", "测试日志");
logger_p->info( "%s:%d", "测试日志", i);
logger_p->warn( "%s", "测试日志");
logger_p->error( "%s", "测试日志");
logger_p->fatal( "%s", "测试日志");
}
for (int i = 0; i < 3000; i++) // 经过极限测试,是线程安全的
{
DEBUG( "%s", "测试日志");
INFO( "%s:%d", "测试日志", i);
WARN( "%s:%d", "测试日志", i);
ERROR( "%s:%d", "测试日志", i);
FATAL( "%s:%d", "测试日志", i);
}
}
// void global_log_test()
// {
// lrtlog::lrt_logger::ptr logger_p =lrtlog::logger_manager::get_instance().get_rootlogger();
// if (logger_p == nullptr)
// {
// std::cout << "l1 fail";
// return;
// }
// for (int i = 0; i < 3000; i++) // 经过极限测试,是线程安全的
// {
// logger_p->debug( "%s", "测试日志");
// logger_p->info( "%s:%d", "测试日志", i);
// logger_p->warn( "%s", "测试日志");
// logger_p->error( "%s", "测试日志");
// logger_p->fatal( "%s", "测试日志");
// }
// lrtlog::lrt_logger::ptr logger_p2 =lrtlog::logger_manager::get_instance().get_logger("异步全局日志器");
// if (logger_p2 == nullptr)
// {
// std::cout << "l2 fail" << std::endl;
// return;
// }
// for (int i = 0; i < 3000; i++) // 经过极限测试,是线程安全的
// {
// logger_p2->debug( "%s", "测试日志");
// logger_p2->info( "%s:%d", "测试日志", i);
// logger_p2->warn( "%s", "测试日志");
// logger_p2->error( "%s", "测试日志");
// logger_p2->fatal( "%s", "测试日志");
// }
// }
int main()
{
// 全局日志器建造者模式测试
std::unique_ptr<lrtlog::global_logger_build> logger_build(new lrtlog::global_logger_build());
logger_build->name_build("异步全局日志器");
logger_build->level_build(lrtlog::log_level::DEBUG); // 输出debug及其以上的信息
logger_build->formatter_build("[%d][%C][%p]%T[%f:%l tid:%t]:[%m]%n");
logger_build->type_build(lrtlog::logger_type::ASY_LOGGER);
logger_build->sinks_build<lrtlog::stdout_sink>();
logger_build->sinks_build<lrtlog::file_sink>("./logfile/filetest");
logger_build->unsafe_build(lrtlog::AsyncType::ASYN_UNSAFE);
logger_build->sinks_build<lrtlog::rolly_by_size_sink>("./logfile/rolltest", 1024 * 1024);
// std::cout << "begin" << std::endl;
logger_build->build();
global_log_test2();
// //局部日志器建造者模式测试
// std::unique_ptr<lrtlog::local_logger_build> logger_build(new lrtlog::local_logger_build());
// logger_build->name_build("异步日志器");
// logger_build->level_build(lrtlog::log_level::DEBUG);//输出debug及其以上的信息
// logger_build->formatter_build("[%d][%C][%p]%T[%f:%l tid:%t]:[%m]%n");
// logger_build->type_build(lrtlog::logger_type::ASY_LOGGER);
// //logger_build->sinks_build<lrtlog::stdout_sink>();
// logger_build->sinks_build<lrtlog::file_sink>("./logfile/filetest");
// logger_build->unsafe_build(lrtlog::AsyncType::ASYN_UNSAFE);
// logger_build->sinks_build<lrtlog::rolly_by_size_sink>("./logfile/rolltest", 1024 * 1024);
// // std::cout << "begin" << std::endl;
// lrtlog::lrt_logger::ptr logger_p = logger_build->build();
// for(int i = 0 ;i<30000;i++)//经过极限测试,是线程安全的
// {
// logger_p->debug( "%s", "测试日志");
// logger_p->info( "%s:%d", "测试日志",i);
// logger_p->warn( "%s", "测试日志");
// logger_p->error( "%s", "测试日志");
// logger_p->fatal( "%s", "测试日志");
// }
// std::cout << "END" << std::endl;
//sleep(10);
// lrtlog::func::File::creat_directory("./logfile/");
// std::ifstream ifs("./logfile/rolltest-124214-172420.log-1",std::ios::binary);
// if (ifs.is_open() == false)
// {
// std::cout << "fail ";
// return 0;
// }
// ifs.seekg(0,std::ios::end);//读写位置跳转到文件末尾
// size_t fsize = ifs.tellg();//获取当前读写位置相对起始位置的偏移量
// ifs.seekg(0,std::ios::beg);//重新跳转到起始位置
// std::string body;
// body.resize(fsize);
// ifs.read(&body[0],fsize);
// if(ifs.good() == false){
// std::cout << "read error";
// }
// ifs.close();
// lrtlog::Buffer buffer;
// for(int i=0;i<body.size();i++)
// {
// buffer.push(&body[i],1);
// }
// int max = buffer.read_able_size();
// std::ofstream ofs("./logfile/buffer222",std::ios::binary);
// for(int i=0;i<max;i++)
// {
// ofs.write(buffer.begin(),1);
// buffer.move_reader_idx(1);
// }
// ofs.close();
//局部日志器建造者模式测试
// std::unique_ptr<lrtlog::local_logger_build> logger_build(new lrtlog::local_logger_build());
// logger_build->name_build("同步日志器");
// logger_build->level_build(lrtlog::log_level::DEBUG);
// logger_build->formatter_build("[%d][%C][%p]%T[%f:%l tid:%t]%m%n");
// logger_build->type_build(lrtlog::logger_type::SYN_LOGGER);
// //logger_build->sinks_build<lrtlog::stdout_sink>();
// logger_build->sinks_build<lrtlog::file_sink>("./logfile/filetest");
// logger_build->sinks_build<lrtlog::rolly_by_size_sink>("./logfile/rolltest", 1024 * 1024);
// lrtlog::lrt_logger::ptr logger_p = logger_build->build();
// for(int i = 0 ;i<1000;i++)
// {
// logger_p->debug( "%s", "测试日志");
// logger_p->info( "%s", "测试日志");
// logger_p->warn( "%s", "测试日志");
// logger_p->error( "%s", "测试日志");
// logger_p->fatal( "%s", "测试日志");
// }
// 日志器各基础接口的测试
// std::string logger_name = "lrt_syn_logger";
// lrtlog::log_level::level level=lrtlog::log_level::level::DEBUG;
// lrtlog::formatter::ptr formatter(new lrtlog::formatter("[%d][%C][%p]%T[%f:%l tid:%t]%m%n"));
// std::vector<lrtlog::log_sink::ptr> sinks;
// lrtlog::log_sink::ptr stdout_p = lrtlog::sink_factory::create<lrtlog::stdout_sink>();
// lrtlog::log_sink::ptr file_p = lrtlog::sink_factory::create<lrtlog::file_sink>("./logfile/filetest");
// lrtlog::log_sink::ptr roll_p = lrtlog::sink_factory::create<lrtlog::rolly_by_size_sink>("./logfile/rolltest", 1024 * 1024);
// sinks = {stdout_p,file_p,roll_p};
// std::string str="----";
// lrtlog::lrt_logger::ptr logger_p(new lrtlog::synchronous_logger(logger_name,level,formatter,sinks));
// //lrtlog::lrt_logger::ptr logger_p(std::make_shared<lrtlog::synchronous_logger>(logger_name,level,formatter,sinks));
// logger_p->debug(__FILE__,__LINE__,"%s","测试日志");
// logger_p->info(__FILE__,__LINE__,"%s","测试日志");
// logger_p->warn(__FILE__,__LINE__,"%s","测试日志");
// logger_p->error(__FILE__,__LINE__,"%s","测试日志");
// logger_p->fatal(__FILE__,__LINE__,"%s","测试日志");
// int i = 0;
// for (int cur = 0; cur < 1024 * 1024 * 10; cur += str.size())
// {
// usleep(100000);
// std::string s = str + std::to_string(i++);
// }
// lrtlog::log_message msg(lrtlog::log_level::INFO, (size_t)50, (size_t)1000, "root", "格式化日志信息功能的测试", "1111...");
// lrtlog::formatter fmt;
// std::string str = fmt.format(msg);
// //std::cout << str << std::endl;
// //lrtlog::log_sink::ptr stdout_p = lrtlog::sink_factory::create<lrtlog::stdout_sink>();
// //lrtlog::log_sink::ptr file_p = lrtlog::sink_factory::create<lrtlog::file_sink>("./logfile/filetest");
// lrtlog::log_sink::ptr roll_p = lrtlog::sink_factory::create<lrtlog::rolly_by_size_sink>("./logfile/rolltest", 1024 * 1024);
// // stdout_p->log(str.c_str(), str.size());
// //file_p->log(str.c_str(), str.size());
// int i = 0;
// for (int cur = 0; cur < 1024 * 1024 * 10; cur += str.size())
// {
// std::string s = str + std::to_string(i++);
// roll_p->log(s.c_str(), str.size());
// }
// lrtlog::log_message msg(lrtlog::log_level::INFO,(size_t)50,(size_t)1000,"root","格式化日志信息功能的测试","1111...");
// lrtlog::formatter fmt;
// std::string str = fmt.format(msg);
// std::cout <<str <<std::endl;
// std::cout << lrtlog::log_level::to_string(lrtlog::log_level::ERROR) << std::endl;
// std::cout << lrtlog::func::Date::get_time() << std::endl;
// std::string pathname="./111/222/333/454/555/666/";
// lrtlog::func::File::creat_directory(pathname);
// return 0;
}
func.hpp
#ifndef MY_FUNC
#define MY_FUNC
#include<iostream>
#include<ctime>
#include<unistd.h>
//#include<cstring>
#include<sys/stat.h>
namespace lrtlog{
namespace func{
class Date{
public:
static size_t get_time()
{
return (size_t)time(nullptr);
}
};
//全部创建的是文件夹而不会把最后一个文件创建成文本因此通过open自动创建,唉唉唉。
class File{
public:
static bool is_exist(const std::string &pathname)
{
struct stat st;
if (stat(pathname.c_str(),&st) < 0 )
{
return false;
}
return true;
//return (access(pathname.c_str(),F_OK)==0);//不支持跨平台
}
static std::string path(const std::string &pathname){
size_t pos=0;
pos=pathname.find_last_of("/\\");
if (pos==std::string::npos) return ".";
return pathname.substr(0,pos+1);//第二个参数是长度
}
static void creat_directory(const std::string &pathname){
size_t idx=0,pos=0;
std::string parent_dir;
while(pos < pathname.size() ){
pos=pathname.find_first_of("/\\",idx);//idx表示从哪里开始找
if(pos == std::string::npos){
mkdir(pathname.c_str(),0755);
}
parent_dir=pathname.substr(0,pos+1);
if(is_exist(parent_dir)==false) mkdir(parent_dir.c_str(),0755);
idx=pos+1;
}
}
};
}
}
#endif
log_level.hpp
#ifndef LOG_LEVEL
#define LOG_LEVEL
namespace lrtlog{
class log_level{
public:
enum level
{
UNKNOW = 0,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
OFF
};
static const char* to_string(log_level::level level){
switch (level)
{
case log_level::level::DEBUG : return "DEBUG";break;
case log_level::level::INFO : return "INFO";break;
case log_level::level::WARN : return "WARN";break;
case log_level::level::ERROR : return "ERROR";break;
case log_level::level::FATAL : return "FATAL";break;
case log_level::level::OFF : return "OFF";break;
default: return "UNKNOW"; break;
}
}
};
}
#endif
my_format.hpp
#ifndef MY_FORMAT
#define MY_FORMAT
#include "log_level.hpp"
#include "my_message.hpp"
#include <time.h>
#include <vector>
#include <cassert>
#include <sstream>
#include <string>
namespace lrtlog
{
class format_item
{
public:
using ptr = std::shared_ptr<format_item>;
virtual void format(std::ostream &out, log_message &msg) = 0;
};
class level_format_item : public format_item
{
public:
void format(std::ostream &out,log_message &msg) override
{
out << log_level::to_string(msg._level);
}
};
class payload_format_item : public format_item
{
public:
virtual void format(std::ostream &out, log_message &msg) override
{
out << msg._payload;
}
};
class name_format_item : public format_item
{
public:
void format(std::ostream &out, log_message &msg) override
{
out << msg._name;
}
};
class tid_format_item : public format_item
{
public:
void format(std::ostream &out, log_message &msg) override
{
//没找到什么好的办法把thread_id转换成int
//std::string s((int)(msg._tid));
//auto * i = &(msg._tid);
//std::stringstream ss;
//ss << msg._tid;
//std::cout <<ss.str();
//std::cout << std::this_thread::get_id();//因为tid并不唯一标识真的有需要的话可以自己弄一个
out <<msg._tid;
}
};
class file_format_item : public format_item
{
public:
void format(std::ostream &out, log_message &msg) override
{
out << msg._file;
}
};
class line_format_item : public format_item
{
public:
void format(std::ostream &out, log_message &msg) override
{
out << msg._line;
}
};
class time_format_item : public format_item
{
public:
time_format_item(const std::string& s = "%H:%M:%S")
:_time_format(s)
{}
void format(std::ostream &out, log_message &msg) override
{
time_t t = msg._ctime;
struct tm t2;
localtime_r(&t, &t2); // 把指定的时间格式转换成时间结构
char tmp[64] = {0};
strftime(tmp, 63, _time_format.c_str(), &t2); // 把指定的时间格式按指定的格式放入tmp中
//std::cout << tmp;
out << tmp;
}
private:
std::string _time_format;
};
class tab_format_item : public format_item
{
public:
void format(std::ostream &out, log_message &msg) override
{
out << "\t";
}
};
class nline_format_item : public format_item
{
public:
void format(std::ostream &out, log_message &msg) override
{
out << "\n";
}
};
class other_format_item : public format_item
{
public:
other_format_item(const std::string c) : _s(c) {}
void format(std::ostream &out, log_message &msg) override
{
out << _s;
}
private:
const std::string _s;
};
// %d ⽇期
// %T 缩进
// %t 线程id
// %p ⽇志级别
// %c ⽇志器名称
// %f ⽂件名
// %l ⾏号
// %m ⽇志消息
// %n 换⾏
class formatter
{
public:
using ptr = std::shared_ptr<formatter>;
formatter(const std::string &pattern = "[%d][%C][%p]%T[%f:%l tid:%t]%m%n") : _pattern(pattern) //[%d]{%H:%M:%S}
{
parsePatter();
//std::cout << "hhhh";
//assert(parsePatter());
}
// 格式化msg
private:
void format(std::ostream &out, log_message &msg)
{
for (auto &item : _item)
{
item->format(out, msg);
}
//item->format(out,msg._ctime);
}
public:
//外界调用的不需要传入字符串流
std::string format(log_message &msg)
{
std::stringstream ss;
format(ss, msg);
return ss.str();
}
// 解析格式化字符串
bool parsePatter()
{
// aaa%%%ww[%d{%H:%M:%S}][%P]%t%M%n
size_t pos = 0;
std::vector<std::pair<std::string, std::string>> tmp_format;
std::string key;
std::string val;
while (pos < _pattern.size())
{
// 1.不是%,说明是other,处理完进行下一次循环
if (_pattern[pos] != '%')
{
val.push_back(_pattern[pos++]);
continue;
}
// 双%特别处理,%%处理为一个%
if ((pos + 1) < _pattern.size() && _pattern[pos + 1] == '%')
{
// val.push_back(_pattern[pos])
val.push_back('%');
pos += 2;
continue;
}
// other处理完毕,先进行添加
if (val.empty() == false)
{
tmp_format.push_back(std::make_pair("", val));
val.clear();
}
pos += 1;
if (pos == _pattern.size())
{
std::cout << "%tail not have enoug payload";
return false;
}
// 记录类型
key = _pattern[pos];
// if (pos < _pattern.size() && _pattern[pos] == '{')
// {
// pos += 1;
// while (pos < _pattern.size() && _pattern[pos] == '}')
// {
// val.push_back(_pattern[pos++]);
// }
// if (pos >= _pattern.size())
// {
// std::cout << "{} error";
// return false;
// }
// // 走出 }
// pos += 1;
// }
if(_pattern[pos-1] == '%') pos++;//防止添加类型进val
tmp_format.push_back(std::make_pair(key, val));
key.clear();
val.clear();
}
for (auto& it : tmp_format)
{
//std::cout<< it.first;
_item.push_back(_creat_all_item(it.first, it.second));
}
return true;
}
private:
std::string _pattern;
std::vector<format_item::ptr> _item;
private:
// 由传入的格式化字符串创建不同的格式化子项 ,也就是上面继承的一群类
format_item::ptr _creat_all_item(const std::string &fc, const std::string &val)
{
if (fc == "m")
return std::make_shared<payload_format_item>();
if (fc == "p")
return std::make_shared<level_format_item>();
if (fc == "C")
return std::make_shared<name_format_item>();
if (fc == "t")
return std::make_shared<tid_format_item>();
if (fc == "n")
return std::make_shared<nline_format_item>();
if (fc == "d")
return std::make_shared<time_format_item>();//子格式的传入、有问题
if (fc == "f")
return std::make_shared<file_format_item>();
if (fc == "l")
return std::make_shared<line_format_item>();
if (fc == "T")
return std::make_shared<tab_format_item>();
if (fc == "")
return std::make_shared<other_format_item>(val);
//std::cout <<"unknow format" <<std::endl;
//abort();
return std::make_shared<other_format_item>("");
}
};
}
#endif
my_messeage.hpp
#ifndef MY_MESSAGE
#define MY_MESSAGE
#include "func.hpp"
#include "log_level.hpp"
#include <thread>
#include <time.h>
namespace lrtlog{
struct log_message
{
using ptr =std::shared_ptr<log_message>;
size_t _line;
time_t _ctime;
size_t _tid;
const std::string _name;
const std::string _file;
const std::string _payload;
const log_level::level _level;
log_message(
log_level::level level,
size_t line,
size_t tid,
const std::string name,
const std::string file,
const std::string payload):
_name(name),
_file(file),
_ctime(func::Date::get_time()),
_payload(payload),
_level(level),
_line(line),
_tid(tid) {}
};
}
#endif
log_sink.hpp
#ifndef __LOG_SINK__
#define __LOG_SINK__
#include <memory>
#include <fstream>
#include <assert.h>
#include <sstream>
#include "func.hpp"
namespace lrtlog{
class log_sink{
public:
using ptr=std::shared_ptr<log_sink>;
log_sink() {}
// log_sink(lrtlog::log_sink::ptr p)
// {
// ptr=p;
// }
virtual ~log_sink(){}
virtual void log(const char* data,size_t len) =0;
};
//落地方向一:标准输出
class stdout_sink : public log_sink {
public:
//将日志写到标准输出
void log(const char* data,size_t len) override
{
std::cout.write(data,len);
}
};
//落地方向二:文件输出
class file_sink :public log_sink{
public:
file_sink(const std::string& name)
:_name(name)
{
func::File::creat_directory(func::File::path(name));//需要嵌套
_ofs.open(_name, std::ios::binary | std::ios::app);
assert(_ofs.is_open());
}
//将日志消息写到标准输出
void log(const char* data,size_t len) override
{
_ofs.write(data,len);
assert(_ofs.good());
}
private:
std::string _name;
std::ofstream _ofs;
};
//落地方向三 滚动文件输出
class rolly_by_size_sink: public log_sink
{
public:
rolly_by_size_sink(const std::string& name,size_t max_size) // 构造并打开文件,并通过_ofs管理
:_base_name(name),
_max_size(max_size),
_cur_size(0),
_name_count(1)
{
std::string pathname=_creat_new_file();
func::File::creat_directory(func::File::path(name)); //貌似不需要先创建parent——path吧
_ofs.open(pathname,std::ios::binary | std::ios::app);
assert(_ofs.good());
}
// 超过就切换文件
void log(const char *data, size_t len) override
{
if (_cur_size >= _max_size)
{
_ofs.close();
std::string pathname = _creat_new_file();
//func::File::creat_directory(pathname);
_ofs.open(pathname, std::ios::binary | std::ios::app);
assert(_ofs.is_open());
_cur_size = 0;//切换清零
}
_ofs.write(data, len);
assert(_ofs.good());
_cur_size += len; // 注意需要追加
}
private:
std::string _base_name; // 通过基础加扩展文件名生成输出文件
std::ofstream _ofs;
size_t _max_size;
size_t _cur_size;
size_t _name_count;
private:
std::string _creat_new_file(){
time_t t=func::Date::get_time();
struct tm lt;
localtime_r(&t,<);
std::stringstream filename;
filename << _base_name.c_str();
filename << "-";
filename <<lt.tm_year;
filename <<lt.tm_mon+1; //注意
filename <<lt.tm_mday;
filename << "-";
filename <<lt.tm_hour;
filename <<lt.tm_min;
filename <<lt.tm_sec;
filename <<".log";
filename <<"-";
filename <<_name_count++;
return filename.str();
}
};
enum time_type{
gap_second = 1,
gap_min =60,
gap_hour =3600,
gap_day =3600*24
};
// 落地方向三 滚动文件输出,但是根据时间切换
class rolly_by_time_sink : public log_sink
{
public:
rolly_by_time_sink(const std::string &name, size_t time_type) // 构造并打开文件,并通过_ofs管理
: _base_name(name),
_gap_time(time_type),
_cur_time(func::Date::get_time()/time_type),
_name_count(1)
{
std::string pathname = _creat_new_file();
func::File::creat_directory(func::File::path(name)); // 貌似不需要先创建parent——path吧
_ofs.open(pathname, std::ios::binary | std::ios::app);
assert(_ofs.good());
}
// 超过就切换文件
void log(const char *data, size_t len) override
{
if (_cur_time != ((func::Date::get_time())/_gap_time))
{
_cur_time =func::Date::get_time()/_gap_time;//需要更新
_ofs.close();
std::string pathname = _creat_new_file();
// func::File::creat_directory(pathname);也可以分文件夹进行拓展,此处不展开
_ofs.open(pathname, std::ios::binary | std::ios::app);
assert(_ofs.is_open());
//_cur_size = 0; // 切换清零
}
_ofs.write(data, len);
assert(_ofs.good());
//_cur_size += len; // 注意需要追加
}
private:
std::string _base_name; // 通过基础加扩展文件名生成输出文件
std::ofstream _ofs;
size_t _gap_time;
size_t _cur_time;
size_t _name_count;
private:
std::string _creat_new_file()
{
time_t t = func::Date::get_time();
struct tm lt;
localtime_r(&t, <);
std::stringstream filename;
filename << _base_name.c_str();
filename << "-";
filename << lt.tm_year;
filename << lt.tm_mon + 1; // 注意
filename << lt.tm_mday;
filename << "-";
filename << lt.tm_hour;
filename << lt.tm_min;
filename << lt.tm_sec;
filename << ".log";
filename << "-";
filename << _name_count++;
return filename.str();
}
};
class sink_factory
{
public:
template <typename sink_type, typename ...Args>
static log_sink::ptr create(Args &&...args)
{
// 展开参数包,采用了模板成员函数,遵循开闭原则,易于拓展
return std::make_shared<sink_type>(std::forward<Args>(args)...); // 注意语法
}
};
}
#endif
looper.hpp
#ifndef __LOOPER__
#define __LOOPER__
#include "buffer.hpp"
#include <functional>
#include <thread>
#include <mutex>
#include <atomic>
#include <iostream>
// conditional
// 在 C++ 中,std::conditional 不是一个变量,而是一个模板类,
// 位于 <type_traits> 头文件中。它提供了条件式选择功能,根据给定的条件在编译时选择类型。
//#include <type_traits>
#include <condition_variable>//条件变量
namespace lrtlog{
using Functor = std::function<void(Buffer &)>;
enum AsyncType
{
ASYN_SAFE,
ASYN_UNSAFE
};
class Asynlooper{
public:
~Asynlooper() { stop(); }//漏了会报位置错误,因为有未知的线程没了
using ptr = std::shared_ptr<Asynlooper>;
// 启动了一个名为 _thread 的线程,线程入口是 thread_entry 函数,而 this 指针作为参数传递给 thread_entry
// 函数。这样,在异步线程中,thread_entry 函数就能够访问到
// 当前对象的成员变量和成员函数,因为它们都属于当前对象的范围。
Asynlooper(const Functor& cb)//
:_stop(false),
_thread(std::thread(&lrtlog::Asynlooper::thread_entry,this)),
_call_back(cb)
{
//std::cout << "3" <<std::endl;
}
void stop()
{
_stop=true;
_cond_con.notify_all();//唤醒所有工作线程
_thread.join();//等待工作线程退出
}
void push(const char* data,size_t len)
{
std::unique_lock<std::mutex> lock(_mutex);
// 一个可调用的对象或函数,它不带任何参数,
// 并返回一个可以计算为布尔值的值。
// 重复调用此值,直到其计算结果为 true。
if(_looper_type == AsyncType::ASYN_SAFE)
_cond_pro.wait(lock,[&](){return (_pro_buf.write_able_size() > len);});//避免资源耗尽型的安全模式
_pro_buf.push(data,len);
//唤醒消费者处理缓冲区的数据
_cond_con.notify_all();
}
void change_type(AsyncType looper_type){
_looper_type = looper_type;
}
// 对消费缓冲区的数据进行处理,处理完毕后,交换缓冲区
void thread_entry() // 线程入口
{
while (_stop != true)
{
std::unique_lock<std::mutex> lock(_mutex);
if (_stop && _pro_buf.empty())
break; // 防止阻塞
if (_looper_type == AsyncType::ASYN_SAFE)
_cond_con.wait(lock, [&]()
{ return !_stop || !_pro_buf.empty(); }); // 有则交换,进行数据消费或者要退出了就刷新缓冲区
_con_buf.swap(_pro_buf);
_cond_pro.notify_all();//换完了就快点去干活
//std::cout << "准备干活";
_call_back(_con_buf);
_con_buf.reset();
}
}
private:
//双缓冲区异步
AsyncType _looper_type;
std::atomic<bool> _stop;//停止标志
Buffer _pro_buf; //生产者
Buffer _con_buf; //消费者
std::mutex _mutex;
std::condition_variable _cond_pro;
std::condition_variable _cond_con;
std::thread _thread;//工作器对应的线程
lrtlog::Functor _call_back;
};
}
#endif
buffer.hpp
#ifndef __BUFFER__
#define __BUFFER__
#include <vector>
#include <string>
#include <assert.h>
#define DEFAULT_BUFFER_SIZE (1*1024*1024)//注意加括号
#define THRESHOLD_BUFFER_SIZE (10*1024*1024)
#define INCREAS_BUFFER_SIZE (10*1024*1024)
namespace lrtlog{
class Buffer {
public:
Buffer()
{
_len=1024*10;
_reader_idx = _write_idx = 0;
_buffer.resize(_len);
}
//向缓冲写
void push(const char* data,size_t len)
{
while( len > write_able_size() )//扩容
{
if (_len < THRESHOLD_BUFFER_SIZE)
{
_len = _len * 2;
_buffer.resize(_len);
}
else
{
_len = _len + INCREAS_BUFFER_SIZE;
_buffer.resize(_len);
}
}
std::copy(data,data+len,&_buffer[_write_idx]);
move_writer_idx(len);
}
//可读数据起始
const char* begin()
{
// a =&_buffer[_reader_idx];
//_read_p = &_buffer[_reader_idx];
return &_buffer[_reader_idx];
}
size_t read_able_size()
{
return _write_idx - _reader_idx;
}
size_t write_able_size()
{
return _buffer.size() -_write_idx;
}
//移动读写指针
void move_reader_idx(size_t len)
{
assert(len <= read_able_size());//相等是可以的
_reader_idx+=len;
}
void move_writer_idx(size_t len)
{
assert(len <= write_able_size());
_write_idx+=len;
}
//重置读写位置,初始化缓冲区
void reset()
{
_write_idx = _reader_idx = 0;
}
//buffer互相交换管理的指针
void swap(Buffer& buffer)
{
_buffer.swap(buffer._buffer);
std::swap(_reader_idx,buffer._reader_idx);
std::swap(_write_idx,buffer._write_idx);
}
bool empty()
{
if(read_able_size() == 0) return true;
return false;
}
private:
size_t _reader_idx;
size_t _write_idx;
size_t _len;
const char* _read_p;
std::vector<char> _buffer;
};
}
#endif
my_logger.hpp
#ifndef __LRT_LOGGER__
#define __LRT_LOGGER__
#include "log_level.hpp"
#include "log_sink.hpp"
#include "func.hpp"
#include "my_format.hpp"
#include "my_message.hpp"
#include "looper.hpp"
#include <atomic>
#include <mutex>
#include <stdarg.h>
#include <unordered_map>
namespace lrtlog{
class lrt_logger{
public:
using ptr = std::shared_ptr<lrt_logger>;
lrt_logger(std::string& logger_name,log_level::level level,formatter::ptr& formatter,std::vector<log_sink::ptr>& sinks):
_logger_name(logger_name),
_limit_level(level),
_fmt(formatter),
_sinks(sinks)
{}
virtual void final_log(const std::string& s) = 0 ;
const std::string& get_name()
{
return _logger_name;
}
void debug(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 1. 判断当前的日志是否达到了输出等级
if (log_level::level::DEBUG < _limit_level)
{
//std::cout << "debug log output fail" <<std::endl;
return;
}
// 2. 对格式化字符串fmt和可变参数进行字符串组织,得到日志消息的字符串
// char* res;
va_list ap;
va_start(ap, fmt);
std::string si;
si = log_init(log_level::DEBUG, _logger_name, line, fmt, ap);
// 3.日志落地
final_log(si);
}
void info(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 1. 判断当前的日志是否达到了输出等级
if (log_level::level::INFO < _limit_level)
{
return;
}
// 2. 对格式化字符串fmt和可变参数进行字符串组织,得到日志消息的字符串
// char* res;
va_list ap;
va_start(ap, fmt);
std::string si;
si = log_init(log_level::INFO, _logger_name, line, fmt, ap);
// 3.日志落地
final_log(si);
}
void warn(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 1. 判断当前的日志是否达到了输出等级
if (log_level::level::WARN < _limit_level)
{
return;
}
// 2. 对格式化字符串fmt和可变参数进行字符串组织,得到日志消息的字符串
// char* res;
va_list ap;
va_start(ap, fmt);
std::string si;
si = log_init(log_level::WARN, _logger_name, line, fmt, ap);
// 3.日志落地
final_log(si);
}
void error(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 1. 判断当前的日志是否达到了输出等级
if (log_level::level::ERROR < _limit_level)
{
return;
}
// 2. 对格式化字符串fmt和可变参数进行字符串组织,得到日志消息的字符串
// char* res;
va_list ap;
va_start(ap, fmt);
std::string si;
si = log_init(log_level::ERROR, _logger_name, line, fmt, ap);
// 3.日志落地
final_log(si);
}
void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 1. 判断当前的日志是否达到了输出等级
if (log_level::level::FATAL < _limit_level)
{
return;
}
// 2. 对格式化字符串fmt和可变参数进行字符串组织,得到日志消息的字符串
// char* res;
va_list ap;
va_start(ap, fmt);
std::string si;
si = log_init(log_level::FATAL, _logger_name, line, fmt, ap);
// 3.日志落地
final_log(si);
}
//私有成员继承无法访问
protected:
std::mutex _mutex;
std::string _logger_name;
std::atomic<log_level::level> _limit_level;//保证原子操作,避免访问一个小整形而频繁的加锁
formatter::ptr _fmt;
std::vector<log_sink::ptr> _sinks;//多个落地方向
std::string log_init(log_level::level level,const std::string& file,size_t line,const std::string& fmt, va_list& vl )
{
char* buf;
std::string msg;
int ret = vasprintf(&buf,fmt.c_str(),vl);
if(ret < 0)
{
std::cout << "vasprintf fail";
abort();
}
//替换msg中的内容
msg.assign(buf,ret);
free(buf);
//格式化消息
log_message lm(level,line,1000,_logger_name,file,msg);
std::string str;
str = _fmt->format(lm);
//std::cout << str ;
return str;
}
};
class synchronous_logger:public lrt_logger{
public:
synchronous_logger(std::string& logger_name, log_level::level level, formatter::ptr &formatter, std::vector<log_sink::ptr>& sinks)
: lrt_logger(logger_name, level, formatter, sinks)
{
//std::cout << "syn logger";
}
void final_log(const std::string& s) override
{
std::unique_lock<std::mutex> lock(_mutex);
if(_sinks.empty())
{
std::cout << "_sink empty";
return;
}
for(auto& sink: _sinks)
{
sink->log(s.c_str(),s.size());
}
}
};
enum logger_type
{
SYN_LOGGER,
ASY_LOGGER
};
class logger_builder{
public:
using ptr = std::shared_ptr<logger_builder>;
logger_builder():
_logger_type(logger_type::SYN_LOGGER),
_limit_level(log_level::level::DEBUG),
_looper_type(AsyncType::ASYN_SAFE){}
public:
void type_build(logger_type logger_type){ _logger_type = logger_type;}
void name_build(const std::string &logger_name) { _logger_name = logger_name; }
void unsafe_build(AsyncType looper_type){ _looper_type = looper_type;}
void level_build(lrtlog::log_level::level level) { _limit_level = level; }
void formatter_build(const std::string &pattern)
{
_formatter = std::make_shared<lrtlog::formatter>(pattern);
}
template <typename sink_type, typename... Args>
void sinks_build(Args &&...args)
{
log_sink::ptr sink_p = sink_factory::create<sink_type>(std::forward<Args>(args)...);
_sinks.push_back(sink_p);
}
virtual lrt_logger::ptr build() = 0;
protected:
std::string _logger_name;
logger_type _logger_type;
log_level::level _limit_level;
formatter::ptr _formatter;
std::vector<log_sink::ptr> _sinks;
lrtlog::AsyncType _looper_type;
};
class asynchronous_logger :public lrt_logger
{
public:
asynchronous_logger(std::string& logger_name,
log_level::level level,
formatter::ptr& formatter,
std::vector<log_sink::ptr> &sinks,
AsyncType looper_type) : lrt_logger(logger_name, level, formatter, sinks),
_looper(std::make_shared<Asynlooper>(std::bind(&asynchronous_logger::reallog, this, std::placeholders::_1)))
{
//_looper->change_type(looper_type);
//std::cout<< "2";
}
void final_log(const std::string& s){
_looper->push(s.c_str(),s.size());
}
void reallog(Buffer& buffer)
{
//std:: cout << "4" ;
if(_sinks.empty())
{
std::cout << "asyn _sink empty";
return;
}
for(auto& sink: _sinks)
{
sink->log(buffer.begin(),buffer.read_able_size());
}
}
private:
Asynlooper::ptr _looper;
};
//忘记加public就会出现父类public函数的情况
class local_logger_build :public logger_builder{
public:
lrt_logger::ptr build() override
{
//std::cout << "254 line";
//assert(_logger_name.empty()==false);
if(_formatter.get()==nullptr) _formatter = std::make_shared<formatter>();
if(_sinks.empty()) sinks_build<stdout_sink>();
if(_logger_type == logger_type::SYN_LOGGER){
std::cout << std::endl;
return std::make_shared<synchronous_logger>(_logger_name,_limit_level,_formatter,_sinks);
}
else
{
std::cout << "asyc" <<std::endl;
return std::make_shared<asynchronous_logger>(_logger_name,_limit_level,_formatter,_sinks,_looper_type);//传给looper了,所以asynchronous_logger可以没有looper_type,也体现了此处已经解耦
}
std::cout << "nullptr "<< std::endl;
return nullptr;
}
};
class logger_manager{
public:
static logger_manager& get_instance()
{
static logger_manager eton;//单例对象
return eton;
}
void add_logger(lrt_logger::ptr& logger)
{
if(hash_logger(logger->get_name()) == true) return;//已经存在则不创建
std::unique_lock<std::mutex> lock(_mutex);
_loggers.insert(std::make_pair(logger->get_name(),logger));//对象放在堆上,就要用指针,也就是对象指针->函数;放在栈上,就对象.函数
}
bool hash_logger(const std::string& name)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _loggers.find(name);
if( it == _loggers.end())
{
return false;
}
return true;
}
lrt_logger::ptr get_logger(std::string logger_name)//失败返回空
{
std::unique_lock<std::mutex> lock(_mutex);
std::cout << "OK,IN 306" << std::endl;
auto it = _loggers.find(logger_name);
if (it == _loggers.end())
{
return nullptr;
}
return it->second;
}
lrt_logger::ptr get_rootlogger()
{
return _root_logger;
}
private:
logger_manager()
{
std::unique_ptr<lrtlog::local_logger_build> logger_build(new lrtlog::local_logger_build());//不可使用全局的建造者
logger_build->name_build("root_logger");
_root_logger = logger_build->build();
_loggers.insert(std::make_pair(_root_logger->get_name(),_root_logger));
}
private:
lrt_logger::ptr _root_logger;
std::mutex _mutex;
std::unordered_map<std::string,lrtlog::lrt_logger::ptr> _loggers;
};
class global_logger_build :public logger_builder{
public:
lrt_logger::ptr build() override
{
lrt_logger::ptr logger;
//std::cout << "254 line";
//assert(_logger_name.empty()==false);
if(_formatter.get()==nullptr) _formatter = std::make_shared<formatter>();
if(_sinks.empty()) sinks_build<stdout_sink>();
if(_logger_type == logger_type::SYN_LOGGER){
std::cout << std::endl;
logger = std::make_shared<synchronous_logger>(_logger_name,_limit_level,_formatter,_sinks);
}
else
{
std::cout << "asyc" <<std::endl;
logger = std::make_shared<asynchronous_logger>(_logger_name,_limit_level,_formatter,_sinks,_looper_type);//传给looper了,所以asynchronous_logger可以没有looper_type,也体现了此处已经解耦
}
logger_manager::get_instance().add_logger(logger);//添加到单例对象进行管理
//std::cout << "nullptr "<< std::endl;
return logger;
}
};
}
#endif
lrt_log.h
#ifndef __LRT_LOG__
#define __LRT_LOG__
#include "lrt_logger.hpp"
#define debug(fmt, ...) debug(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define info(fmt, ...) info(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define warn(fmt, ...) warn(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define error(fmt, ...) error(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define fatal(fmt, ...) fatal(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG(fmt, ...) lrtlog::get_rootlogger()->debug(fmt, ##__VA_ARGS__)
#define INFO(fmt, ...) lrtlog::get_rootlogger()->info(fmt, ##__VA_ARGS__)
#define WARN(fmt, ...) lrtlog::get_rootlogger()->warn(fmt, ##__VA_ARGS__)
#define ERROR(fmt, ...) lrtlog::get_rootlogger()->error(fmt, ##__VA_ARGS__)
#define FATAL(fmt, ...) lrtlog::get_rootlogger()->fatal(fmt, ##__VA_ARGS__)
namespace lrtlog{
lrt_logger::ptr get_logger(const std::string& name)
{
return lrtlog::logger_manager::get_instance().get_logger(name);
}
lrt_logger::ptr get_rootlogger()
{
return lrtlog::logger_manager::get_instance().get_rootlogger();
}
}
#endif