日志系统——格式化模块

日志系统的日志输出格式可以有用户自定义传入,在进行格式化时,需要根据用户期望的格式按一定的顺序进行格式化,于是格式化模块需要在具体的事件通过具体的对象完成具体位置的格式化操作。

子格式化抽象基类

一条日志消息涉及到的元素有很多,每一个属性都需要通过具体的对象进行构建,这些对象都具有共同的特征,那就是从属于格式化类,基于设计原则中的面向接口设计,需要率先定义抽象基类,便于后续所有由此基类派生而出的具体类通过基类的指针或引用实现具体方法

namespace formatstep //*子格式化类
{
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual ~FormatItem() {}
        virtual void format(std::ostream &out, LogMsg &msg) = 0;
    };
}

FormatItem的派生类必须重写基类的format虚函数来实现具体的格式化操作,每一个子格式化类所需要处理的任务大体上都是相似的——从LogMsg对象msg中去除所需要的属性,将其进行描述之后加入ostream对象out中(一般是stringstream对象)

子格式化派生类

根据一条标准日志可能涉及到的属性,需要先涉及每一个日志属性所对应的占位符

  • %d 日期
  • %T 缩进
  • %t 线程id
  • %p 日志级别
  • %c 日志器名称
  • %f 文件名
  • %l 行号
  • %m 消息
  • %n 换行
  • %% 百分号
    根据每一个占位符设置相应的子格式化对象,对于非占位符统一由一个子格式化对象进行处理
class DateFormatItem : public FormatItem //*时间格式化
    {
    private:
        std::string _format;//时间具有子格式,用户可以按需传入具体的时间格式
    public:
        DateFormatItem(const std::string &format = "%H:%M:%S");
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class TabFormatItem : public FormatItem //*缩进格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class ThreadFormatItem : public FormatItem //*线程id格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class LevelFormatItem : public FormatItem //*日志等级格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class LoggerFormatItem : public FormatItem //*日志器格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class FileFormatItem : public FormatItem //*文件名格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class LineFormatItem : public FormatItem //*行号格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class PayloadFormatItem : public FormatItem //*正文格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class EndFormatItem : public FormatItem //*换行格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class PercentFormatItem : public FormatItem //*%格式化
    {
    public:
        void format(std::ostream &out, LogMsg &msg) override;
    };
    class OtherFormatItem : public FormatItem //*其他字符格式化,对于非占位符不做任何改变
    {
    private:
        std::string _str;
    public:
        OtherFormatItem(const std::string &str);
        void format(std::ostream &out, LogMsg &msg) override;
    };

此处仅展示DateFormatItem的具体实现,其他子格式化类具体实现见https://gitee.com/chxchenhaixiao/log/blob/master/Log/format.cc

formatstep::DateFormatItem::DateFormatItem(const std::string &format) : _format(format) {}
void formatstep::DateFormatItem::format(std::ostream &out, LogMsg &msg)
{
    time_t timestamp = msg._time;
    struct tm *t = localtime(&timestamp);
    char tmp[32] = {0};
    strftime(tmp, sizeof(tmp)-1, _format.c_str(), t); 
    		//strftime可以将struct tm对象的属性按照指定的格式进行格式化,具体占位符可以参考strftime文档
    out << tmp;
}

格式化类

定义子格式化类之后,需要一个格式化类来对其进行管理(指挥者的角色),该类需要对用户传入的格式化标准进行解析,解析过程中实例化相应的子格式化对象并尾插入一个数组,便于后续按序格式化

class Formater
{
private:
    std::string _style;             //*用户期望的输出格式
    std::vector<formatstep::FormatItem::ptr> _items; //*子格式化数组
public:
    Formater(const std::string &style = "[%d{%Y-%m-%d %H:%M:%S}][%p][%c][%t][%f:%l]%T%m%n"); 
    //提供一个默认格式,并且规定时间的具体格式必须使用{}修饰
    void formatMsg(std::ostream &out, LogMsg &msg); //*对日志消息进行格式化(从数组中按需取出子格式化对象并调用其format方法)
private:
    bool parseStyle();         //*对用户传入的格式进行解析,必须返回true,否则无法进行下一步操作
    formatstep::FormatItem::ptr createItem(const std::string &key, const std::string &val = ""); 
    //*构建负责子部分格式化的对象
};

具体实现见https://gitee.com/chxchenhaixiao/log/blob/master/Log/format.cc

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shall#

你的鼓励将是我前进的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值