# ftlog
[![Build Status](https://github.com/nonconvextech/ftlog/workflows/ftlog/badge.svg?branch=main)](https://github.com/nonconvextech/ftlog/actions)
![License](https://img.shields.io/crates/l/ftlog.svg)
[![Latest Version](https://img.shields.io/crates/v/ftlog.svg)](https://crates.io/crates/ftlog)
[![ftlog](https://docs.rs/ftlog/badge.svg)](https://docs.rs/ftlog)
普通的日志库受到磁盘io和系统pipe影响,单线程顺序写入单条速度大概要2500ns(SSD),如果碰到io抖动或者慢磁盘,日志会是低延时交易的主要瓶颈。
本库先把日志send到channel,再启动后台单独线程recv并且磁盘写入,测试速度在300ns左右。
## 用法
在 `Cargo.toml` 中加入引用
```toml
ftlog = "0.2"
```
在`main`函数开头加入配置:
```rust
// ftlog 导出了log库常用的宏
use ftlog::appender::FileAppender;
use ftlog::{debug, trace};
use log::{error, info, warn};
// 最简配置,使用默认设置
// root函数传入None则打印日志到stderr
let dest = FileAppender::new("./current.log");
ftlog::builder().root(dest).build().unwrap().init().unwrap();
trace!("Hello world!");
debug!("Hello world!");
info!("Hello world!");
warn!("Hello world!");
error!("Hello world!");
```
以下是一个功能更丰富的例子:
```rust
use ftlog::{
appender::{Duration, FileAppender, Period},
FtLogFormat, LevelFilter, Record,
};
// 配置logger
let logger = ftlog::builder()
// 全局日志过滤等级
.max_log_level(LevelFilter::Info)
// 设置全局日志格式,为了性能时间戳格式不允许修改
.format(FtLogFormatter)
// 设置日志消息处理队列长度上限,避免大量未处理日志导致高内存占用
// 设成 `false` 会在队列满时丢弃新日志
// 设成 `true` 会在队列满时阻塞线程,等待日志线程
// 以下是默认设置
// 也可以用 unbound() 设置成不定长消息队列,消息特别多时会按需申请内存
.bounded(100_000, false) // .unbounded()
// 配置默认输出位置,传入None则输出到stderr
.root(FileAppender::rotate_with_expire(
"./current.log",
Period::Day,
Duration::days(7),
))
// 将 ftlog::appender 下的日志输出到后面定义的"ftlog-appender",即"ftlog-appender.log"文件
.filter("ftlog::appender", "ftlog-appender", LevelFilter::Error)
.appender("ftlog-appender", FileAppender::new("ftlog-appender.log"))
.build()
.expect("logger build failed");
// 将ftlog设置为全局logger
logger.init().expect("set logger failed");
```
更多例子请参见 `./examples`。
### 日志格式
```plain
2022-04-08 19:20:48.190+08 298ms <<这里是日志的延时,正常这个数为0,如果太大说明channel堵塞了 INFO main [src/ftlog.rs:14] 14575
```
其中 `298ms` 表示调用日志打印到日志专属线程开始处理日志消息之间的延迟,正常应为0ms。
```plain
2022-04-10 21:27:15.996+08 0ms 2 <<这表示丢弃了2条日志,说明使用了limit语法 INFO main [src/main.rs:29] limit running3 !
```
### 带间隔的日志
本库支持限制单条日志打印的频率。
不同代码行的日志间隔是互相独立的,互不影响。
后台线程以(模块、文件名、代码行)为key来记录上次日志打印时间,小于间隔则跳过。
下面这一行日志会有最小3000毫秒的间隔,也就是最多3000ms一条。
```rust
info!(limit=3000; "limit running{} !", 1);
```
```markdown
2022-04-10 21:27:10.996+08 0ms 0 INFO main [src/main.rs:29] limit running 3s !
2022-04-10 21:27:15.996+08 0ms 2 INFO main [src/main.rs:29] limit running 3s !
```
上面的数字2表示,在两条日志中间,还有2条被丢弃的日志。
### 日志分割
本库支持日志按时间分割,自动适配本地时区,可选分割时间精度如下:
- 分钟 `Period::Minute`
- 小时 `Period::Hour`
- 日 `Period::Day`
- 月 `Period::Month`
- 年 `Period::Year`
日志分割通过`FileAppender`设置,使用示例如下:
```rust
use ftlog::appender::{FileAppender, Period};
let logger = ftlog::builder()
.root(FileAppender::rotate("./mylog.log", Period::Minute))
.build()
.unwrap();
logger.init().unwrap();
```
设置按分钟分割时,输出日志文件格式为 `mylog-{MMMM}{YY}{DD}T{hh}{mm}.log`,其中`mylog`为调用时指定的文件名。如果未指定拓展名则在指定文件名后面加时间标签。
时间戳只保留到分割的时间精度,如按日分割保存的文件名格式为 `log-{MMMM}{YY}{DD}.log`。
输出的日志示例:
```sh
$ ls
# 按分钟分割
current-20221026T1351.log
# 按小时分割
current-20221026T13.log
# 按日分割
current-20221026.log
# 按月分割
current-202210.log
# 按年分割
current-2022.log
# 设置的保存文件名无拓展时(如"./log"),则直接在文件名末尾加时间
log-20221026T1351
```
#### 自动清理分割后的日志
在设置了日志按时间分割的基础上,可以设置自动清理日志。按照上次修改时间清理ftlog产生的日志文件。
**注意**:自动清理使用文件名匹配,文件名与日志文件名格式完全一致的文件也会被清理。
```rust
use ftlog::{appender::{Period, FileAppender, Duration}};
// 这个例子中,文件名满足current-\d{8}T\d{4}.log的文件将被清理
// `another-\d{8}T\d{4}.log`, `current-\d{8}T\d{4}` 等等由于文件名不匹配不会被清理
// `current-\d{8}.log` 由于分割时间精度不同也不会被清理
// 按天分割,日志最多保留7天。
let appender = FileAppender::rotate_with_expire("./current.log", Period::Day, Duration::days(7));
let logger = ftlog::builder()
.root(appender)
.build()
.unwrap();
logger.init().unwrap();
```
## 可选功能
- **tsc**
使用TSC寄存器作为时钟源,实现同等时间精度下更快地获取时间戳。
注意,启用TSC必须满足以下条件:
1. CPU不能变频
1. 必须在 **x86架构** 的CPU上,目前只支持 **Linux** 系统。否则会启用备用方案,牺牲时间精度换取速度
## 性能评测
> Rust:1.67.0-nightly
//! | | message type | Apple M1 Pro, 3.2GHz | AMD EPYC 7T83, 3.2GHz |
//! | ------------------------------------------------- | ------------- | --------------------- | --------------------- |
//! | `ftlog` | static string | 75 ns/iter | 385 ns/iter |
//! | `ftlog` | with i32 | 106 ns/iter | 491 ns/iter |
//! | `env_logger`
output to file | static string | 1,674 ns/iter | 1,142 ns/iter |
//! | `env_logger`
output to file | with i32 | 1,681 ns/iter | 1,179 ns/iter |
//! | `env_logger`
output to file with `BufWriter`| static string | 279 ns/iter | 550 ns/iter |
//! | `env_logger`
output to file with `BufWriter`| with i32 | 278 ns/iter | 565 ns/iter |
License: MIT OR Apache-2.0