# easylog 简介

easylog 是C++17 实现的高性能易用的 header only 日志库，它支持主流的3 中流输出模式：std::cout模式、sprintf模式和fmt::format/std::format 模式。主要特点:

- 支持3 种输出流
- 同步写日志
- 异步写日志
- 日志输出到控制台
- 日志输出到文件
- waring 以上级别的日志通过颜色区分
- 设置文件大小
- 设置文件数
- 手动flush
- 创建多个日志实例

# 基本用法

```c++
// 流式输出
ELOG_INFO << "easylog " << 42;
ELOG(INFO) << "easylog " << 42;

// printf输出
ELOGV(INFO, "easylog %d", 42);

// fmt::format/std::format 输出
#if __has_include(<fmt/format.h>) || __has_include(<format>)
  ELOGFMT(INFO, "easylog {}", 42);
#endif
```

流式输出的ELOG_INFO 等价于ELOG(INFO)。 

easylog 定义了如下日志级别：
```c++
enum class Severity {
  NONE,
  TRACE,
  DEBUG,
  INFO,
  WARN,
  WARNING = WARN,
  ERROR,
  CRITICAL,
  FATAL = CRITICAL,
};
```
用户可以根据需要输出这几种级别的日志，默认的日志级别是DEBUG，其中WARN 和 WARNING 是等价的，CRITICAL 和FATAL 是等价的，主要是为了适配其它日志库。

可以通过API ```easylog::set_min_severity(Severity severity)``` 来设置最小的日志级别，如把日志级别设置为WARN 之后，将会只输出WARNING 及以上级别的日志。

easylog 默认会将日志输出到控制台，如果不希望输出到控制台则通过API ```void set_console(bool enable)``` 将它禁用即可。

easylog 默认是同步输出日志，同步输出日志的性能没有异步日志的性能好，可以通过API ```easylog::void set_async(bool enable)```启用异步模式来输出日志以获得最好的性能。

easylog 默认不会每次flush 日志，可以通过调用API ```easylog::flush()```手动将日志内容flush 到日志文件。

# 输出到文件
easylog 默认会将日志输出到控制台，如果希望easylog 将日志输出到文件则需要调用easylog::init 接口做初始化。

```c++
/// \param Id 日志实例的唯一id，默认为0
/// \param min_severity 最低的日志级别
/// \param filename 日志文件名称
/// \param async 是否为异步模式
/// \param enable_console 是否输出到控制台
/// \param max_file_size 日志文件最大size
/// \param max_files 最大日志文件数
/// \param flush_every_time 是否每次flush 日志
template <size_t Id = 0>
void init_log(Severity min_severity, const std::string &filename = "",
                     bool async = true, bool enable_console = true,
                     size_t max_file_size = 0, size_t max_files = 0,
                     bool flush_every_time = false);
```

如果日志文件大小达到了max_file_size 旧的日志文件将会被覆盖，如果max_files 设置为1，当大小达到了max_file_size 时，日志文件会被覆盖。

```c++
easylog::init_log(Severity::DEBUG, filename, false, true, 5, 3);
ELOG_INFO << "long string test, long string test";
ELOG_INFO << "long string test, long string test";
ELOG_INFO << "long string test, long string test";
```
这个代码将产生3 个日志文件(easylog.txt, easylog.1.txt, easylog.2.txt)，当日志size达到设定的最大size 时，将会把easylog.txt 重命名为easylog.1.txt，接着创建新文件easylog.txt，日志内容会写到最新的easylog.txt 文件里。如果日志文件数达到了最大的max_files 3 时，最老的日志文件easylog.3.txt 会被删除，之前的easylog.txt 和 easylog.1.txt 会被重命名为easylog.1.txt 和 easylog.2.txt，然后创建最新的日志文件easylog.txt 用于写日志。

日志文件覆盖的原则是，当达到最大file size 时就要创建新的日志文件写日志，如果文件数量达到了最大size，则删掉最旧的日志文件，把之前的日志文件重命名重命名之后再创建最新的日志文件写日志，保持始终最多只有max_files 个日志文件。

# 创建多个日志实例

默认的日志实例只有一个，如果希望创建更多日志实例，则通过唯一的日志ID 来创建新的日志实例。

```c++
constexpr size_t Id = 2;
easylog::init_log<Id>(Severity::DEBUG, "testlog.txt");
MELOG_INFO(Id) << "test";
MELOGV(INFO, Id, "it is a test %d", 42);

constexpr size_t Id3 = 3;
easylog::init_log<Id3>(Severity::DEBUG, "testlog3.txt");
MELOG_INFO(Id3) << "test";
MELOGV(INFO, Id3, "it is a test %d", 42);
```

# 线程模型
easylog 是线程安全的，无论是同步模式还是异步模式。同步模式和异步模式的区别在于是否使用了后台线程去写日志。

## 同步模式
同步模式是指日志格式化、输出控制台和写文件从头到尾都是在调用者线程中完成，要保证多线程写日志的安全性，写文件和写控制台的时候会有一个锁来保证安全性。

## 异步模式
异步模式是指日志格式化、输出控制台和写文件从头到尾都在后台线程中。异步模式下后台线程只有一个，它不停的从一个无锁队列中取出日志信息进行处理，
由于是单线程处理，所以异步模式下日志的生成和写文件是不需要加锁的。

异步模式无疑问比同步模式性能更好，因此一般情况下应该优先使用异步模式去写日志。