增加日志组件
This commit is contained in:
21
Ayay.SerilogLogs/Ayay.SerilogLogs.csproj
Normal file
21
Ayay.SerilogLogs/Ayay.SerilogLogs.csproj
Normal file
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.Enrichers.Process" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Map" Version="2.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
183
Ayay.SerilogLogs/LogBootstrapper.cs
Normal file
183
Ayay.SerilogLogs/LogBootstrapper.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Exceptions; // Nuget: Serilog.Exceptions
|
||||
using Serilog.Enrichers.Span; // Nuget: Serilog.Enrichers.Span
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ayay.SerilogLogs
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志引导程序
|
||||
/// <para>负责初始化 Serilog 全局配置,包括文件策略、控制台输出、Seq 连接以及上下文丰富化。</para>
|
||||
/// <para>实现了按日期分文件夹、按模块分文件、以及主次日志分离的复杂策略。</para>
|
||||
/// </summary>
|
||||
public static class LogBootstrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化日志系统 (通常在程序启动最开始调用)
|
||||
/// </summary>
|
||||
/// <param name="opts">配置选项</param>
|
||||
public static void Init(LogOptions opts)
|
||||
{
|
||||
// --------------------------------------------------------
|
||||
// 1. 目录容错处理 (防止因为 D 盘不存在导致程序崩溃)
|
||||
// --------------------------------------------------------
|
||||
string finalLogPath = opts.LogRootPath;
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(finalLogPath))
|
||||
{
|
||||
Directory.CreateDirectory(finalLogPath);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 如果创建失败(比如没有 D 盘权限),回退到程序运行目录下的 Logs 文件夹
|
||||
finalLogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 2. 构建 Serilog 配置
|
||||
// --------------------------------------------------------
|
||||
var builder = new LoggerConfiguration();
|
||||
|
||||
// 2.1 设置全局最低门槛 (兜底策略)
|
||||
builder.MinimumLevel.Is(opts.GlobalMinimumLevel);
|
||||
|
||||
// 2.2 应用模块级别的特殊配置 (关键:处理 Algorithm=Debug, Ping=Fatal 等)
|
||||
if (opts.ModuleLevels != null)
|
||||
{
|
||||
foreach (var module in opts.ModuleLevels)
|
||||
{
|
||||
builder.MinimumLevel.Override(module.Key, module.Value);
|
||||
}
|
||||
}
|
||||
// 强制覆盖微软自带的啰嗦日志
|
||||
builder.MinimumLevel.Override("Microsoft", LogEventLevel.Warning);
|
||||
builder.MinimumLevel.Override("System", LogEventLevel.Warning);
|
||||
|
||||
// 2.3 注入全套元数据 (Enrichers) - 让日志更聪明
|
||||
builder
|
||||
.Enrich.FromLogContext() // 允许使用 .ForContext() 注入上下文
|
||||
.Enrich.WithProperty("AppId", opts.AppId) // 注入应用标识
|
||||
.Enrich.WithThreadId() // 线程ID
|
||||
.Enrich.WithProcessId() // 进程ID (用于识别重启)
|
||||
.Enrich.WithExceptionDetails() // 结构化异常堆栈
|
||||
.Enrich.WithSpan(); // 全链路追踪 ID
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 3. 配置输出端 (Sinks)
|
||||
// --------------------------------------------------------
|
||||
|
||||
// 定义通用模板:包含了 SourceContext (模块名), TraceId, AppId
|
||||
// 示例: 2026-01-15 12:00:01 [INF] [Algorithm] [Dev01] 计算完成
|
||||
string outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] [{AppId}] {Message:lj}{NewLine}{Exception}";
|
||||
|
||||
// 3.1 控制台输出 (开发调试用)
|
||||
builder.WriteTo.Async(a => a.Console(
|
||||
restrictedToMinimumLevel: opts.ConsoleLevel,
|
||||
outputTemplate: outputTemplate
|
||||
));
|
||||
|
||||
// =================================================================
|
||||
// 3.2 [核心逻辑] 双重 Map 实现: 日期文件夹 -> 模块文件
|
||||
// =================================================================
|
||||
|
||||
// 第一层 Map:根据【日期】分发 (key = "2026-01-15")
|
||||
// 目的:实现 D:\Logs\App\2026-01-15\ 这样的目录结构
|
||||
builder.WriteTo.Map(le => le.Timestamp.ToString("yyyy-MM-dd"), (dateKey, dailyConfig) =>
|
||||
{
|
||||
// 动态计算当天的文件夹路径
|
||||
var dailyPath = Path.Combine(finalLogPath, dateKey);
|
||||
|
||||
// 第二层 Map:根据【模块 SourceContext】分发
|
||||
// 目的:在日期文件夹下,区分 System.txt, Network.txt
|
||||
dailyConfig.Map("SourceContext", (moduleKey, moduleConfig) =>
|
||||
{
|
||||
// 如果没填 SourceContext,默认归为 General
|
||||
var moduleName = string.IsNullOrEmpty(moduleKey) ? "General" : moduleKey;
|
||||
|
||||
// --- A. 配置【主要数据】文件 (Main) ---
|
||||
// 规则: 只存 Information 及以上
|
||||
// 路径: .../2026-01-15/System-20260115.txt
|
||||
moduleConfig.File(
|
||||
path: Path.Combine(dailyPath, $"{moduleName}-.txt"), // RollingInterval.Day 会自动把 - 替换为 -yyyyMMdd
|
||||
restrictedToMinimumLevel: LogEventLevel.Information, // 👈 过滤掉 Debug
|
||||
rollingInterval: RollingInterval.Day,
|
||||
|
||||
// 文件大小限制 (超过 10MB 切割 Main_001.txt)
|
||||
fileSizeLimitBytes: opts.FileSizeLimitBytes,
|
||||
rollOnFileSizeLimit: opts.RollOnFileSizeLimit,
|
||||
|
||||
// ⚠️ 设为 null,因为我们有自定义的 LogCleaner 接管清理工作,避免 Serilog 内部逻辑冲突
|
||||
retainedFileCountLimit: null,
|
||||
|
||||
encoding: Encoding.UTF8,
|
||||
outputTemplate: outputTemplate,
|
||||
shared: true // 允许跨进程/多线程共享
|
||||
);
|
||||
|
||||
// --- B. 配置【细节数据】文件 (Detail) ---
|
||||
// 规则: 存 Debug 及以上 (包含 Main 的数据,是最全的备份)
|
||||
// 路径: .../2026-01-15/SystemDetail-20260115.txt
|
||||
moduleConfig.File(
|
||||
path: Path.Combine(dailyPath, $"{moduleName}Detail-.txt"),
|
||||
restrictedToMinimumLevel: LogEventLevel.Debug, // 👈 包含 Debug
|
||||
rollingInterval: RollingInterval.Day,
|
||||
|
||||
// 文件大小限制 (与 Main 保持一致)
|
||||
fileSizeLimitBytes: opts.FileSizeLimitBytes,
|
||||
rollOnFileSizeLimit: opts.RollOnFileSizeLimit,
|
||||
|
||||
retainedFileCountLimit: null,
|
||||
|
||||
encoding: Encoding.UTF8,
|
||||
outputTemplate: outputTemplate,
|
||||
shared: true
|
||||
);
|
||||
|
||||
}, sinkMapCountLimit: 20); // 限制模块数量,防止 Context 乱填导致句柄爆炸
|
||||
|
||||
}, sinkMapCountLimit: 2); // 限制日期 Sink 数量 (只需要保持今天和昨天,防止跨天运行时内存不释放)
|
||||
|
||||
// 3.3 Seq 远程输出 (生产监控)
|
||||
if (!string.IsNullOrWhiteSpace(opts.SeqServerUrl))
|
||||
{
|
||||
builder.WriteTo.Async(a => a.Seq(
|
||||
serverUrl: opts.SeqServerUrl,
|
||||
apiKey: opts.SeqApiKey,
|
||||
restrictedToMinimumLevel: opts.SeqLevel
|
||||
));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 4. 生成 Logger 并赋值给全局静态变量
|
||||
// --------------------------------------------------------
|
||||
Log.Logger = builder.CreateLogger();
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 5. 启动后台清理任务 (LogCleaner)
|
||||
// --------------------------------------------------------
|
||||
// 延迟 5 秒执行,避免在程序刚启动的高负载时刻争抢 IO 资源
|
||||
Task.Delay(5000).ContinueWith(_ =>
|
||||
{
|
||||
// 调用我们在 LogCleaner.cs 中定义的静态方法
|
||||
LogCleaner.RunAsync(
|
||||
opts
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭日志系统,确保缓冲区数据写入磁盘/网络
|
||||
/// <para>请在程序退出 (OnExit) 时调用</para>
|
||||
/// </summary>
|
||||
public static void Close()
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
157
Ayay.SerilogLogs/LogCleaner.cs
Normal file
157
Ayay.SerilogLogs/LogCleaner.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ayay.SerilogLogs
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志清理工具
|
||||
/// <para>弥补 Serilog 原生清理功能的不足,支持“按天数”和“按总大小”进行全局清理。</para>
|
||||
/// <para>✅ 已适配多级目录结构,会自动清理删除文件后留下的空文件夹。</para>
|
||||
/// </summary>
|
||||
public static class LogCleaner
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步执行清理任务
|
||||
/// </summary>
|
||||
/// <param name="opts">配置选项</param>
|
||||
public static void RunAsync(LogOptions opts)
|
||||
{
|
||||
string rootPath = opts.LogRootPath; // 日志根目录
|
||||
int maxDays = opts.MaxRetentionDays; // 最大保留天数
|
||||
long maxBytes = opts.MaxTotalLogSize; // 最大总字节数
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
CleanUp(rootPath, maxDays, maxBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "[LogCleaner] 日志自动清理任务执行失败");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void CleanUp(string rootPath, int maxDays, long maxBytes)
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(rootPath);
|
||||
if (!dirInfo.Exists) return;
|
||||
|
||||
// =========================================================
|
||||
// 第一步:清理文件 (递归查找所有子目录)
|
||||
// =========================================================
|
||||
|
||||
// 获取所有 .txt 文件,按最后修改时间升序排列 (最旧的在前面)
|
||||
// SearchOption.AllDirectories 确保了能扫描到 2026-01-15 这种子文件夹里的内容
|
||||
var allFiles = dirInfo.GetFiles("*.txt", SearchOption.AllDirectories)
|
||||
.OrderBy(f => f.LastWriteTime)
|
||||
.ToList();
|
||||
|
||||
if (allFiles.Count == 0) return;
|
||||
|
||||
long currentTotalSize = 0;
|
||||
var cutOffDate = DateTime.Now.Date.AddDays(-maxDays); // 只保留到今天之前的 N 天
|
||||
int deletedCount = 0;
|
||||
|
||||
// --- 策略 A: 按时间清理 ---
|
||||
for (int i = allFiles.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var file = allFiles[i];
|
||||
|
||||
if (file.LastWriteTime.Date < cutOffDate)
|
||||
{
|
||||
try
|
||||
{
|
||||
file.Delete();
|
||||
allFiles.RemoveAt(i);
|
||||
deletedCount++;
|
||||
}
|
||||
catch { /* 忽略占用 */ }
|
||||
}
|
||||
else
|
||||
{
|
||||
currentTotalSize += file.Length;
|
||||
}
|
||||
}
|
||||
|
||||
if (deletedCount > 0)
|
||||
{
|
||||
Log.ForContext("SourceContext", LogModules.Core)
|
||||
.Information("[LogCleaner] 时间策略: 已删除 {Count} 个过期文件", deletedCount);
|
||||
}
|
||||
|
||||
// --- 策略 B: 按总大小清理 ---
|
||||
if (currentTotalSize > maxBytes)
|
||||
{
|
||||
int sizeDeletedCount = 0;
|
||||
long freedBytes = 0;
|
||||
|
||||
foreach (var file in allFiles)
|
||||
{
|
||||
if (currentTotalSize <= maxBytes) break;
|
||||
|
||||
try
|
||||
{
|
||||
long len = file.Length;
|
||||
file.Delete();
|
||||
currentTotalSize -= len;
|
||||
freedBytes += len;
|
||||
sizeDeletedCount++;
|
||||
}
|
||||
catch { /* 忽略占用 */ }
|
||||
}
|
||||
|
||||
if (sizeDeletedCount > 0)
|
||||
{
|
||||
Log.ForContext("SourceContext", LogModules.Core)
|
||||
.Warning("[LogCleaner] 空间策略: 已删除 {Count} 个旧文件, 释放 {SizeMB:F2} MB",
|
||||
sizeDeletedCount, freedBytes / 1024.0 / 1024.0);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// 第二步:清理空文件夹 (新增逻辑)
|
||||
// =========================================================
|
||||
// 目的:当 2026-01-01 文件夹里的文件都被删光后,这个文件夹本身也应该被删掉
|
||||
DeleteEmptyDirectories(dirInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归删除空文件夹
|
||||
/// </summary>
|
||||
private static void DeleteEmptyDirectories(DirectoryInfo dir)
|
||||
{
|
||||
// 1. 先递归处理子文件夹
|
||||
foreach (var subDir in dir.GetDirectories())
|
||||
{
|
||||
DeleteEmptyDirectories(subDir);
|
||||
}
|
||||
|
||||
// 2. 检查当前文件夹是否为空 (且不是根目录自己)
|
||||
try
|
||||
{
|
||||
// 重新刷新状态
|
||||
dir.Refresh();
|
||||
|
||||
// 如果没有文件 且 没有子文件夹
|
||||
if (dir.GetFiles().Length == 0 && dir.GetDirectories().Length == 0)
|
||||
{
|
||||
// 注意:不要删除根目录 LogRootPath
|
||||
// 这里虽然逻辑上递归会删到底,但通常外层调用时传入的是 Logs 根目录,
|
||||
// 只要 LogBootstrapper 里保证 Logs 存在,这里删掉子目录没问题。
|
||||
// 为了安全,可以判断一下是否是根目录的直接子级,或者简单地 try catch 忽略根目录无法删除的异常。
|
||||
|
||||
dir.Delete();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略异常 (比如文件夹正被打开,或者试图删除根目录但被占用)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Ayay.SerilogLogs/LogModules.cs
Normal file
28
Ayay.SerilogLogs/LogModules.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Ayay.SerilogLogs
|
||||
{
|
||||
/// <summary>
|
||||
/// 统一管理系统中的日志模块名称(SourceContext)
|
||||
/// <para>使用常量可以避免硬编码字符串带来的拼写错误</para>
|
||||
/// </summary>
|
||||
public static class LogModules
|
||||
{
|
||||
// --- 核心架构层 ---
|
||||
public const string Core = "Core"; // 系统主逻辑/启动关闭
|
||||
public const string Network = "Network"; // 底层网络通讯 (TCP/UDP)
|
||||
public const string WebApi = "WebAPI"; // 对外 HTTP 接口
|
||||
public const string WebSocket = "WebSocket"; // 实时通讯
|
||||
public const string Ping = "Ping"; // 心跳/Ping包 (通常量大且不重要)
|
||||
|
||||
// --- 业务逻辑层 ---
|
||||
public const string UserSystem = "UserSystem"; // 用户账户/权限系统
|
||||
public const string UserAction = "UserAction"; // 用户操作行为 (审计日志)
|
||||
public const string DeviceOps = "DeviceOps"; // 设备运行/控制指令
|
||||
|
||||
// --- 核心算法层 ---
|
||||
public const string Algorithm = "Algorithm"; // 算法分析/AI识别 (需要上下文追踪)
|
||||
public const string Observation = "Observation"; // 观察点/埋点 (用于调试或统计)
|
||||
|
||||
// --- 第三方集成 ---
|
||||
public const string Sdk = "SDK"; // 第三方 SDK 调用封装
|
||||
}
|
||||
}
|
||||
154
Ayay.SerilogLogs/LogOptions.cs
Normal file
154
Ayay.SerilogLogs/LogOptions.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using Serilog.Events;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ayay.SerilogLogs
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志组件配置选项
|
||||
/// <para>包含身份标识、存储路径、分级策略以及自动清理策略。</para>
|
||||
/// </summary>
|
||||
public class LogOptions
|
||||
{
|
||||
// ==========================================
|
||||
// 1. 基础身份标识
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 应用名称/服务ID。
|
||||
/// <para>例如: "VideoServer-01", "Gatekeeper-Api"。</para>
|
||||
/// <para>在 Seq 中对应 {AppId} 属性,用于区分多服务环境下的日志来源。</para>
|
||||
/// </summary>
|
||||
public string AppId { get; set; } = "DefaultApp";
|
||||
|
||||
// ==========================================
|
||||
// 2. 存储路径配置
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 本地日志文件的存储根目录。
|
||||
/// <para>默认: @"D:\Logs"</para>
|
||||
/// <para>程序会自动在此目录下按模块创建子文件夹(如 System, Network)。</para>
|
||||
/// <para>注意:如果该目录无写入权限,组件会自动降级到程序运行目录。</para>
|
||||
/// </summary>
|
||||
public string LogRootPath { get; set; } = @"D:\Logs";
|
||||
|
||||
// ==========================================
|
||||
// 3. Seq 集中式日志配置
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Seq 服务器地址。
|
||||
/// <para>例如: "http://192.168.1.100:5341"</para>
|
||||
/// <para>如果留空 (null/empty),则不启用 Seq 投递。</para>
|
||||
/// </summary>
|
||||
public string SeqServerUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Seq API Key (令牌)。
|
||||
/// <para>建议在 Seq 后台申请仅具有 [Ingest] 权限的 Key。</para>
|
||||
/// <para>配置 Key 后支持动态调整日志级别 (Dynamic Level Control)。</para>
|
||||
/// </summary>
|
||||
public string SeqApiKey { get; set; }
|
||||
|
||||
// ==========================================
|
||||
// 4. 输出端级别控制 (Sink Levels)
|
||||
// 用于控制不同媒介的“过滤网”疏密程度
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 控制台输出的最低级别。
|
||||
/// <para>默认: Information (开发调试时可改为 Debug)</para>
|
||||
/// </summary>
|
||||
public LogEventLevel ConsoleLevel { get; set; } = LogEventLevel.Information;
|
||||
|
||||
/// <summary>
|
||||
/// 本地文件记录的最低级别。
|
||||
/// <para>默认: Debug (保留详细案底,便于事后追溯)</para>
|
||||
/// <para>注意:具体写入哪个文件(Main/Detail)由内部逻辑决定,此属性控制总开关。</para>
|
||||
/// </summary>
|
||||
public LogEventLevel FileLevel { get; set; } = LogEventLevel.Verbose;
|
||||
|
||||
/// <summary>
|
||||
/// Seq 网络传输的最低级别。
|
||||
/// <para>默认: Information (减少网络带宽和服务器存储压力)</para>
|
||||
/// <para>生产环境建议设为 Information 或 Warning,除非需要在线排错。</para>
|
||||
/// </summary>
|
||||
public LogEventLevel SeqLevel { get; set; } = LogEventLevel.Verbose;
|
||||
|
||||
// ==========================================
|
||||
// 5. 业务模块级别控制 (Context Levels)
|
||||
// 用于精细化控制特定业务模块的日志开关
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 全局默认最低级别。
|
||||
/// <para>如果某个日志没有指定模块,或者模块不在 ModuleLevels 列表中,则使用此级别。</para>
|
||||
/// </summary>
|
||||
public LogEventLevel GlobalMinimumLevel { get; set; } = LogEventLevel.Verbose;
|
||||
|
||||
/// <summary>
|
||||
/// 针对特定业务模块的日志级别覆盖 (Override)。
|
||||
/// <para>Key: 模块名称 (建议使用 LogModules 常量字符串)</para>
|
||||
/// <para>Value: 该模块允许记录的最低级别</para>
|
||||
/// </summary>
|
||||
public Dictionary<string, LogEventLevel> ModuleLevels { get; set; } = new Dictionary<string, LogEventLevel>
|
||||
{
|
||||
// --- 系统层 ---
|
||||
{ LogModules.Core, LogEventLevel.Debug }, // 系统主逻辑
|
||||
{ LogModules.Network, LogEventLevel.Debug }, // 网络通讯:平时只看警告,防止心跳刷屏
|
||||
{ LogModules.WebApi, LogEventLevel.Debug }, // WebAPI:记录请求响应
|
||||
|
||||
// --- 业务层 ---
|
||||
{ LogModules.UserSystem, LogEventLevel.Debug }, // 用户系统
|
||||
{ LogModules.UserAction, LogEventLevel.Debug }, // 用户操作:必须记录,用于审计
|
||||
{ LogModules.DeviceOps, LogEventLevel.Debug }, // 设备操作:记录关键指令
|
||||
|
||||
// --- 核心/高频数据 ---
|
||||
{ LogModules.Algorithm, LogEventLevel.Debug }, // 算法:核心业务,开启 Debug 以记录全过程
|
||||
{ LogModules.Observation, LogEventLevel.Debug }, // 观察点:最详细的埋点
|
||||
|
||||
// --- 降噪区 (垃圾数据屏蔽) ---
|
||||
{ LogModules.WebSocket, LogEventLevel.Debug }, // WS:数据量极大,除非报错否则不记
|
||||
{ LogModules.Ping, LogEventLevel.Debug }, // Ping:几乎不记,除非完全断连
|
||||
{ LogModules.Sdk, LogEventLevel.Debug } // SDK:屏蔽第三方的废话日志
|
||||
};
|
||||
|
||||
// ==========================================
|
||||
// 6. 文件滚动策略 (Rolling Policy)
|
||||
// 控制单个日志文件的大小和生成频率
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 单个日志文件大小限制 (单位:字节)。
|
||||
/// <para>默认: 10MB (10 * 1024 * 1024)</para>
|
||||
/// <para>当文件超过此大小时,会自动创建新文件 (例如 Main_001.txt)。</para>
|
||||
/// <para>建议不要设置过大,否则记事本打开会很卡。</para>
|
||||
/// </summary>
|
||||
public long FileSizeLimitBytes { get; set; } = 10 * 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// 超过大小限制后是否创建新文件。
|
||||
/// <para>默认: true (推荐)</para>
|
||||
/// </summary>
|
||||
public bool RollOnFileSizeLimit { get; set; } = true;
|
||||
|
||||
// ==========================================
|
||||
// 7. 自动清理策略 (Auto Cleanup)
|
||||
// 由后台 LogCleaner 任务执行,满足任意条件即清理
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 历史日志最大保留天数 (时间策略)。
|
||||
/// <para>默认: 30天</para>
|
||||
/// <para>系统会强制删除最后修改时间超过此天数的文件。</para>
|
||||
/// </summary>
|
||||
public int MaxRetentionDays { get; set; } = 30;
|
||||
|
||||
/// <summary>
|
||||
/// 日志目录总大小上限 (空间策略)。
|
||||
/// <para>默认: 1GB (1024 * 1024 * 1024)</para>
|
||||
/// <para>如果所有日志文件总和超过此大小,系统会按时间倒序(从旧到新)删除文件,直到空间低于此值。</para>
|
||||
/// </summary>
|
||||
public long MaxTotalLogSize { get; set; } = 1024L * 1024 * 1024;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user