增加日志组件

This commit is contained in:
2026-01-15 18:56:39 +08:00
parent 801ffb25fd
commit 2754cdff15
13 changed files with 1153 additions and 142 deletions

View File

@@ -1,121 +1,63 @@
using Grpc.Net.Client;
using Ayay.SerilogLogs;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Serilog;
using SHH.CameraSdk;
using SHH.Contracts.Grpc;
using Microsoft.Extensions.Logging;
namespace SHH.CameraService;
public class Program
{
/// <summary>
/// 主程序
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static async Task Main(string[] args)
{
// 2. 硬件预热 (静态方法保留)
HikNativeMethods.NET_DVR_Init();
HikSdkManager.ForceWarmUp();
// 1. [核心环境] 必须在所有网络操作前开启
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
// 2. 模拟/解析配置
if (args.Length == 0)
{
string serviceArgs = "--appid CameraApp_01 " +
"--uris localhost,9001,video,调试PC; " +
"--uris localhost,9001,command,调试PC; " +
"--mode 1 --ports 5000,100";
args = serviceArgs.Split(' ', StringSplitOptions.RemoveEmptyEntries);
}
var config = ServiceConfig.BuildFromArgs(args);
// 2. 解析配置与初始化日志
var (config, opts, isDebugArgs) = Bootstrapper.ParseConfigAndInitLogger(args);
var sysLog = Log.ForContext("SourceContext", LogModules.Core);
// =============================================================
// 3. 【强行复刻成功逻辑】 在 Web 容器启动前直接执行注册
// 1. 启动日志
// =============================================================
if (config.CommandEndpoints.Any())
{
try
{
// 将 tcp:// 转换为 http:// 以适配 gRPC
string targetUrl = config.CommandEndpoints.First().Uri.Replace("tcp://", "http://");
using var channel = GrpcChannel.ForAddress(targetUrl);
var client = new GatewayProvider.GatewayProviderClient(channel);
Console.WriteLine($"[gRPC] 正在执行预注册 (环境: 纯净): {targetUrl}");
var resp = await client.RegisterInstanceAsync(new RegisterRequest
{
InstanceId = config.AppId,
Version = "2.0.0-grpc",
ServerIp = "127.0.0.1",
WebapiPort = config.BasePort,
StartTimeTicks = DateTime.Now.Ticks,
ProcessId = Environment.ProcessId,
Description = "Camera Service"
});
Console.WriteLine($"[gRPC] 预注册成功: {resp.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"[gRPC] 预注册尝试失败 (不影响启动): {ex.Message}");
}
}
sysLog.Warning($"🚀 视频取流进程启动, 日志组件初始化完毕 => 进程: {opts.AppId}");
string argString = string.Join(" ", args);
sysLog.Debug($"🚀 启动参数({(isDebugArgs ? "" : "")}: {argString}");
// =============================================================
// 4. 构建 Web 主机环境
// 2. 硬件预热、端口扫描、gRPC链接
// =============================================================
Bootstrapper.WarmUpHardware(sysLog);
// 端口自动扫描 (必须做,否则端口冲突)
int activePort = Bootstrapper.ScanForAvailablePort(config, sysLog);
if (activePort == -1)
{
sysLog.Fatal("💀 无法启动:配置范围内无可用端口");
Bootstrapper.Shutdown("无法启动:配置范围内无可用端口", exitCode: 1);
return;
}
config.UpdateActualPort(activePort); // 回填端口
// 具体的 gRPC 链接逻辑封装在 Bootstrapper 中,保持 Main 清爽但逻辑可见
await Bootstrapper.RegisterToGatewayAsync(config, sysLog);
// =============================================================
// 3. 构建 Web 主机环境
// =============================================================
var builder = WebApplication.CreateBuilder(args);
// 基础业务单例注册
builder.Services.AddSingleton(config);
builder.Services.AddSingleton<ProcessingConfigManager>();
builder.Services.AddSingleton(sp => new ImageScaleCluster(4, sp.GetRequiredService<ProcessingConfigManager>()));
builder.Services.AddSingleton(sp => new ImageEnhanceCluster(4, sp.GetRequiredService<ProcessingConfigManager>()));
builder.Services.AddHostedService<PipelineConfigurator>();
// ★ 核心改动:一行代码注册所有业务 (SDK, Workers, gRPC, 视频流)
builder.Services.AddCameraBusinessServices(config, sysLog);
// 接入 SDK 核心逻辑
builder.Services.AddCameraSdk(config.NumericId);
builder.Services.AddHostedService<CameraEngineWorker>();
// ★ 注册 gRPC 版本的状态监控工作者 (不讲道理,直接注册)
builder.Services.AddHostedService<DeviceStatusHandler>();
builder.Services.AddHostedService<ParentProcessSentinel>();
builder.Services.AddHostedService<GatewayService>();
// =============================================================
// 5. 视频流 Target 注册 (gRPC 模式)
// =============================================================
var netTargets = new List<StreamTarget>();
if (config.VideoEndpoints != null)
{
foreach (var cfgVideo in config.VideoEndpoints)
{
netTargets.Add(new StreamTarget(new PushTargetConfig
{
Name = cfgVideo.Description,
Endpoint = cfgVideo.Uri,
QueueCapacity = 10,
}));
}
}
builder.Services.AddSingleton<IEnumerable<StreamTarget>>(netTargets);
builder.Services.AddHostedService<ImageMonitorController>();
// 为每个 Target 绑定一个 gRPC 流发送者
foreach (var target in netTargets)
{
builder.Services.AddHostedService(sp =>
new GrpcSenderWorker(target, sp.GetRequiredService<ILogger<GrpcSenderWorker>>()));
}
// 注册指令分发 (不再使用 NetMQ 的 CommandClientWorker)
builder.Services.AddSingleton<InterceptorPipeline>();
builder.Services.AddSingleton<CommandDispatcher>();
builder.Services.AddSingleton<ICommandHandler, DeviceConfigHandler>();
builder.Services.AddSingleton<ICommandHandler, RemoveCameraHandler>();
ConfigureWebServices(builder, config);
// ★ 核心改动:注册 Web 基础 (Controller, Swagger, Cors)
builder.Services.AddWebSupport(config);
// =============================================================
// 6. 启动服务
@@ -123,51 +65,66 @@ public class Program
var app = builder.Build();
// 激活 SDK 管理器并启动业务点火
await StartBusinessLogic(app);
await StartBusinessLogic(app, sysLog);
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", $"SHH Gateway #{config.AppId}"));
// ★ 核心改动:配置 HTTP 管道 (Swagger, MapControllers 等)
app.ConfigurePipeline(config);
app.MapGet("/", () => $"SHH Gateway {config.AppId} is running (gRPC Mode).");
app.UseCors("AllowAll");
app.MapControllers();
// 启动监听
string url = $"http://0.0.0.0:{config.BasePort}";
sysLog.Information($"🚀 [WebApi] 服务启动,监听: {url}");
Console.WriteLine($"[System] 正在启动 Web 服务,监听端口: {config.BasePort}");
await app.RunAsync($"http://0.0.0.0:{config.BasePort}");
await app.RunAsync(url);
}
/// <summary>
/// 激活单例并启动相机管理器
/// </summary>
static async Task StartBusinessLogic(WebApplication app)
/// <param name="app"></param>
/// <param name="logger"></param>
static async Task StartBusinessLogic(WebApplication app, Serilog.ILogger logger)
{
var manager = app.Services.GetRequiredService<CameraManager>();
// 激活哨兵
_ = app.Services.GetRequiredService<ConnectivitySentinel>();
await manager.StartAsync();
Console.WriteLine("[System] 核心业务逻辑已激活。");
Console.WriteLine("[System] 核心业务逻辑已激活。");
}
}
/// <summary>
/// 注册 Web API 支持
/// </summary>
static void ConfigureWebServices(WebApplicationBuilder builder, ServiceConfig cfg)
{
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
builder.Services.AddControllers(options =>
{
options.Filters.Add<UserActionFilter>();
})
.AddApplicationPart(typeof(CamerasController).Assembly)
.AddApplicationPart(typeof(MonitorController).Assembly);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"SHH Gateway #{cfg.AppId}", Version = "v1" });
});
}
}
/*
🚀 启动/发布 程序启动、服务预热、开始监听
🏁 结束/终点 批量任务全部完成、程序正常退出
🔄 重试/循环 正在重试连接、定时任务触发、同步数据中
⏳ 等待/耗时 长耗时操作开始、排队中
💤 休眠/闲置 线程挂起、服务进入待机模式
🌐 网络/HTTP HTTP 请求、API 调用、Web 服务
🔌 连接 数据库连接建立、Socket 连接
📡 信号/广播 发送 MQ 消息、广播通知、取流
💾 存储/磁盘 写入文件、数据库落盘、缓存读写
🔒 安全/锁 加密、解密、登录成功、获取分布式锁
⚙️ 配置/系统 加载配置、系统底层操作
🐞 Bug/调试 捕捉到的异常、临时调试信息
🧪 测试/实验 单元测试环境、灰度测试代码
🔍 搜索/检查 查询数据库、检查文件是否存在
💡 提示/发现 逻辑分支提示、参数值打印
🔴 红:致命/严重 (Fatal/Error)
🟡 黄:警告 (Warning)
🟢 绿:正常/成功 (Info/Success)
🔵 蓝:数据/网络 (Data/Network)
⚪ 灰:细节/忽略 (Debug/Verbose)
✅ Check Mark Button \u{2705} &#x2705;
🆗 OK Button \u{1F197} &#x1F197;
🔚 END Arrow (逻辑结束) \u{1F51A} &#x1F51A;
💯 Hundred Points (完美结束) \u{1F4AF} &#x1F4AF;
🛑 Stop Sign (最强提示) \u{1F6D1} &#x1F6D1;
⛔ No Entry (禁止/中断) \u{26D4} &#x26D4;
🚫 Prohibited (非法操作终止) \u{1F6AB} &#x1F6AB;
⏹️ Stop Button (播放器风格) \u{23F9} &#x23F9;
❌ Cross Mark (任务失败结束) \u{274C} &#x274C;
💀 Skull (进程被 Kill) \u{1F480} &#x1F480;
⚰️ Coffin (彻底销毁) \u{26B0} &#x26B0;
👻 Ghost (变成孤儿进程) \u{1F47B} &#x1F47B;
*/