运用多设计模式的同步&异步滚动日志系统

还有使用样例代码

和扩展样例代码(test.cc以及写的很详细了,后续补充)

以及性能测试代码

test.cc

cpp 复制代码
// #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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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,&lt);
            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, &lt);
            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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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 
相关推荐
阿史大杯茶几秒前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
C++忠实粉丝9 分钟前
计算机网络socket编程(3)_UDP网络编程实现简单聊天室
linux·网络·c++·网络协议·计算机网络·udp
黑客Ela16 分钟前
网络安全中常用浏览器插件、拓展
网络·安全·web安全·网络安全·php
Chris _data18 分钟前
二叉树oj题解析
java·数据结构
დ旧言~30 分钟前
【高阶数据结构】图论
算法·深度优先·广度优先·宽度优先·推荐算法
我们的五年35 分钟前
【Linux课程学习】:进程描述---PCB(Process Control Block)
linux·运维·c++
张彦峰ZYF35 分钟前
投资策略规划最优决策分析
分布式·算法·金融
qdprobot36 分钟前
ESP32桌面天气摆件加文心一言AI大模型对话Mixly图形化编程STEAM创客教育
网络·人工智能·百度·文心一言·arduino
The_Ticker1 小时前
CFD平台如何接入实时行情源
java·大数据·数据库·人工智能·算法·区块链·软件工程
程序猿阿伟1 小时前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链