规范并补充日志内容

This commit is contained in:
2026-01-16 14:30:42 +08:00
parent 4e0bb33ce2
commit fd6a82eb4e
28 changed files with 325 additions and 537 deletions

View File

@@ -1,4 +1,7 @@
namespace SHH.CameraSdk
using Ayay.SerilogLogs;
using Serilog;
namespace SHH.CameraSdk
{
#region --- (Frame Processor Cluster) ---
@@ -15,6 +18,9 @@
where TWorker : BaseWorker
{
#region --- ---
private static ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
/// <summary> Worker 线程池,负责具体的帧处理任务 </summary>
protected readonly List<TWorker> _workers = new List<TWorker>();
@@ -39,7 +45,10 @@
{
// 校验并行度参数,避免无效配置
if (workerCount < 1)
throw new ArgumentOutOfRangeException(nameof(workerCount), "Worker数量必须大于0");
{
_sysLog.Error("[Core] 帧处理集群初始化失败, 线程数必须 > 0.");
throw new ArgumentOutOfRangeException(nameof(workerCount), "帧处理集群初始化失败, 线程数必须 > 0.");
}
_configManager = configManager; // 先赋值配置管理器
_workerCount = workerCount;
@@ -50,7 +59,7 @@
_workers.Add(CreateWorker(i));
}
Console.WriteLine($"[{serviceName}] 服务已初始化 (并行度: {workerCount})");
_sysLog.Information($"[Core] 帧处理集群初始化成功, {serviceName} 并行数 {workerCount}. 注: 不能大于CPU核心数.");
}
#endregion
@@ -156,6 +165,8 @@
{
#region --- ---
private static ILogger _gRpcLog = Log.ForContext("SourceContext", LogModules.gRpc);
/// <summary> 线程内任务队列容量限制100防止内存溢出 </summary>
private readonly BlockingCollection<(long DeviceId, SmartFrame Frame, FrameDecision Decision)> _taskQueue = new BlockingCollection<(long, SmartFrame, FrameDecision)>(100);
@@ -195,7 +206,7 @@
{
// 背压处理:丢弃当前帧,释放引用计数
frame.Dispose();
Console.WriteLine($"[Worker] 任务队列已满,丢弃设备 {deviceId} 的帧 (引用计数已释放)");
_gRpcLog.Debug($"[gRpc] 任务队列已满,BaseWorker 丢弃设备 {deviceId} 的帧.");
}
}
@@ -228,7 +239,7 @@
}
catch (Exception ex)
{
Console.WriteLine($"[Worker] 帧处理异常: {ex.Message}");
_gRpcLog.Information($"[gRpc] 帧处理异常BaseWorker 异常消息: {ex.Message}.");
// 异常保底策略:即使处理失败,也透传帧到下一个环节,保证流水线不中断
NotifyFinished(taskItem.DeviceId, frame, taskItem.Decision);
@@ -239,11 +250,11 @@
catch (OperationCanceledException)
{
// 正常取消:线程退出,无需报错
Console.WriteLine("[Worker] 处理循环已正常终止");
_gRpcLog.Information($"[gRpc] BaseWorker 处理循环已正常终止.");
}
catch (Exception ex)
{
Console.WriteLine($"[Worker] 处理循环异常终止: {ex.Message}");
_gRpcLog.Error($"[gRpc] BaseWorker 处理循环异常终止.");
}
}

View File

@@ -1,4 +1,6 @@
using OpenCvSharp;
using Ayay.SerilogLogs;
using OpenCvSharp;
using Serilog;
namespace SHH.CameraSdk
{
@@ -17,6 +19,9 @@ namespace SHH.CameraSdk
public class DisplayWindowManager : IDisposable
{
#region --- ---
private static ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
/// <summary>
/// 单个窗口的上下文信息载体
/// 存储设备关联、运行状态、回调函数等核心数据
@@ -64,7 +69,7 @@ namespace SHH.CameraSdk
_cameraManager = cameraManager;
// 启动长驻UI线程设置 LongRunning 提升调度优先级
_uiThread = Task.Factory.StartNew(UILoop, TaskCreationOptions.LongRunning);
Console.WriteLine("[DisplayManager] 渲染引擎就绪 (防僵尸窗口终极版)");
_sysLog.Information("[DisplayManager] 渲染引擎就绪 (防僵尸窗口终极版)");
}
/// <summary>
@@ -77,7 +82,7 @@ namespace SHH.CameraSdk
// 2. 等待 UI 线程退出最多等待1秒防止卡死
try { _uiThread.Wait(1000); }
catch (Exception ex) { Console.WriteLine($"[DisplayManager] 线程退出异常: {ex.Message}"); }
catch (Exception ex) { _sysLog.Error($"[DisplayManager] 线程退出异常: {ex.Message}"); }
// 3. 强制清理所有活跃窗口
foreach (var appId in _activeWindows.Keys)
@@ -95,7 +100,7 @@ namespace SHH.CameraSdk
_uiActionQueue.Dispose();
_cts.Dispose();
Console.WriteLine("[DisplayManager] 渲染引擎已安全销毁");
_sysLog.Information("[DisplayManager] 渲染引擎已安全销毁");
}
#endregion
@@ -110,7 +115,7 @@ namespace SHH.CameraSdk
// 防重入:已存在该窗口则直接返回
if (_activeWindows.ContainsKey(appId)) return;
Console.WriteLine($"[DisplayManager] 正在启动窗口: {appId} -> Device {deviceId}");
_sysLog.Information($"[DisplayManager] 正在启动窗口: {appId} -> Device {deviceId}");
// 初始化窗口上下文
var context = new WindowContext
@@ -126,7 +131,7 @@ namespace SHH.CameraSdk
if (mouseEvent == MouseEventTypes.LButtonDown)
{
context.IsPaused = !context.IsPaused;
Console.WriteLine($"[DisplayManager] 窗口 {appId} 状态切换: {(context.IsPaused ? "" : "")}");
_sysLog.Information($"[DisplayManager] 窗口 {appId} 状态切换: {(context.IsPaused ? "" : "")}");
}
};
@@ -148,7 +153,7 @@ namespace SHH.CameraSdk
}
catch (Exception ex)
{
Console.WriteLine($"[DisplayManager] 窗口 {appId} 初始化失败: {ex.Message}");
_sysLog.Error($"[DisplayManager] 窗口 {appId} 初始化失败: {ex.Message}");
}
});
@@ -179,7 +184,7 @@ namespace SHH.CameraSdk
}
catch (Exception ex)
{
Console.WriteLine($"[DisplayManager] 帧克隆失败: {ex.Message}");
_sysLog.Error($"[DisplayManager] 帧克隆失败: {ex.Message}");
return;
}
@@ -223,7 +228,7 @@ namespace SHH.CameraSdk
// 从注册表中移除窗口上下文
if (_activeWindows.TryRemove(appId, out var context))
{
Console.WriteLine($"[DisplayManager] 正在清理窗口资源: {appId}");
_sysLog.Information($"[DisplayManager] 正在清理窗口资源: {appId}");
// 步骤1立即取消帧数据流订阅
GlobalStreamDispatcher.Unsubscribe(appId);
@@ -237,7 +242,7 @@ namespace SHH.CameraSdk
}
catch (Exception ex)
{
Console.WriteLine($"[DisplayManager] 窗口 {appId} 销毁失败: {ex.Message}");
_sysLog.Error($"[DisplayManager] 窗口 {appId} 销毁失败: {ex.Message}");
}
});
@@ -257,7 +262,7 @@ namespace SHH.CameraSdk
if (_activeWindows.TryGetValue(appId, out var ctx))
{
ctx.IsPaused = true;
Console.WriteLine($"[DisplayManager] 窗口 {appId} 已暂停");
_sysLog.Information($"[DisplayManager] 窗口 {appId} 已暂停");
}
}
@@ -270,7 +275,7 @@ namespace SHH.CameraSdk
if (_activeWindows.TryGetValue(appId, out var ctx))
{
ctx.IsPaused = false;
Console.WriteLine($"[DisplayManager] 窗口 {appId} 已恢复");
_sysLog.Information($"[DisplayManager] 窗口 {appId} 已恢复");
}
}
#endregion
@@ -290,7 +295,7 @@ namespace SHH.CameraSdk
var device = _cameraManager.GetDevice(deviceId);
if (device == null)
{
Console.WriteLine($"[策略联动] 设备 {deviceId} 不存在");
_sysLog.Information($"[策略联动] 设备 {deviceId} 不存在");
return;
}
@@ -298,7 +303,7 @@ namespace SHH.CameraSdk
var frameController = device.Controller;
if (frameController == null)
{
Console.WriteLine($"[策略联动] 设备 {deviceId} 未配置帧控制器");
_sysLog.Information($"[策略联动] 设备 {deviceId} 未配置帧控制器");
return;
}
@@ -306,12 +311,12 @@ namespace SHH.CameraSdk
if (fps > 0)
{
frameController.Register(appId, fps);
Console.WriteLine($"[策略联动] ✅ 已注册流控: {appId} -> {fps} FPS");
_sysLog.Information($"[策略联动] ✅ 已注册流控: {appId} -> {fps} FPS");
}
else
{
frameController.Unregister(appId);
Console.WriteLine($"[策略联动] 🗑️ 已注销流控: {appId}");
_sysLog.Information($"[策略联动] 🗑️ 已注销流控: {appId}");
}
// 记录审计日志,用于前端排查问题
@@ -319,7 +324,7 @@ namespace SHH.CameraSdk
}
catch (Exception ex)
{
Console.WriteLine($"[策略联动] ❌ 联动失败: {ex.Message}");
_sysLog.Error($"[策略联动] ❌ 联动失败: {ex.Message}");
}
}
#endregion
@@ -360,7 +365,7 @@ namespace SHH.CameraSdk
}
catch (Exception ex)
{
Console.WriteLine($"[UI] 渲染循环异常: {ex.Message}");
_sysLog.Error($"[UI] 渲染循环异常: {ex.Message}");
}
}
@@ -385,7 +390,7 @@ namespace SHH.CameraSdk
// Visible < 1.0 表示窗口已被用户手动关闭
if (Cv2.GetWindowProperty(appId, WindowPropertyFlags.Visible) < 1.0)
{
Console.WriteLine($"[UI] 检测到窗口 {appId} 已被手动关闭,触发清理...");
_sysLog.Information($"[UI] 检测到窗口 {appId} 已被手动关闭,触发清理...");
// 异步清理:避免 StopDisplay 内部的队列操作阻塞 UI 线程
Task.Run(() => StopDisplay(appId));
}

View File

@@ -1,172 +0,0 @@
using System.Text.Json;
namespace SHH.CameraSdk;
public class FileStorageService : IStorageService
{
public int ProcessId { get; }
private readonly string _baseDir;
private readonly string _devicesPath;
private readonly string _systemLogPath; // 系统日志路径
private readonly string _logsDir; // 设备日志文件夹
// 【关键优化】双锁分离:配置读写和日志读写互不干扰
private readonly SemaphoreSlim _configLock = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim _logLock = new SemaphoreSlim(1, 1);
// JSON 配置 (保持不变)
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
IncludeFields = true,
PropertyNameCaseInsensitive = true,
NumberHandling = JsonNumberHandling.AllowReadingFromString
};
public FileStorageService(int processId)
{
ProcessId = processId;
// 目录结构:
// App_Data/Process_1/
// ├── devices.json
// ├── system.log
// └── logs/
// ├── device_101.log
// └── device_102.log
_baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", $"Process_{processId}");
_devicesPath = Path.Combine(_baseDir, "devices.json");
_systemLogPath = Path.Combine(_baseDir, "system.log");
_logsDir = Path.Combine(_baseDir, "logs");
if (!Directory.Exists(_baseDir)) Directory.CreateDirectory(_baseDir);
if (!Directory.Exists(_logsDir)) Directory.CreateDirectory(_logsDir);
Console.WriteLine($"[Storage] 服务就绪 | 日志路径: {_systemLogPath}");
}
// ==================================================================
// 1. 设备配置管理 (使用 _configLock)
// ==================================================================
public async Task SaveDevicesAsync(IEnumerable<VideoSourceConfig> configs)
{
await _configLock.WaitAsync();
try
{
if (SdkGlobal.SaveCameraConfigEnable)
{
var json = JsonSerializer.Serialize(configs, _jsonOptions);
await File.WriteAllTextAsync(_devicesPath, json);
}
}
catch (Exception ex)
{
Console.WriteLine($"[Storage] ❌ 保存配置失败: {ex.Message}");
}
finally { _configLock.Release(); }
}
public async Task<List<VideoSourceConfig>> LoadDevicesAsync()
{
if (!File.Exists(_devicesPath)) return new List<VideoSourceConfig>();
await _configLock.WaitAsync();
try
{
if (!SdkGlobal.SaveCameraConfigEnable)
return new List<VideoSourceConfig>();
var json = await File.ReadAllTextAsync(_devicesPath);
if (string.IsNullOrWhiteSpace(json)) return new List<VideoSourceConfig>();
var list = JsonSerializer.Deserialize<List<VideoSourceConfig>>(json, _jsonOptions);
return list ?? new List<VideoSourceConfig>();
//return new List<VideoSourceConfig>();
}
catch (Exception ex)
{
Console.WriteLine($"[Storage] ❌ 读取配置失败: {ex.Message}");
return new List<VideoSourceConfig>();
}
finally { _configLock.Release(); }
}
// ==================================================================
// 2. 系统操作日志 (使用 _logLock)
// ==================================================================
public async Task AppendSystemLogAsync(string action, string ip, string path)
{
// 格式: [时间] | IP | 动作 路径
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var line = $"[{time}] | {ip} | {action} {path}";
await _logLock.WaitAsync(); // 等待日志锁
try
{
// 追加写入 (Async)
await File.AppendAllTextAsync(_systemLogPath, line + Environment.NewLine);
}
catch { /* 忽略日志写入错误,别崩了主程序 */ }
finally { _logLock.Release(); }
}
public async Task<List<string>> GetSystemLogsAsync(int count)
{
if (!File.Exists(_systemLogPath)) return new List<string> { "暂无日志" };
await _logLock.WaitAsync();
try
{
// 读取所有行 (如果日志文件非常大这里建议优化为倒序读取但几MB以内没问题)
var lines = await File.ReadAllLinesAsync(_systemLogPath);
// 取最后 N 行,并反转(让最新的显示在最上面)
return lines.TakeLast(count).Reverse().ToList();
}
catch (Exception ex)
{
return new List<string> { $"读取失败: {ex.Message}" };
}
finally { _logLock.Release(); }
}
// ==================================================================
// 3. 设备审计日志 (使用 _logLock)
// ==================================================================
public async Task AppendDeviceLogAsync(int deviceId, string message)
{
var path = Path.Combine(_logsDir, $"device_{deviceId}.log");
var time = DateTime.Now.ToString("MM-dd HH:mm:ss");
var line = $"{time} > {message}";
await _logLock.WaitAsync(); // 复用日志锁防止多文件同时IO导致磁盘抖动
try
{
await File.AppendAllTextAsync(path, line + Environment.NewLine);
}
catch { }
finally { _logLock.Release(); }
}
public async Task<List<string>> GetDeviceLogsAsync(int deviceId, int count)
{
var path = Path.Combine(_logsDir, $"device_{deviceId}.log");
if (!File.Exists(path)) return new List<string>();
await _logLock.WaitAsync();
try
{
var lines = await File.ReadAllLinesAsync(path);
return lines.TakeLast(count).Reverse().ToList();
}
catch
{
return new List<string>();
}
finally { _logLock.Release(); }
}
}

View File

@@ -1,4 +1,7 @@
namespace SHH.CameraSdk;
using Ayay.SerilogLogs;
using Serilog;
namespace SHH.CameraSdk;
/// <summary>
/// [配置中心] 预处理参数管理器
@@ -6,6 +9,8 @@
/// </summary>
public class ProcessingConfigManager
{
private static ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
// 内存字典Key=设备ID, Value=配置对象
private readonly ConcurrentDictionary<long, ProcessingOptions> _configs = new();
@@ -30,7 +35,7 @@ public class ProcessingConfigManager
// 直接覆盖旧配置,由于是引用替换,原子性较高
_configs.AddOrUpdate(deviceId, newOptions, (key, old) => newOptions);
Console.WriteLine($"[ConfigManager] 设备 {deviceId} 预处理参数已更新: " +
_sysLog.Information($"[ConfigManager] 设备 {deviceId} 预处理参数已更新: " +
$"Expand={newOptions.EnableExpand} Shrink:{newOptions.EnableShrink} 分辨率:({newOptions.TargetWidth}x{newOptions.TargetHeight}), " +
$"EnableBrightness}}={newOptions.EnableBrightness}");
}