1. LogLevel

日志级别:通过LogLevel类可以定义日志的重要级别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class LogLevel
{
public:
enum Level
{
UNKNOW = 0,
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
FATAL = 5
};
static const char *ToString(LogLevel::Level level); // enum转string
static LogLevel::Level FromString(const std::string &str); // string转enum
};

2. LogEvent

日志事件:日志事件LogEvent类包含当前日志的所有具体信息,包括Logger日志器、LogLevel日志级别、行号、线程号等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class LogEvent
{
public:
typedef std::shared_ptr<LogEvent> ptr;
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);

const char *getFile() const { return m_file; }
int32_t getLine() const { return m_line; }
uint32_t getElapse() const { return m_elapse; }
uint32_t getThreadId() const { return m_threadId; }
uint32_t getFiberId() const { return m_fiberId; }
uint64_t getTime() const { return m_time; }
const std::string &getThreadName() const { return m_threadName; }
std::string getContent() const { return m_ss.str(); }
std::shared_ptr<Logger> getLogger() const { return m_logger; }
LogLevel::Level getLevel() const { return m_level; }
std::stringstream &getSS() { return m_ss; }
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;
uint32_t m_threadId = 0;
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;
};

3. LogFormatter

格式化文本:日志格式器LogFormatter,返回格式化字符串

  • 成员函数init():根据构造函数传入的pattern:%xxx %xxx{xxx} %%初始化m_items
  • 成员函数format():根据传入的Logger日志器、LogLevel日志级别、LogEvent日志事件,遍历调用m_items将其转换为格式化文本
  • 成员对象string``m_pattern
  • 成员对象vector<FormatItem::ptr> m_items
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class LogFormatter
    {
    public:
    typedef std::shared_ptr<LogFormatter> ptr;
    LogFormatter(const std::string &pattern);

    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);
    void init();
    bool isError() const { return m_error; }
    const std::string getPattern() const { return m_pattern; }

    public:
    class FormatItem
    {
    public:
    typedef std::shared_ptr<FormatItem> ptr;
    virtual ~FormatItem() {}
    virtual void format(std::ostream &os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
    };

    private:
    std::string m_pattern;
    std::vector<FormatItem::ptr> m_items;
    bool m_error = false;
    };

3.1 FormatItem

格式器项:根据自身要求输出对应的信息,如MessageFormatItem类负责输出LogEvent中的字节流,他只获取LogEvent中的Content对象,并输出到对应的ostream

1
2
3
4
5
6
7
8
9
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();
}
};

4. LogAppender

输出:日志输出器,用于输出LogFormatter返回的格式化文本,其中保存了LogFormatter格式器对象,LogLevel日志等级

  • 成员函数log():通过传入Logger日志器、LogLevel日志级别、LogEvent日志事件,调用LogFormatter,得到格式化文本并输出到指定位置
  • 成员对象m_formatter:每个输出器都有自己的格式器
  • 成员对象m_level:配置LogLevelLogAppender根据自己的LogLevel判断传入的日志事件是否进行输出操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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;
    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;
    };

4.1 StdoutLogAppender

控制台输出:日志输出器LogAppender的具体类,通过调用log函数实现输出

1
2
3
4
5
6
7
8
9
10
11
12
class StdoutLogAppender : public LogAppender {
public:
typedef std::shared_ptr<StdoutLogAppender> ptr;
void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) {
if (level >= m_level) {
MutexType::Lock lock(m_mutex);
m_formatter->format(std::cout, logger, level, event);
}
}
std::string toYamlString() override;
};

5. Logger

日志器类:保存有字段:LogLevel日志级别,LogAppender日志输出地,LogFormatter日志格式器

  • 成员对象LogLevel
  • 成员对象list<LogAppender::ptr>
  • 成员对象LogFormatter:默认给LogAppender内的Logformatter做配置
  • 成员函数debug/info/warn/error/fatal:输出该日志等级的日志事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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);
void debug(LogEvent::ptr event);
void info(LogEvent::ptr event);
void warn(LogEvent::ptr event);
void error(LogEvent::ptr event);
void fatal(LogEvent::ptr event);

void addAppender(LogAppender::ptr appender);
void delAppender(LogAppender::ptr 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();

std::string toYamlString();

private:
std::string m_name;
LogLevel::Level m_level;
MutexType m_mutex;
std::list<LogAppender::ptr> m_appenders;
LogFormatter::ptr m_formatter;
Logger::ptr m_root;
};

6. LogEventWrap

日志包装器:可以把LogEventWrap当作临时的右值,创建时直接传入在构造函数中传入LogEvent,本行结束后自动析构,调用log,不会产生额外的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
class LogEventWrap {
public:
LogEventWrap(LogEvent::ptr e);
~LogEventWrap() {
m_event->getLogger()->log(m_event->getLevel(), m_event);
}

LogEvent::ptr getEvent() const { return m_event; }
std::stringstream& getSS();

private:
LogEvent::ptr m_event;
};

7. 调用过程

7.1 Logger::log

  • Logger::log函数被调用,根据Logger日志器的日志等级判断本次log是否有效
    • 有效则遍历Logger中的所有LogAppender
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      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);
      }
      }
      }

7.2 LogAppender::log

  • Logger::log调用
  • 每个LogAppender都保存有自己的LogLevel日志等级
    • 如果本次日志有效,则调用LogFormatter,并且传入std::cout作为输出端口
      1
      2
      3
      4
      5
      6
      7
      8
      void StdoutLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event)
      {
      if (level >= m_level)
      {
      // 锁 MutexType::Lock lock(m_mutex);
      m_formatter->format(std::cout, logger, level, event);
      }
      }

7.3 LogFormatter::format

  • LogAppender::log调用
  • 遍历内部保存的每个FormatItem,有两个重载
    • 方法一将所有的内容存储到字节流中,并返回字符串
    • 方法二传入输出流,并直接通过format将流输出到对应位置
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      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();
      }

      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;
      }

7.4 FormatItem::format

  • 以线程格式化器ThreadIdFormatItem为例
  • 该方法对应了LogFormatter::format中的重载方法二
    • 可以传入std::cout,直接将对应的数据输出到std::cout
      1
      2
      3
      4
      5
      6
      7
      8
      9
      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();
      }
      };

8. Logger使用示例

1
2
3
4
5
6
7
8
9
void testLog()
{
// 声明 logger
sylar::Logger::ptr logger(new sylar::Logger());
// 给 logger 添加两种 appender
logger->addAppender(sylar::StdoutLogAppender::ptr(new sylar::StdoutLogAppender()));
logger->addAppender(sylar::FileLogAppender::ptr(new sylar::FileLogAppender("/home/oracle/projects/Sylar/bin/x64/Debug/log.txt")));
SYLAR_LOG_DEBUG(logger) << "输出DEBUG级别的日志信息" << std::endl;
}