【服务器】Log日志模块

log日志模块

以下是从sylar服务器中学的,对其的复习;

1.日志器Logger

日志器,包含一个日志格式器m_formatter,一个root Logger,N个LogAppender
提供日志写入方法。根据日志器的配置格式和内容。将日志写到对应的地方

  • 智能指针share_ptr的使用
  • std::enable_shared_from_this类
 class Logger : public std::enable_shared_from_this<Logger> {
 friend class LoggerManager;
 public:
     typedef std::shared_ptr<Logger> ptr;
     typedef Spinlock MutexType;

     Logger(const std::string& name = "root");
     // 写入日志,指定日志级别
     void log(LogLevel::Level level, LogEvent::ptr event);
     // 写debug日志
     void debug(LogEvent::ptr event);
     // 写info日志
     void info(LogEvent::ptr event);
     // 写warn日志
     void warn(LogEvent::ptr event);
     // 写error日志
     void error(LogEvent::ptr event);
     // 写fatal日志
     void fatal(LogEvent::ptr event);
     // 添加appender
     void addAppender(LogAppender::ptr appender);
     // 删除appender
     void delAppender(LogAppender::ptr appender);
     // 清空appender
     void clearAppenders();

     LogLevel::Level getLevel() const { return m_level;}
     // 设置日志级别
     void setLevel(LogLevel::Level val) { m_level = val;}
     // 获取日志名称
     const std::string& getName() const { return m_name;}
     // 设置日志格式
     void setFormatter(LogFormatter::ptr val);
     // 设置文本日志格式
     void setFormatter(const std::string& val);

     LogFormatter::ptr getFormatter();
     // 转成Yaml格式的配置文本
     std::string toYamlString();
 private:
     std::string m_name;
     //日志级别,低于该级别不会输出
     LogLevel::Level m_level;
     MutexType m_mutex;
     //appender集合
     std::list<LogAppender::ptr> m_appenders;
     //日志格式
     LogFormatter::ptr m_formatter;
     //主日志器,如果当前日志未定义,使用主日志器输出
     Logger::ptr m_root;
 };

1.1 关键实现

//构造函数:
Logger::Logger(const std::string& name)
    :m_name(name)
    ,m_level(LogLevel::DEBUG) {
    m_formatter.reset(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
}
//log:遍历m_appenders输出
void Logger::log(LogLevel::Level level, LogEvent::ptr event) {
    if(level >= m_level) {
        auto self = shared_from_this();
        MutexType::Lock lock(m_mutex);
        if(!m_appenders.empty()) {
            for(auto& i : m_appenders) {
                i->log(self, level, event);
            }
        } else if(m_root) {
            m_root->log(level, event);
        }
    }
}

2.日志输出地LogAppender

日志落地点抽象。目前只要实现了输出到控制台(StdoutLogAppender)和输出到文件(FileLogAppender),LogAppender可以拥有自己的LogFormat。
一个日志器,可以对应多个LogAppender。也就是说写一条日志,可以落到多个输出,并且每个输出的格式都可以不一样。
Appender有单独的日志级别,可以自定义不同级别的日志,输出到不同的Appender,常用于将错误日志统一输出到一个地方。
以后可以通过扩展LogAppender,实现向日志服务器写日志(利用socket)

  • yaml-cpp使用
  • 线程同步、线程安全
class LogAppender {
 friend class Logger;
 public:
     typedef std::shared_ptr<LogAppender> ptr;
     typedef Spinlock MutexType;

     virtual ~LogAppender() {}
     //将日志输出到对应的落地点
     virtual void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
     //将日志落地点输出成Yaml格式的配置
     virtual std::string toYamlString() = 0;

     void setFormatter(LogFormatter::ptr val);

     LogFormatter::ptr getFormatter();

     LogLevel::Level getLevel() const { return m_level;}

     void setLevel(LogLevel::Level val) { m_level = val;}
 protected:
     LogLevel::Level m_level = LogLevel::DEBUG;
     bool m_hasFormatter = false;
     MutexType m_mutex;
     //日志格式器
     LogFormatter::ptr m_formatter;
 };

  //输出到控制台
  class StdoutLogAppender : public LogAppender {
 public:
     typedef std::shared_ptr<StdoutLogAppender> ptr;
     void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override;
     std::string toYamlString() override;
 };

 //输出到日志文件
 class FileLogAppender : public LogAppender {
 public:
     typedef std::shared_ptr<FileLogAppender> ptr;
     FileLogAppender(const std::string& filename);
     void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override;
     std::string toYamlString() override;

     bool reopen();
 private:
     //文件名
     std::string m_filename;
     std::ofstream m_filestream;
     uint64_t m_lastTime = 0;
 };

2.1关键实现

//输出到标准输出
void StdoutLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
    if(level >= m_level) {
        MutexType::Lock lock(m_mutex);
        std::cout << m_formatter->format(logger, level, event);
    }
}
//输出到文件
void FileLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
    if(level >= m_level) {
        uint64_t now = time(0);
        if(now != m_lastTime) {
            reopen();
            m_lastTime = now;
        }
        MutexType::Lock lock(m_mutex);
        if(!(m_filestream << m_formatter->format(logger, level, event))) {
            std::cout << "error" << std::endl;
        }
    }
}

3.日志格式器LogFormat

日志格式器,执行日志格式化,负责日志格式的初始化。
解析日志格式,将用户自定义的日志格式,解析为对应的FormatItem。

  • 设计模式:策略模式
  • 纯虚函数
class LogFormatter {
public:
    /**
     * @brief 构造函数
     * @param[in] pattern 格式模板
     * @details 
     *  %m 消息
     *  %p 日志级别
     *  %r 累计毫秒数
     *  %c 日志名称
     *  %t 线程id
     *  %n 换行
     *  %d 时间
     *  %f 文件名
     *  %l 行号
     *  %T 制表符
     *  %F 协程id
     *  %N 线程名称
     *
     *  默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
     */
    LogFormatter(const std::string& pattern);
    /**
     * 返回格式化日志文本
     * @param[in] logger 日志器
     * @param[in] level 日志级别
     * @param[in] event 日志事件
     */
    std::string format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);
    std::ostream& format(std::ostream& ofs, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);
public:
    /**
     * 日志内容项格式化
     */
    class FormatItem {
    public:
        typedef std::shared_ptr<FormatItem> ptr;
        virtual ~FormatItem() {}
        /**
         * @brief 格式化日志到流
         * @param[in, out] os 日志输出流
         * @param[in] logger 日志器
         * @param[in] level 日志等级
         * @param[in] event 日志事件
         */
        //纯虚函数
        virtual void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
    };

    /**
     * 初始化,解析日志模板, 做m_pattern的解析
     */
    void init();
private:
    /// 日志格式模板
    std::string m_pattern;
    /// 日志格式解析后格式
    std::vector<FormatItem::ptr> m_items;
    /// 是否有错误
    bool m_error = false;
};

3.1关键实现

/** [%m] 消息*/
class MessageFormatItem : public LogFormatter::FormatItem {
public:
    MessageFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getContent();
    }
};
/** [%p] 日志级别 */
class LevelFormatItem : public LogFormatter::FormatItem {
public:
    LevelFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << LogLevel::ToString(level);
    }
};

/** [%r] 累计毫秒数 */
class ElapseFormatItem : public LogFormatter::FormatItem {
public:
    ElapseFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getElapse();
    }
};

/** [%c] 日志名称 */
class NameFormatItem : public LogFormatter::FormatItem {
public:
    NameFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getLogger()->getName();
    }
};

/** [%t] 线程id */
class ThreadIdFormatItem : public LogFormatter::FormatItem {
public:
    ThreadIdFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getThreadId();
    }
};

/** [%F] 协程id */
class FiberIdFormatItem : public LogFormatter::FormatItem {
public:
    FiberIdFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getFiberId();
    }
};

/** [%N] 线程名称 */
class ThreadNameFormatItem : public LogFormatter::FormatItem {
public:
    ThreadNameFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getThreadName();
    }
};
     
/** [%d] 时间 */
class DateTimeFormatItem : public LogFormatter::FormatItem {
public:
    DateTimeFormatItem(const std::string& format = "%Y-%m-%d %H:%M:%S")
        :m_format(format) {
        if(m_format.empty()) {
            m_format = "%Y-%m-%d %H:%M:%S";
        }
    }

    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        struct tm tm;
        time_t time = event->getTime();
        //localtime_r 用于linux平台下获取系统时间
        localtime_r(&time, &tm);
        char buf[64];
        //strftime 根据format中定义的格式化规则,格式化结构tm表示的时间,并把它存储在buf中。
        strftime(buf, sizeof(buf), m_format.c_str(), &tm);
        os << buf;
    }
private:
    std::string m_format;
};

/** [%f] 文件名 */
class FilenameFormatItem : public LogFormatter::FormatItem {
public:
    FilenameFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getFile();
    }
};

/** [%l] 行号 */
class LineFormatItem : public LogFormatter::FormatItem {
public:
    LineFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getLine();
    }
};

/** [%n] 换行 */
class NewLineFormatItem : public LogFormatter::FormatItem {
public:
    NewLineFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << std::endl;
    }
};

class StringFormatItem : public LogFormatter::FormatItem {
public:
    StringFormatItem(const std::string& str)
        :m_string(str) {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << m_string;
    }
private:
    std::string m_string;
};

/** [%T] 制表符 */
class TabFormatItem : public LogFormatter::FormatItem {
public:
    TabFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << "\t";
    }
};

/** 构造函数,先初始化解析parttern */
LogFormatter::LogFormatter(const std::string& pattern)
    :m_pattern(pattern) {
    init();
}

/** 执行每个m_iterms内的子类中的format,让其内容都添加到ss中 */
std::string LogFormatter::format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
    std::stringstream ss;
    for (auto& i : m_items) {
        i->format(ss, logger, level, event);
    }
    return ss.str();
    }

/** 与上一个函数功能类似,只是输出到ostream */
std::ostream& LogFormatter::format(std::ostream& ofs, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
    for (auto& i : m_items) {
        i->format(ofs, logger, level, event);
    }
    return ofs;
    }

// %xxx %xxx{xxx} %%
// "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
// 解析日志格式
    void LogFormatter::init() {
    //str, format, type
    std::vector<std::tuple<std::string, std::string, int> > vec;
    std::string nstr;
    for (size_t i = 0; i < m_pattern.size(); ++i) {
        if (m_pattern[i] != '%') {
            nstr.append(1, m_pattern[i]);
            continue;
        }

        if ((i + 1) < m_pattern.size()) {
            if (m_pattern[i + 1] == '%') {
                nstr.append(1, '%');
                continue;
            }
        }

        size_t n = i + 1;
        int fmt_status = 0;
        size_t fmt_begin = 0;

        std::string str;
        std::string fmt;
        while (n < m_pattern.size()) {
            if (!fmt_status && (!isalpha(m_pattern[n]) && m_pattern[n] != '{'
                && m_pattern[n] != '}')) {
                str = m_pattern.substr(i + 1, n - i - 1);
                break;
            }
            if (fmt_status == 0) {
                if (m_pattern[n] == '{') {
                    str = m_pattern.substr(i + 1, n - i - 1);
                    fmt_status = 1; //解析格式
                    fmt_begin = n;
                    ++n;
                    continue;
                }
            }
            else if (fmt_status == 1) {
                if (m_pattern[n] == '}') {
                    fmt = m_pattern.substr(fmt_begin + 1, n - fmt_begin - 1);
                    fmt_status = 0;
                    ++n;
                    break;
                }
            }
            ++n;
            if (n == m_pattern.size()) {
                if (str.empty()) {
                    str = m_pattern.substr(i + 1);
                }
            }
        }

        if (fmt_status == 0) {
            if (!nstr.empty()) {
                vec.push_back(std::make_tuple(nstr, std::string(), 0));
                nstr.clear();
            }
            vec.push_back(std::make_tuple(str, fmt, 1));
            i = n - 1;
        }
        else if (fmt_status == 1) {
            std::cout << "pattern parse error: " << m_pattern << " - " << m_pattern.substr(i) << std::endl;
            m_error = true;
            vec.push_back(std::make_tuple("<<pattern_error>>", fmt, 0));
        }
    }

    if (!nstr.empty()) {
        vec.push_back(std::make_tuple(nstr, "", 0));
    }
    static std::map<std::string, std::function<FormatItem::ptr(const std::string& str)> > s_format_items = {
    #define XX(str, C) \
        {#str, [](const std::string& fmt) { return FormatItem::ptr(new C(fmt));}}

        XX(m, MessageFormatItem),           //m:消息
        XX(p, LevelFormatItem),             //p:日志级别
        XX(r, ElapseFormatItem),            //r:累计毫秒数
        XX(c, NameFormatItem),              //c:日志名称
        XX(t, ThreadIdFormatItem),          //t:线程id
        XX(n, NewLineFormatItem),           //n:换行
        XX(d, DateTimeFormatItem),          //d:时间
        XX(f, FilenameFormatItem),          //f:文件名
        XX(l, LineFormatItem),              //l:行号
        XX(T, TabFormatItem),               //T:Tab
        XX(F, FiberIdFormatItem),           //F:协程id
        XX(N, ThreadNameFormatItem),        //N:线程名称
    #undef XX
    };

    for (auto& i : vec) {
        if (std::get<2>(i) == 0) {
            m_items.push_back(FormatItem::ptr(new StringFormatItem(std::get<0>(i))));
        }
        else {
            auto it = s_format_items.find(std::get<0>(i));
            if (it == s_format_items.end()) {
                m_items.push_back(FormatItem::ptr(new StringFormatItem("<<error_format %" + std::get<0>(i) + ">>")));
                m_error = true;
            }
            else {
                m_items.push_back(it->second(std::get<1>(i)));
            }
        }
    }
}

4.日志事件LogEvent

日志事件的封装,将要写的日志,填充到LogEvent中。填充完毕之后,写入到对应的logger中

  • 变长模板va_list
class LogEvent{

public:
    void format(const char* fmt, ...);
    void format(const char* fmt, va_list al);

private:
    /// 文件名
    const char* m_file = nullptr;
    /// 行号
    int32_t m_line = 0;
    /// 程序启动开始到现在的毫秒数
    uint32_t m_elapse = 0;
    /// 线程ID
    uint32_t m_threadId = 0;
    /// 协程ID
    uint32_t m_fiberId = 0;
    /// 时间戳
    uint64_t m_time = 0;
    /// 线程名称
    std::string m_threadName;
    /// 日志内容流
    std::stringstream m_ss;
    /// 日志器
    std::shared_ptr<Logger> m_logger;
    /// 日志等级
    LogLevel::Level m_level;
}

4.1关键实现

LogEvent::LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level
        ,const char* file, int32_t line, uint32_t elapse
        ,uint32_t thread_id, uint32_t fiber_id, uint64_t time
        ,const std::string& thread_name)
        :m_file(file)
        ,m_line(line)
        ,m_elapse(elapse)
        ,m_threadId(thread_id)
        ,m_fiberId(fiber_id)
        ,m_time(time)
        ,m_threadName(thread_name)
        ,m_logger(logger)
        ,m_level(level) {
    }

void LogEvent::format(const char* fmt, ...) {
    //va_start、va_end成对出现
    va_list al;  //val_list 用于获取不确定个数的参数
    va_start(al, fmt);
    format(fmt, al);
    va_end(al);
}

/** 将内容都添加到m_ss中 */
void LogEvent::format(const char* fmt, va_list al) {
    char* buf = nullptr;
    int len = vasprintf(&buf, fmt, al);
    if(len != -1) {
        m_ss << std::string(buf, len);
        free(buf);
    }
}

5.日志级别LogLevel

  • enum
  • static
  • 宏定义
class LogLevel{
public:
    enum Level {
        /// 未知级别
        UNKNOW = 0,
        /// DEBUG: 记录有关通过系统的流量的详细信息,应用程序记录的大多数为 DEBUG;
        DEBUG = 1,
        /// INFO:运行时事件(启动/关闭);
        INFO = 2,
        /// WARN:使用已弃用的 API、API 使用不当、“几乎”错误、其他不合需要或意外但不一定“错误”的运行时情况;
        WARN = 3,
        /// ERROR:其他运行时错误或意外情况
        ERROR = 4,
        /// FATAL:导致提前终止的严重错误
        FATAL = 5
    };
    /** Level转字符串 */ 
    static const char* ToString(LogLevel::Level level);
    /** 字符串转Level */
    static LogLevel::Level FromString(const std::string& str);
};

6.日志管理器LoggerManager

管理所有的日志器,并且可以通过解析Yaml配置,动态创建或修改日志器相关的内容(日志级别,日志格式,输出落地点等等)

  • 设计模式:单例模式
 class LoggerManager {
 public:
     typedef Spinlock MutexType;
     LoggerManager();
     // 获取名称为name的日志器
     // 如果name不存在,则创建一个,并使用root配置
     Logger::ptr getLogger(const std::string& name);

     void init();

     Logger::ptr getRoot() const { return m_root;}
     // 转成yaml格式的配置
     std::string toYamlString();
 private:
     MutexType m_mutex;
     // 所有日志器
     std::map<std::string, Logger::ptr> m_loggers;
     // 主日志器(默认日志器)
     Logger::ptr m_root;
 };

typedef sylar::Singleton<LoggerManager> LoggerMgr;

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值