secra-logger 2.0.2

一个生产级的 Rust 日志系统库,基于 tracing 生态系统构建,支持结构化 JSON 日志、文件滚动、UTC+8 时区等特性
Documentation
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
# Secra Logger

一个生产级的 Rust 日志系统库,基于 tracing 生态系统构建。

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Crates.io](https://img.shields.io/crates/v/secra-logger.svg)](https://crates.io/crates/secra-logger)

## 特性

- ✅ 基于 tracing 生态,支持结构化日志
- ✅ JSON 格式输出(基于 tracing-subscriber)
- ✅ 支持控制台、文件、或同时输出
- ✅ 可配置的控制台彩色输出
- ✅ 线程安全
- ✅ 支持 log crate 桥接
- ✅ 支持 actix-web 集成
- ✅ 高性能异步写入
- ✅ 字段规范化支持(遵循 OpenTelemetry 语义约定)
- ✅ Request ID 注入支持
- ✅ Logrotate 配置生成支持
- ✅ Vector 日志收集器配置支持

## 安装

在 `Cargo.toml` 中添加依赖:

```toml
[dependencies]
secra-logger = "1.0"
```

## 快速开始

### 基本使用

```rust
use secra_logger::{LoggingModule, LoggingConfig, OutputMode};
use tracing::{info, error, warn, debug};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建日志模块
    let logging_module = LoggingModule::new();
    
    // 创建日志配置
    let config = LoggingConfig {
        level: "info".to_string(),
        output_mode: OutputMode::Both,
        file: Some(secra_logger::LogFileConfig {
            path: "./logs/app.log".to_string(),
            enabled: true,
            logrotate: None,
        }),
        console: Some(secra_logger::LogConsoleConfig {
            enabled: true,
            ansi: true,
        }),
        enable_request_id: true,
        fields: secra_logger::LogFieldsConfig::default(),
        vector: None,
    };
    
    // 初始化日志系统
    logging_module.init(Some(config))?;
    
    // 使用 tracing 宏记录日志
    info!("应用启动");
    error!("发生错误");
    warn!("警告信息");
    debug!("调试信息(不会输出,因为级别是 INFO)");
    
    Ok(())
}
```

### 使用默认配置

```rust
use secra_logger::{LoggingModule, LoggingConfig};
use tracing::info;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let logging_module = LoggingModule::new();
    
    // 使用默认配置(输出到文件)
    logging_module.init(None)?;
    
    info!("使用默认配置记录日志");
    
    Ok(())
}
```

### 结构化日志

`secra-logger` 完全支持 tracing 的结构化日志功能:

```rust
use secra_logger::{LoggingModule, LoggingConfig, OutputMode};
use tracing::info;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let logging_module = LoggingModule::new();
    let config = LoggingConfig {
        level: "info".to_string(),
        output_mode: OutputMode::Both,
        file: Some(secra_logger::LogFileConfig {
            path: "./logs/app.log".to_string(),
            enabled: true,
            logrotate: None,
        }),
        console: Some(secra_logger::LogConsoleConfig {
            enabled: true,
            ansi: true,
        }),
        enable_request_id: true,
        fields: secra_logger::LogFieldsConfig::default(),
        vector: None,
    };
    logging_module.init(Some(config))?;
    
    // 结构化日志记录
    let user_id = 12345;
    let action = "login";
    
    info!(
        user_id = user_id,
        action = action,
        ip = "192.168.1.1",
        duration_ms = 150,
        "用户登录成功"
    );
    
    // 使用格式化字符串
    info!(
        user_id = user_id,
        "用户 {} 执行了操作", user_id
    );
    
    Ok(())
}
```

输出示例(JSON 格式):
```json
{
  "timestamp": "2024-01-15T10:30:45.123456789+08:00",
  "level": "INFO",
  "fields": {
    "message": "用户登录成功",
    "user_id": 12345,
    "action": "login",
    "ip": "192.168.1.1",
    "duration_ms": 150
  },
  "target": "myapp",
  "file": "src/main.rs",
  "line": 86
}
```

### 错误处理

```rust
use secra_logger::{LoggingModule, LoggingConfig, OutputMode};

fn main() {
    let logging_module = LoggingModule::new();
    let config = LoggingConfig {
        level: "info".to_string(),
        output_mode: OutputMode::File,
        file: Some(secra_logger::LogFileConfig {
            path: "./logs/app.log".to_string(),
            enabled: true,
            logrotate: None,
        }),
        console: None,
        enable_request_id: true,
        fields: secra_logger::LogFieldsConfig::default(),
        vector: None,
    };
    
    match logging_module.init(Some(config)) {
        Ok(()) => {
            tracing::info!("日志系统初始化成功");
        }
        Err(e) => {
            eprintln!("初始化失败: {}", e);
        }
    }
}
```

### 多线程使用

`secra-logger` 是线程安全的,可以在多线程环境中安全使用:

```rust
use secra_logger::{LoggingModule, LoggingConfig, OutputMode};
use std::thread;
use tracing::info;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let logging_module = LoggingModule::new();
    let config = LoggingConfig {
        level: "info".to_string(),
        output_mode: OutputMode::Both,
        file: Some(secra_logger::LogFileConfig {
            path: "./logs/app.log".to_string(),
            enabled: true,
            logrotate: None,
        }),
        console: Some(secra_logger::LogConsoleConfig {
            enabled: true,
            ansi: true,
        }),
        enable_request_id: true,
        fields: secra_logger::LogFieldsConfig::default(),
        vector: None,
    };
    logging_module.init(Some(config))?;
    
    let handles: Vec<_> = (0..5)
        .map(|i| {
            thread::spawn(move || {
                for j in 0..10 {
                    info!(
                        thread_id = i,
                        iteration = j,
                        "线程 {} 执行第 {} 次迭代", i, j
                    );
                }
            })
        })
        .collect();
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    Ok(())
}
```

### Actix-Web 集成

```rust
use actix_web::{web, App, HttpServer, Responder};
use secra_logger::{LoggingModule, LoggingConfig, OutputMode};
use tracing::info;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // 初始化日志系统
    let logging_module = LoggingModule::new();
    let config = LoggingConfig {
        level: "info".to_string(),
        output_mode: OutputMode::Both,
        file: Some(secra_logger::LogFileConfig {
            path: "./logs/webapp.log".to_string(),
            enabled: true,
            logrotate: None,
        }),
        console: Some(secra_logger::LogConsoleConfig {
            enabled: true,
            ansi: true,
        }),
        enable_request_id: true,
        fields: secra_logger::LogFieldsConfig::default(),
        vector: None,
    };
    logging_module.init(Some(config)).expect("初始化日志系统失败");
    
    info!("Web 服务器启动中...");
    
    // 启动 HTTP 服务器
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(|| async { "Hello, World!" }))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}
```

### 使用 log crate 兼容接口

如果你需要使用传统的 `log` crate API,`secra-logger` 也提供了桥接支持:

```rust
use secra_logger::{LoggingModule, LoggingConfig, OutputMode};
use log::{info, error, warn};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let logging_module = LoggingModule::new();
    let config = LoggingConfig {
        level: "info".to_string(),
        output_mode: OutputMode::Both,
        file: Some(secra_logger::LogFileConfig {
            path: "./logs/app.log".to_string(),
            enabled: true,
            logrotate: None,
        }),
        console: Some(secra_logger::LogConsoleConfig {
            enabled: true,
            ansi: true,
        }),
        enable_request_id: true,
        fields: secra_logger::LogFieldsConfig::default(),
        vector: None,
    };
    logging_module.init(Some(config))?;
    
    // 可以使用 log crate 的宏
    info!("使用 log crate 记录日志");
    warn!("警告信息");
    error!("错误信息");
    
    Ok(())
}
```

## 配置说明

### LoggingConfig

`LoggingConfig` 是日志系统的核心配置结构:

```rust
pub struct LoggingConfig {
    pub level: String,                    // 日志级别(trace | debug | info | warn | error)
    pub output_mode: OutputMode,          // 输出模式
    pub file: Option<LogFileConfig>,      // 文件输出配置
    pub console: Option<LogConsoleConfig>, // 控制台输出配置
    pub enable_request_id: bool,          // 是否启用 Request ID 注入
    pub fields: LogFieldsConfig,          // 字段规范化配置
    pub vector: Option<VectorConfig>,    // Vector 日志收集器配置(可选)
}
```

#### 创建配置

使用 `LoggingConfig::default()` 创建默认配置:

```rust
let config = LoggingConfig::default();
```

或者手动创建配置:

```rust
use secra_logger::{LoggingConfig, OutputMode};

let config = LoggingConfig {
    level: "info".to_string(),
    output_mode: OutputMode::Both,
    file: Some(secra_logger::LogFileConfig {
        path: "./logs/app.log".to_string(),
        enabled: true,
        logrotate: None,
    }),
    console: Some(secra_logger::LogConsoleConfig {
        enabled: true,
        ansi: true,
    }),
    enable_request_id: true,
    fields: secra_logger::LogFieldsConfig::default(),
    vector: None,
};
```

#### 配置验证

配置对象提供了 `validate()` 方法用于验证配置的有效性:

```rust
let config = LoggingConfig {
    level: "invalid".to_string(), // 无效的日志级别
    output_mode: OutputMode::File,
    file: Some(secra_logger::LogFileConfig {
        path: "./logs/app.log".to_string(),
        enabled: true,
        logrotate: None,
    }),
    console: None,
    enable_request_id: true,
    fields: secra_logger::LogFieldsConfig::default(),
    vector: None,
};

if let Err(e) = config.validate() {
    eprintln!("配置验证失败: {}", e);
}
```

### OutputMode

`OutputMode` 枚举定义了日志的输出模式:

- `OutputMode::File` - 仅输出到文件(适合生产环境)
- `OutputMode::Console` - 仅输出到控制台(适合开发环境)
- `OutputMode::Both` - 同时输出到文件和控制台(适合需要同时查看和保存的场景)

### LogFileConfig

文件输出配置:

```rust
pub struct LogFileConfig {
    pub path: String,                    // 日志文件路径
    pub enabled: bool,                    // 是否启用
    pub logrotate: Option<LogrotateConfig>, // Logrotate 配置(可选)
}
```

### LogConsoleConfig

控制台输出配置:

```rust
pub struct LogConsoleConfig {
    pub enabled: bool,  // 是否启用
    pub ansi: bool,    // 是否使用彩色输出
}
```

### LogFieldsConfig

字段规范化配置:

```rust
pub struct LogFieldsConfig {
    pub enabled: bool,                    // 是否启用字段规范化
    pub mappings: HashMap<String, String>, // 字段映射规则(可选)
}
```

字段规范化遵循 OpenTelemetry 语义约定:
- HTTP 请求:`http.method`, `http.path`, `http.status_code`
- 数据库:`db.system`, `db.name`, `db.operation`
- 服务:`service.name`, `service.version`
- 请求追踪:`trace_id`, `span_id`, `request_id`
- 用户:`user.id`, `user.email`

### LogrotateConfig

Logrotate 配置用于生成 logrotate 配置文件,以便系统自动管理日志文件。

> **注意**:使用 logrotate 功能前,需要先在系统上安装 logrotate 工具。详细安装教程请参考 [Logrotate 安装指南]docs/logrotate/INSTALL.md
```rust
pub struct LogrotateConfig {
    pub enabled: bool,                    // 是否启用
    pub path: String,                     // 日志文件路径模式(支持通配符)
    pub rotate: String,                   // 轮转周期(daily | weekly | monthly | yearly | size)
    pub size: Option<String>,              // 文件大小限制(单位:KB, MB, GB)
    pub maxsize: Option<String>,          // 最大文件大小(已废弃,建议使用 size)
    pub minsize: Option<String>,          // 最小文件大小(小于此大小不轮转)
    pub rotate_count: u32,                 // 保留的日志文件数量
    pub maxage: Option<u32>,               // 最大保留天数
    pub start: u32,                        // 起始轮转编号
    pub compress: bool,                   // 是否压缩旧日志文件
    pub delaycompress: u32,                // 压缩延迟
    pub compresscmd: Option<String>,       // 压缩命令(默认 gzip)
    pub uncompresscmd: Option<String>,     // 解压缩命令(默认 gunzip)
    pub compressext: String,               // 压缩文件扩展名(默认 .gz)
    pub compressoptions: Option<String>,  // 压缩选项
    pub create: bool,                      // 是否在轮转时创建新文件
    pub create_mode: String,              // 新文件的权限(八进制格式)
    pub create_owner: Option<String>,     // 新文件的所有者
    pub copy: bool,                        // 是否使用 copy 模式
    pub copytruncate: bool,                // 是否使用 copytruncate 模式
    pub postrotate: bool,                 // 是否在轮转后执行脚本
    pub postrotate_script: Option<String>, // 轮转后执行的脚本命令
    pub prerotate: bool,                  // 是否在轮转前执行脚本
    pub prerotate_script: Option<String>,  // 轮转前执行的脚本命令
    pub firstaction: bool,                // 是否在第一次轮转前执行脚本
    pub firstaction_script: Option<String>, // 第一次轮转前执行的脚本命令
    pub lastaction: bool,                 // 是否在最后一次轮转后执行脚本
    pub lastaction_script: Option<String>,  // 最后一次轮转后执行的脚本命令
    pub missingok: bool,                  // 是否忽略缺失的日志文件
    pub notifempty: bool,                 // 是否在日志文件为空时不轮转
    pub ifempty: bool,                    // 是否即使文件为空也轮转
    pub sharedscripts: bool,              // 是否共享脚本
    pub dateext: bool,                   // 是否使用日期扩展名
    pub dateformat: String,               // 日期扩展名格式
    pub olddir: Option<String>,          // 旧日志文件目录
    pub noolddir: bool,                   // 是否不使用 olddir
    pub extension: Option<String>,        // 压缩文件扩展名
    pub tabooext: Vec<String>,            // 禁止的扩展名列表
    pub su: Option<String>,               // 以指定用户身份运行脚本
    pub mail: bool,                       // 是否发送邮件通知
    pub mailfirst: Option<String>,         // 邮件地址(轮转前发送)
    pub maillast: bool,                   // 是否在轮转后发送邮件
    pub nomail: bool,                     // 是否不发送邮件
    pub include: Vec<String>,             // 包含其他配置文件
    pub shred: bool,                      // 是否安全删除文件
    pub shredcycles: u32,                 // 安全删除循环次数
    pub nocompress: bool,                 // 是否不压缩
}
```

### LogrotateConfig 配置选项说明

**轮转策略**:
- `rotate`: 轮转周期(daily/weekly/monthly/yearly/size)
- `size`: 文件大小限制,当日志文件达到此大小时进行轮转
- `minsize`: 最小文件大小,小于此大小的文件不进行轮转
- `rotate_count`: 保留的日志文件数量
- `maxage`: 最大保留天数,超过此天数的旧日志将被删除
- `start`: 起始轮转编号(默认从 1 开始)

**压缩选项**:
- `compress`: 是否压缩旧日志文件
- `delaycompress`: 压缩延迟(轮转后 N 个周期再压缩)
- `compresscmd`: 压缩命令(默认 gzip)
- `uncompresscmd`: 解压缩命令(默认 gunzip)
- `compressext`: 压缩文件扩展名(默认 .gz)
- `compressoptions`: 压缩选项(如 -9 表示最高压缩级别)
- `nocompress`: 是否不压缩(与 compress 相反)

**文件创建**:
- `create`: 是否在轮转时创建新文件
- `create_mode`: 新文件的权限(八进制格式,如 0644)
- `create_owner`: 新文件的所有者(user:group)
- `copy`: 是否使用 copy 模式(复制原文件后截断)
- `copytruncate`: 是否使用 copytruncate 模式(复制后截断原文件)

**脚本执行**:
- `prerotate`/`prerotate_script`: 轮转前执行的脚本
- `postrotate`/`postrotate_script`: 轮转后执行的脚本
- `firstaction`/`firstaction_script`: 第一次轮转前执行的脚本
- `lastaction`/`lastaction_script`: 最后一次轮转后执行的脚本
- `sharedscripts`: 是否共享脚本(多个日志文件共享一个脚本)
- `su`: 以指定用户身份运行脚本(user:group)

**文件处理**:
- `missingok`: 是否忽略缺失的日志文件
- `notifempty`: 是否在日志文件为空时不轮转
- `ifempty`: 是否即使文件为空也轮转(与 notifempty 相反)
- `olddir`: 旧日志文件目录(用于将旧日志移动到指定目录)
- `noolddir`: 是否不使用 olddir
- `extension`: 压缩文件扩展名
- `tabooext`: 禁止的扩展名列表(这些扩展名的文件不会被轮转)

**日期格式**:
- `dateext`: 是否使用日期扩展名(默认使用数字扩展名 .1, .2, ...)
- `dateformat`: 日期扩展名格式(默认 %Y%m%d,如 .20260114)

**邮件通知**:
- `mail`: 是否发送邮件通知
- `mailfirst`: 邮件地址(轮转前发送)
- `maillast`: 是否在轮转后发送邮件
- `nomail`: 是否不发送邮件

**其他选项**:
- `include`: 包含其他配置文件
- `shred`/`shredcycles`: 是否安全删除文件(使用 shred 命令)

### VectorConfig

Vector 日志收集器配置用于生成 Vector 配置文件,以便将日志发送到各种目标(Loki、Elasticsearch、S3 等):

```rust
pub struct VectorConfig {
    pub enabled: bool,                          // 是否启用
    pub config_path: String,                     // Vector 配置输出路径
    pub source: VectorSourceConfig,              // 日志源配置
    pub transforms: Vec<VectorTransformConfig>, // 日志转换配置
    pub sinks: Vec<VectorSinkConfig>,           // 日志输出目标配置
}
```

### 日志级别

支持的日志级别(从低到高):

- `"trace"` - 最详细的调试信息
- `"debug"` - 调试信息
- `"info"` - 一般信息(推荐用于生产环境)
- `"warn"` - 警告信息
- `"error"` - 错误信息

只有等于或高于配置级别的日志才会被记录。例如,如果配置为 `"info"`,则 `"trace"` 和 `"debug"` 级别的日志不会被记录。

也可以通过环境变量 `RUST_LOG` 来设置日志级别:

```bash
RUST_LOG=debug cargo run
```

## API 参考

### LoggingModule

日志模块,负责初始化日志系统。

```rust
pub struct LoggingModule {
    _file_guard: Arc<Mutex<Option<non_blocking::WorkerGuard>>>,
}
```

#### 方法

##### `new() -> Self`

创建新的 `LoggingModule` 实例。

##### `init(config: Option<LoggingConfig>) -> Result<()>`

初始化日志系统。

**参数**:
- `config: Option<LoggingConfig>` - 日志配置,如果为 `None` 则使用默认配置

**返回值**:
- `Ok(())` - 初始化成功
- `Err(anyhow::Error)` - 初始化失败

**说明**:
- 此方法会设置全局 tracing subscriber
- 如果全局 subscriber 已经设置(例如在测试中),会记录警告但继续执行
- 文件写入器的 `WorkerGuard` 会被保存,确保日志能够正确刷新

##### `shutdown()`

关闭日志系统,清理资源。

### Logger

Logger 组件,用于标识日志系统已初始化。

```rust
pub struct Logger;
```

在实际使用中,通过 tracing 宏直接记录日志,无需通过 Logger 组件。此组件主要用于标识日志系统已初始化。

## 运行示例

项目提供了多个示例来演示不同的使用场景:

```bash
# 运行基本示例(展示基本日志记录功能)
cargo run --example basic

# 运行文件滚动测试示例(测试文件滚动机制)
cargo run --example rotation

# 运行路径测试示例(测试目录和文件路径配置)
cargo run --example path_test

# 运行目录路径测试示例
cargo run --example path_test_dir

# 运行 span 和 event 示例
cargo run --example span_event

# 运行 actix-web 集成示例(需要 actix-web 依赖)
cargo run --example actix_web
```

运行示例后,可以在 `./logs/` 目录下查看生成的日志文件。

## 最佳实践

### 1. 日志级别选择

- **开发环境**:使用 `"debug"``"trace"` 获取详细调试信息
- **生产环境**:使用 `"info"``"warn"`,避免过多日志影响性能
- **关键服务**:使用 `"warn"`,只记录警告和错误

### 2. 输出模式选择

- **开发环境**:使用 `OutputMode::Console``OutputMode::Both`,方便查看日志
- **生产环境**:使用 `OutputMode::File`,避免控制台输出影响性能
- **容器环境**:使用 `OutputMode::Both`,文件用于持久化,stdout 用于容器日志收集

### 3. 结构化日志

充分利用结构化日志的优势:

```rust
// 好的做法:使用结构化字段
info!(
    user_id = user_id,
    action = "login",
    ip = %client_ip,
    duration_ms = elapsed.as_millis(),
    "用户登录成功"
);

// 避免:只使用格式化字符串
info!("用户 {} 从 {} 登录,耗时 {}ms", user_id, client_ip, elapsed.as_millis());
```

### 4. 错误处理

始终处理初始化错误:

```rust
match logging_module.init(Some(config)) {
    Ok(()) => {
        tracing::info!("日志系统初始化成功");
    }
    Err(e) => {
        eprintln!("日志系统初始化失败: {}", e);
        // 根据应用需求决定是否退出
        std::process::exit(1);
    }
}
```

### 5. 多线程环境

`secra-logger` 是线程安全的,可以在多线程环境中直接使用,无需额外同步:

```rust
use std::thread;

// 多个线程可以安全地同时记录日志
for i in 0..10 {
    thread::spawn(move || {
        tracing::info!(thread_id = i, "线程 {} 启动", i);
    });
}
```

### 6. Request ID 注入

启用 `enable_request_id` 后,可以在日志中自动注入 Request ID,方便追踪请求链路。

### 7. 字段规范化

启用字段规范化后,日志字段会遵循 OpenTelemetry 语义约定,便于日志分析和监控系统集成。

## 性能考虑

- **异步写入**:日志写入是异步的,不会阻塞主线程
- **批量刷新**:日志会批量刷新到磁盘,提高性能
- **线程安全**:使用高性能锁(`parking_lot`),多线程环境下性能优秀
- **JSON 序列化**:使用高效的 JSON 序列化库,性能开销小

## 常见问题

### Q: 为什么日志文件没有创建?

A: 确保日志路径的父目录存在且有写权限。如果父目录不存在,日志系统会尝试创建,但需要相应的权限。

### Q: 如何更改日志级别?

A: 可以在配置中设置 `level` 字段,或者通过环境变量 `RUST_LOG` 来设置。环境变量的优先级更高。

### Q: 可以在运行时更改日志级别吗?

A: 当前版本不支持运行时更改配置。需要在初始化时设置好日志级别。如果需要动态调整,可以考虑使用 `tracing-subscriber` 的 `EnvFilter`。

### Q: 日志格式可以自定义吗?

A: 当前版本使用固定的 JSON 格式。如果需要自定义格式,可以修改 `module.rs` 中的格式化逻辑。

### Q: 如何集成到现有的 tracing 系统中?

A: `secra-logger` 使用标准的 `tracing-subscriber`,可以与其他 subscriber 组合使用。但需要注意,多次调用 `init` 可能会失败(如果全局 subscriber 已经设置)。

### Q: 如何使用 Logrotate 配置?

A: 在 `LogFileConfig` 中设置 `logrotate` 字段,配置相应的参数。然后可以使用生成的 logrotate 配置文件来管理日志文件。

**安装 logrotate**:如果系统上尚未安装 logrotate,请参考 [Logrotate 安装指南](docs/logrotate/INSTALL.md) 进行安装。支持在线安装和离线安装两种方式。

### Q: 如何使用 Vector 配置?

A: 在 `LoggingConfig` 中设置 `vector` 字段,配置相应的源、转换和目标。然后可以使用生成的 Vector 配置文件来收集和转发日志。

## 依赖

主要依赖:
- `tracing` - 结构化日志框架
- `tracing-subscriber` - 订阅者实现
- `tracing-log` - log crate 桥接
- `tracing-appender` - 异步日志写入
- `parking_lot` - 高性能锁实现
- `chrono` / `chrono-tz` - 日期时间处理
- `once_cell` - 全局初始化状态管理
- `serde` / `serde_json` - 序列化支持
- `anyhow` - 错误处理
- `thiserror` - 错误类型定义

## 版本历史

### 1.0.4
- 优化依赖管理,将仅用于开发和示例的依赖移动到 `dev-dependencies`
- 减少生产依赖大小

### 1.0.3
- 优化文件滚动逻辑
- 改进错误处理
- 添加 `span_event` 示例

### 1.0.2
- 修复 let chains 语法以兼容 Rust 2021 edition

### 1.0.1
- 发布稳定版本,标志着项目进入生产就绪状态

### 0.3.0
- 大幅简化代码结构
- 重构日志写入架构
- 优化文件写入性能

## 贡献

欢迎提交 Issue 和 Pull Request!

## 许可证

MIT License

Copyright (c) 2024 Secra Team

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.