【6】基于多设计模式下的同步&异步日志系统-代码设计

8.4 日志输出格式化类设计

日志格式化(Formatter)类主要负责格式化⽇志消息。其主要包含以下内容

• pattern成员:保存⽇志输出的格式字符串。
◦ %d ⽇期
◦ %T 缩进
◦ %t 线程id
◦ %p ⽇志级别
◦ %c ⽇志器名称
◦ %f ⽂件名
◦ %l ⾏号
◦ %m ⽇志消息
◦ %n 换⾏
• std::vector< FormatItem::ptr> items成员:⽤于按序保存格式化字符串对应的⼦格式化对象。

FormatItem类主要负责⽇志消息⼦项的获取及格式化。其包含以下⼦类

• MsgFormatItem :表⽰要从LogMsg中取出有效⽇志数据
• LevelFormatItem :表⽰要从LogMsg中取出⽇志等级
• NameFormatItem :表⽰要从LogMsg中取出⽇志器名称
• ThreadFormatItem :表⽰要从LogMsg中取出线程ID
• TimeFormatItem :表⽰要从LogMsg中取出时间戳并按照指定格式进⾏格式化
• CFileFormatItem :表⽰要从LogMsg中取出源码所在⽂件名
• CLineFormatItem :表⽰要从LogMsg中取出源码所在⾏号
• TabFormatItem :表⽰⼀个制表符缩进
• NLineFormatItem :表⽰⼀个换⾏
• OtherFormatItem :表⽰⾮格式化的原始字符串

⽰例:“[%d{%H:%M:%S}] %m%n”

pattern = "[%d{%H:%M:%S}] %m%n"
items = {
 {OtherFormatItem(), "["},
 {TimeFormatItem(), "%H:%M:%S"},
 {OtherFormatItem(), "]"},
 {MsgFormatItem (), ""},
 {NLineFormatItem (), ""}
}
LogMsg msg = {
 size_t _line = 22;
 size_t _ctime = 12345678;
 std::thread::id _tid = 0x12345678;
 std::string _name = "logger";
 std::string _file = "main.cpp";
 std::string _payload = "创建套接字失败";
 LogLevel::value _level = ERROR;
};

格式化的过程其实就是按次序从Msg中取出需要的数据进⾏字符串的连接的过程。
最终组织出来的格式化消息: “[22:32:54] 创建套接字失败\n”
代码实现:

#ifndef _MY_FMT_H_
#define _MY_FMT_H_

#include "util.hpp"
#include "level.hpp"
#include "message.hpp"
#include <ctime>
#include <vector>
#include <cassert>
#include <sstream>

namespace log
{
    // 抽象格式化子项基类
    class FormatItem
    {
    public: 
        using ptr = std::shared_ptr<FormatItem>;
        virtual void format(std::ostream &out,const LogMsg &msg) = 0;
    };

    // 派生格式化子项子类---消息,等级,时间,文件名,行号,线程ID,日志器名,制表符,换行,其他

    class MsgFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out,const LogMsg &msg) override
        {
            out << msg._payload;
        }
    };

    class LevelFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMsg &msg) override
        {
            out << LogLevel::toString(msg._level);
        }
    };

    class TimeFormatItem : public FormatItem
    {
    public:
        TimeFormatItem(const std::string &fmt = "%H:%M:%S") : _time_fmt(fmt)
        {
        }
        void format(std::ostream &out, const LogMsg &msg) override
        {
            //     /*  struct tm {
            //    int tm_sec;         /* seconds */
            //    int tm_min;         /* minutes */
            //    int tm_hour;        /* hours */
            //    int tm_mday;        /* day of the month */
            //    int tm_mon;         /* month */
            //    int tm_year;        /* year */
            //    int tm_wday;        /* day of the week */
            //    int tm_yday;        /* day in the year */
            //    int tm_isdst;       /* daylight saving time */
            //    }; */
            out << LogLevel::toString(msg._level);
            struct tm t;
            localtime_r(&msg._ctime, &t); // 获取时间结构体
            char tmp[32] = {0};
            strftime(tmp, 31, _time_fmt.c_str(), &t); // 时间按照_time_fmt格式变成tmp字符串
            out << tmp;
        }

    private:
        std::string _time_fmt; //%H:%M:%S
    };

    class FileFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out,const LogMsg &msg) override
        {
            out << msg._file;
        }
    };

    class LineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg._line;
        }
    };

    class ThreadFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out,const LogMsg &msg) override
        {
            out << msg._tid;
        }
    };
    class LoggerFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out,const LogMsg &msg) override
        {
            out << msg._logger;
        }
    };
    class TabFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out,const LogMsg &msg) override
        {
            out << "\t";
        }
    };
    class NLineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out,const LogMsg &msg) override
        {
            out << "\n";
        }
    };
    class OthrerFormatItem : public FormatItem
    {
    public:
        OthrerFormatItem(const std::string &str) : _str(str)
        {
        }
        void format(std::ostream &out, const LogMsg &msg) override
        {
            out << _str;
        }

    private:
        std::string _str;
    };

    /*
        %d  表示日期,包含子格式{%H:%M:%S}
        %t  表示线程ID
        %c  表示日志器名称
        %f  表示源码文件名
        %l  表示源码行号
        %p  表示日志级别
        %T  表示制表符缩进
        %m  表示主体消息
        %n  表示换行
    */
    class Formatter
    {
    public:
        Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%f:%l][%p]%T%m%n") : _pattern(pattern)
        {
            assert(parsePattern());
        }

       
        std::string format(const LogMsg &msg)
        {
            std::stringstream ss;
            format(ss, msg);
            return ss.str();//返回成功的日志信息
        }
        // 对msg进行格式化
        void format(std::ostream &out,const LogMsg &msg)
        {
            for (auto &item : _items)
            {
                item->format(out, msg);//利用多态,将派生类分析的日志信息片段插入到字符串输入流out中;
            }
        }
 private:
        //序列化字符串解析
        bool parsePattern()
        {
            /*1.对格式化规则字符串进行解析
                abcde[%d{%H:%M:%S}][%p]%T%m%n
                std::
            */
            std::vector<std::pair<std::string, std::string>> fmt_order;
            size_t pos = 0;
            std::string key, val;
            while (pos < _pattern.size())
            {
                // 处理原始字符串---判断是否是%,否则就是原始字符
                
                if (_pattern[pos] != '%')
                {
                    val.push_back(_pattern[pos++]);
                    continue;
                }

                // 能走下来就代表pos位置就是%字符
                if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%')
                {
                    // 说明是双%,此种情况处理为一个%字符。
                    val.push_back('%');
                    pos += 2;
                    continue;
                }

                // 走到这就说明%后面是一个格式化字符,代表原始字符串处理完毕。
                if (val.empty() == false)
                {
                    fmt_order.push_back(std::make_pair("", val));
                    val.clear();
                }

                //这时候的pos指向的是%位置,是格式化字符的处理。
                pos += 1;//这一步之后,POS指向格式化字符位置
                if(pos==_pattern.size())
                {
                    std::cout << "%之后,没有对应的格式化字符!\n";
                    return false; 
                }

                key = _pattern[pos++];//这时候pos指向格式化字符后面的位置。
                

                bool error_flag = false;
                if (pos<_pattern.size()&&_pattern[pos] == '{')
                {
                    error_flag = true;
                    // 这里是子串


                    pos += 1; // 这时候pos指向子规则的起始位置;
                    while (pos < _pattern.size() && _pattern[pos] != '}')
                    {
                        val.push_back(_pattern[pos++]);
                    }

                    // 走到了末尾跳出了循环,则代表没有遇到“}”,代表格式是错误的
                    if (pos == _pattern.size())
                    {
                        std::cout << "子规则{}匹配出错!\n";
                        return false; // 没有找到{
                    }
                    pos += 1; // 因为这时候pos指向的是}位置,向后走一步,走到了下次处理的新位置。
                }
                fmt_order.push_back(std::make_pair(key,val));
                key.clear();
                val.clear();
            }
            // 2.根据解析得到的数据初始化格式化子项数组成员
                for(auto& it:fmt_order)
                {
                    _items.push_back(createItem(it.first,it.second));
                }
                return true;
        }

   
        // 根据不同的格式化字符创建不同的格式化子项对象
        FormatItem::ptr createItem(const std::string &key, const std::string &val)
        {
            if (key == "d")
                return std::make_shared<TimeFormatItem>(val);
            if (key == "t")
                return std::make_shared<ThreadFormatItem>();
            if (key == "c")
                return std::make_shared<LoggerFormatItem>();
            if (key == "f")
                return std::make_shared<FileFormatItem>();
            if (key == "l")
                return std::make_shared<LineFormatItem>();
            if (key == "p")
                return std::make_shared<LevelFormatItem>();
            if (key == "T")
                return std::make_shared<TabFormatItem>();
            if (key == "m")
                return std::make_shared<MsgFormatItem>();
            if (key == "n")
                return std::make_shared<NLineFormatItem>();
            if(key.empty())
                return std::make_shared<OthrerFormatItem>(val);
            std::cout<<"没有对应的格式化字符:%"<<key<<std::endl;
            abort();
            return FormatItem::ptr();
        }

    private:
        std::string _pattern; // 格式化规则字符串
        std::vector<FormatItem::ptr> _items;//_items元素为指向FormatItem类型的智能指针;
    };
}

#endif
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值