完善海康 SDK 日志
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
|
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
|
||||||
<PackageReference Include="Serilog.Enrichers.Process" Version="3.0.0" />
|
<PackageReference Include="Serilog.Enrichers.Process" Version="3.0.0" />
|
||||||
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
|
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ namespace Ayay.SerilogLogs
|
|||||||
// 强制覆盖微软自带的啰嗦日志
|
// 强制覆盖微软自带的啰嗦日志
|
||||||
builder.MinimumLevel.Override("Microsoft", LogEventLevel.Warning);
|
builder.MinimumLevel.Override("Microsoft", LogEventLevel.Warning);
|
||||||
builder.MinimumLevel.Override("System", LogEventLevel.Warning);
|
builder.MinimumLevel.Override("System", LogEventLevel.Warning);
|
||||||
|
builder.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Warning);
|
||||||
|
|
||||||
// 2.3 注入全套元数据 (Enrichers) - 让日志更聪明
|
// 2.3 注入全套元数据 (Enrichers) - 让日志更聪明
|
||||||
builder
|
builder
|
||||||
|
|||||||
@@ -652,16 +652,26 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// 异步销毁在后台执行,避免阻塞 UI 线程
|
// 触发异步销毁,但设定一个超时兜底,防止永久卡死 UI
|
||||||
Task.Run(async () => await DisposeAsync().ConfigureAwait(false)).GetAwaiter().GetResult();
|
// 这里等待 2 秒,如果还没销毁完也强行返回,避免界面冻结
|
||||||
|
Task.Run(async () => await DisposeAsync().ConfigureAwait(false))
|
||||||
|
.Wait(TimeSpan.FromSeconds(2));
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private volatile bool _isDisposed = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步销毁资源(优雅关闭)
|
/// 异步销毁资源(优雅关闭)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>ValueTask</returns>
|
/// <returns>ValueTask</returns>
|
||||||
public virtual async ValueTask DisposeAsync()
|
public virtual async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
|
// 防止重复 Dispose
|
||||||
|
if (_isDisposed) return;
|
||||||
|
_isDisposed = true;
|
||||||
|
|
||||||
// 1. 停止业务逻辑
|
// 1. 停止业务逻辑
|
||||||
await StopAsync().ConfigureAwait(false);
|
await StopAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
if (!HikSdkManager.Initialize())
|
if (!HikSdkManager.Initialize())
|
||||||
{
|
{
|
||||||
_sdkLog.Error("[SDK] HikVision Sdk 初始化失败.");
|
_sdkLog.Error("[SDK] HikVision Sdk 初始化失败.");
|
||||||
|
AddAuditLog($"[SDK] HikVision Sdk 初始化失败.");
|
||||||
|
|
||||||
throw new CameraException(CameraErrorCode.SdkNotInitialized, "HikVision Sdk 初始化失败.", DeviceBrand.HikVision);
|
throw new CameraException(CameraErrorCode.SdkNotInitialized, "HikVision Sdk 初始化失败.", DeviceBrand.HikVision);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,16 +130,22 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
if (currentEpoch != _connectionEpoch)
|
if (currentEpoch != _connectionEpoch)
|
||||||
{
|
{
|
||||||
if (newUserId >= 0) HikNativeMethods.NET_DVR_Logout(newUserId);
|
if (newUserId >= 0) HikNativeMethods.NET_DVR_Logout(newUserId);
|
||||||
|
_sdkLog.Information($"[SDK] Hik 启动任务已过期 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}");
|
||||||
|
AddAuditLog($"[SDK] Hik 启动任务已过期 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}");
|
||||||
throw new OperationCanceledException("启动任务已过期");
|
throw new OperationCanceledException("启动任务已过期");
|
||||||
}
|
}
|
||||||
|
|
||||||
_userId = newUserId;
|
if (newUserId < 0)
|
||||||
if (_userId < 0)
|
|
||||||
{
|
{
|
||||||
uint err = HikNativeMethods.NET_DVR_GetLastError();
|
uint err = HikNativeMethods.NET_DVR_GetLastError();
|
||||||
|
|
||||||
|
_sdkLog.Warning($"[SDK] Hik 登录失败 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}");
|
||||||
|
AddAuditLog($"[SDK] Hik 登录失败 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}");
|
||||||
throw new CameraException(HikErrorMapper.Map(err), $"登录失败: {err}", DeviceBrand.HikVision, (int)err);
|
throw new CameraException(HikErrorMapper.Map(err), $"登录失败: {err}", DeviceBrand.HikVision, (int)err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_userId = newUserId;
|
||||||
|
|
||||||
_instances.TryAdd(_userId, this);
|
_instances.TryAdd(_userId, this);
|
||||||
|
|
||||||
_sdkLog.Information($"[SDK] Hik 登录成功 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}");
|
_sdkLog.Information($"[SDK] Hik 登录成功 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}");
|
||||||
@@ -155,8 +163,8 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_sdkLog.Error($"[SDK] Hik 启动异常. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}");
|
_sdkLog.Error($"[SDK] Hik 启动异常. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}, " + "Exception:{Exp}", ex);
|
||||||
AddAuditLog($"[SDK] Hik 启动异常. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}");
|
AddAuditLog($"[SDK] Hik 启动异常. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}, Exception:{ex.Message}");
|
||||||
CleanupSync();
|
CleanupSync();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
@@ -181,18 +189,46 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
lock (_initLock)
|
lock (_initLock)
|
||||||
{
|
{
|
||||||
// 1. 停止预览
|
// 1. 停止预览
|
||||||
if (_realPlayHandle >= 0)
|
try
|
||||||
{
|
{
|
||||||
HikNativeMethods.NET_DVR_StopRealPlay(_realPlayHandle);
|
if (_realPlayHandle >= 0)
|
||||||
_realPlayHandle = -1;
|
{
|
||||||
|
HikNativeMethods.NET_DVR_StopRealPlay(_realPlayHandle);
|
||||||
|
_realPlayHandle = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
_sdkLog.Debug($"[SDK] Hik 停止预览失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
|
||||||
|
AddAuditLog($"[SDK] Hik 停止预览失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 停止解码
|
// 2. 停止解码
|
||||||
if (_playPort >= 0)
|
if (_playPort >= 0)
|
||||||
{
|
{
|
||||||
HikPlayMethods.PlayM4_Stop(_playPort);
|
try
|
||||||
HikPlayMethods.PlayM4_CloseStream(_playPort);
|
{
|
||||||
HikPlayMethods.PlayM4_FreePort(_playPort);
|
HikPlayMethods.PlayM4_Stop(_playPort);
|
||||||
|
HikPlayMethods.PlayM4_CloseStream(_playPort);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_sdkLog.Debug($"[SDK] Hik 停止解码失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
|
||||||
|
AddAuditLog($"[SDK] Hik 停止解码失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HikPlayMethods.PlayM4_FreePort(_playPort);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_sdkLog.Warning($"[SDK] Hik 端口资源释放失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
|
||||||
|
AddAuditLog($"[SDK] Hik 端口资源释放失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_playPort = -1;
|
_playPort = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,18 +239,33 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 注销登录
|
// 3. 注销登录
|
||||||
if (_userId >= 0)
|
try
|
||||||
{
|
{
|
||||||
_instances.TryRemove(_userId, out _);
|
if (_userId >= 0)
|
||||||
HikNativeMethods.NET_DVR_Logout(_userId);
|
{
|
||||||
_userId = -1;
|
_instances.TryRemove(_userId, out _);
|
||||||
|
HikNativeMethods.NET_DVR_Logout(_userId);
|
||||||
|
_userId = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_sdkLog.Warning($"[SDK] Hik 注销登录失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
|
||||||
|
AddAuditLog($"[SDK] Hik 注销登录失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
_framePool?.Dispose();
|
_framePool?.Dispose();
|
||||||
_framePool = null;
|
_framePool = null;
|
||||||
_isPoolReady = false;
|
_isPoolReady = false;
|
||||||
}
|
}
|
||||||
HikSdkManager.Uninitialize();
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HikSdkManager.Uninitialize();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -234,6 +285,15 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
|
|
||||||
lock (_initLock)
|
lock (_initLock)
|
||||||
{
|
{
|
||||||
|
// 【修复点】双重检查在线状态
|
||||||
|
// 如果在拿锁的过程中,外部已经调用了 StopAsync,这里必须停止,否则会创建"僵尸句柄"
|
||||||
|
if (!IsOnline || !IsPhysicalOnline || _userId < 0)
|
||||||
|
{
|
||||||
|
_sdkLog.Warning($"[SDK] 码流切换被取消,设备已离线.");
|
||||||
|
AddAuditLog($"[SDK] 码流切换被取消,设备已离线.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// A. 停止预览 (Keep Login)
|
// A. 停止预览 (Keep Login)
|
||||||
if (_realPlayHandle >= 0)
|
if (_realPlayHandle >= 0)
|
||||||
{
|
{
|
||||||
@@ -264,6 +324,10 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
uint err = HikNativeMethods.NET_DVR_GetLastError();
|
uint err = HikNativeMethods.NET_DVR_GetLastError();
|
||||||
_sdkLog.Information($"[SDK] Hik 码流切换失败. => Err:{err}, ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}, 播放句柄:{_realPlayHandle}");
|
_sdkLog.Information($"[SDK] Hik 码流切换失败. => Err:{err}, ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}, 播放句柄:{_realPlayHandle}");
|
||||||
AddAuditLog($"[SDK] Hik 码流切换失败. => Err:{err}, ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}, 播放句柄:{_realPlayHandle}");
|
AddAuditLog($"[SDK] Hik 码流切换失败. => Err:{err}, ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}, 播放句柄:{_realPlayHandle}");
|
||||||
|
|
||||||
|
// 【修复点】主动上报错误,让基类感知到当前已经断流了
|
||||||
|
// 这会将状态置为 Reconnecting,并可能触发自动重连
|
||||||
|
ReportError(new CameraException(HikErrorMapper.Map(err), "Hik 码流切换失败.", DeviceBrand.HikVision));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,7 +407,11 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
HikPlayMethods.PlayM4_InputData(_playPort, pBuffer, dwBufSize);
|
HikPlayMethods.PlayM4_InputData(_playPort, pBuffer, dwBufSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
_sdkLog.Debug($"[SDK] Hik SafeOnRealDataReceived 异常. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
|
||||||
|
AddAuditLog($"[SDK] Hik SafeOnRealDataReceived 异常. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -351,10 +419,16 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
#region --- 解码与帧分发 (Decoding) ---
|
#region --- 解码与帧分发 (Decoding) ---
|
||||||
|
|
||||||
// 必须同时加上 SecurityCritical
|
// 必须同时加上 SecurityCritical
|
||||||
[HandleProcessCorruptedStateExceptions]
|
//[HandleProcessCorruptedStateExceptions]
|
||||||
[SecurityCritical]
|
//[SecurityCritical]
|
||||||
private void SafeOnDecodingCallBack(int nPort, IntPtr pBuf, int nSize, ref HikPlayMethods.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
|
private void SafeOnDecodingCallBack(int nPort, IntPtr pBuf, int nSize, ref HikPlayMethods.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
|
||||||
{
|
{
|
||||||
|
// 防御性检查,防止传入空指针导致 OpenCV 崩溃(CSE异常)
|
||||||
|
if (pBuf == IntPtr.Zero || nSize <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// [优化] 维持心跳,防止被哨兵误杀
|
// [优化] 维持心跳,防止被哨兵误杀
|
||||||
MarkFrameReceived(0);
|
MarkFrameReceived(0);
|
||||||
|
|
||||||
@@ -380,14 +454,38 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
// 2. 初始化帧池
|
// 2. 初始化帧池
|
||||||
if (!_isPoolReady)
|
if (!_isPoolReady)
|
||||||
{
|
{
|
||||||
lock (_initLock)
|
// ====================================================================================
|
||||||
|
// 【修改点 Start】: 使用 Monitor.TryEnter 替换 lock
|
||||||
|
// 原因:防止死锁。如果主线程 CleanupSync 持有 _initLock 正在 Stop,
|
||||||
|
// 这里如果用 lock 会死等,导致 StopRealPlay 无法返回。
|
||||||
|
// 改用 TryEnter,如果拿不到锁(说明正在停止),直接放弃这一帧并退出。
|
||||||
|
// ====================================================================================
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (!_isPoolReady)
|
// 尝试获取锁,超时时间 0ms (拿不到立即返回 false)
|
||||||
|
Monitor.TryEnter(_initLock, 0, ref lockTaken);
|
||||||
|
|
||||||
|
if (lockTaken)
|
||||||
{
|
{
|
||||||
_framePool?.Dispose();
|
// 拿到锁了,执行原有的初始化逻辑 (Double Check)
|
||||||
_framePool = new FramePool(width, height, MatType.CV_8UC3, initialSize: 3, maxSize: 5);
|
if (!_isPoolReady)
|
||||||
_isPoolReady = true;
|
{
|
||||||
|
_framePool?.Dispose();
|
||||||
|
_framePool = new FramePool(width, height, MatType.CV_8UC3, initialSize: 3, maxSize: 5);
|
||||||
|
_isPoolReady = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 【关键逻辑】没拿到锁,说明主线程正在操作 (通常是正在 Stop)
|
||||||
|
// 既然都要停止了,这一帧直接丢弃,立即返回,防止死锁
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) Monitor.Exit(_initLock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +493,10 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
|
|
||||||
// 3. 转换与分发
|
// 3. 转换与分发
|
||||||
SmartFrame smartFrame = _framePool.Get();
|
SmartFrame smartFrame = _framePool.Get();
|
||||||
|
|
||||||
|
// 【标志位】用于判断所有权是否成功移交
|
||||||
|
bool handoverSuccess = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (smartFrame == null) return; // 池满丢帧
|
if (smartFrame == null) return; // 池满丢帧
|
||||||
@@ -416,17 +518,25 @@ public class HikVideoSource : BaseVideoSource,
|
|||||||
// decision.TargetAppIds 包含了 "谁需要这一帧" 的信息
|
// decision.TargetAppIds 包含了 "谁需要这一帧" 的信息
|
||||||
//GlobalProcessingCenter.Submit(this.Id, smartFrame, decision);
|
//GlobalProcessingCenter.Submit(this.Id, smartFrame, decision);
|
||||||
GlobalPipelineRouter.Enqueue(Id, smartFrame, decision);
|
GlobalPipelineRouter.Enqueue(Id, smartFrame, decision);
|
||||||
|
|
||||||
|
// 标记成功,禁止 finally 块销毁对象
|
||||||
|
handoverSuccess = true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
smartFrame.Dispose();
|
|
||||||
// 这里为了性能不频繁写日志,仅在调试时开启
|
// 这里为了性能不频繁写日志,仅在调试时开启
|
||||||
// Debug.WriteLine(ex.Message);
|
_sdkLog.Debug($"[SDK] Hik SafeOnDecodingCallBack 异常. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
|
||||||
|
AddAuditLog($"[SDK] Hik SafeOnDecodingCallBack 异常. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (smartFrame != null)
|
// 【核心修复】
|
||||||
|
// 只有当分发失败(异常)时,驱动层才负责回收。
|
||||||
|
// 一旦分发成功,所有权属于 GlobalProcessingCenter,驱动层严禁 Dispose。
|
||||||
|
if (!handoverSuccess && smartFrame != null)
|
||||||
|
{
|
||||||
smartFrame.Dispose();
|
smartFrame.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,14 +62,6 @@ public static class Bootstrapper
|
|||||||
|
|
||||||
MaxRetentionDays = 10,
|
MaxRetentionDays = 10,
|
||||||
FileSizeLimitBytes = 1024L * 1024 * 1024,
|
FileSizeLimitBytes = 1024L * 1024 * 1024,
|
||||||
|
|
||||||
// 动态设置日志级别
|
|
||||||
ModuleLevels = new Dictionary<string, Serilog.Events.LogEventLevel>
|
|
||||||
{
|
|
||||||
// 确保 Core 模块在调试时能输出 Debug,在生产时输出 Info
|
|
||||||
{ LogModules.Core, isDebugArgs ? Serilog.Events.LogEventLevel.Debug : Serilog.Events.LogEventLevel.Information },
|
|
||||||
{ LogModules.Network, Serilog.Events.LogEventLevel.Warning }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
LogBootstrapper.Init(ops);
|
LogBootstrapper.Init(ops);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Ayay.SerilogLogs;
|
using Ayay.SerilogLogs;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using SHH.CameraSdk;
|
using SHH.CameraSdk;
|
||||||
|
|
||||||
@@ -53,6 +55,20 @@ public class Program
|
|||||||
// =============================================================
|
// =============================================================
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// 👇👇👇 核心修复开始 👇👇👇
|
||||||
|
|
||||||
|
// ★ 1. 接管日志系统:告诉 Host 使用我们刚才配置好的 Serilog
|
||||||
|
// dispose: true 表示程序结束时自动刷新日志
|
||||||
|
builder.Host.UseSerilog(dispose: true);
|
||||||
|
|
||||||
|
// ★ 2. 斩草除根:清除 .NET 默认注入的 Console/Debug 日志提供程序
|
||||||
|
// 这一步是解决 "info: Microsoft.Hosting.Lifetime..." 重复输出的关键
|
||||||
|
builder.Logging.ClearProviders();
|
||||||
|
|
||||||
|
// ★ 3. (可选) 彻底静音:禁止 Kestrel 打印 "Now listening on..." 这种启动横幅
|
||||||
|
// 如果你只想看你自己的 "[WebApi] 🚀 服务启动...",就把这行加上
|
||||||
|
builder.WebHost.SuppressStatusMessages(true);
|
||||||
|
|
||||||
// ★ 核心改动:一行代码注册所有业务 (SDK, Workers, gRpc, 视频流)
|
// ★ 核心改动:一行代码注册所有业务 (SDK, Workers, gRpc, 视频流)
|
||||||
builder.Services.AddCameraBusinessServices(config, sysLog);
|
builder.Services.AddCameraBusinessServices(config, sysLog);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user