HJL WebAPI 项目日志入库实战:从建表到自动清理

HJL WebAPI 项目日志入库实战:从建表到自动清理
HJL WebAPI 项目日志入库实战从建表到自动清理一、背景HJL 项目是一个 .NET 8 WebAPI 程序原来使用 NLog 写文本日志。随着业务量增长服务器磁盘 IO 和日志文件管理成为痛点。本文记录将日志全部写入 Oracle 数据库、并配置自动清理策略的完整过程。二、技术栈组件版本/说明框架.NET 8 WebAPI日志框架NLog NLog.Web.AspNetCore数据库OracleORMSQLSugar业务库驱动Oracle.ManagedDataAccess.Core三、第一步数据库建表在 Oracle 中创建专用日志表HJL_API_APPLOG表名含义HJL 项目 - WebAPI - 应用日志。3.1 建表 SQL-- -- HJL WebAPI 应用日志表-- 说明替代服务器文本日志所有日志只写入数据库-- 保留策略180 天由 Oracle Job 自动清理-- CREATETABLEMESFLXRPT.HJL_API_APPLOG(ID VARCHAR2(32)PRIMARYKEY,-- 主键程序生成 GUIDLOG_LEVEL VARCHAR2(20)NOTNULL,-- 日志级别Info/Warning/Error/CriticalCATEGORY VARCHAR2(255),-- 日志来源Logger 名称如 Controller 全名MESSAGE CLOB,-- 日志正文EXCEPTION CLOB,-- 异常堆栈Error 级别时写入CLASS_NAME VARCHAR2(500),-- 产生日志的完整类名METHOD_NAME VARCHAR2(255),-- 产生日志的方法名Action 名称THREAD_ID VARCHAR2(50),-- 线程 ID排查并发问题TRACE_ID VARCHAR2(100),-- HTTP 请求追踪 ID同一次请求的所有日志共享IP_ADDRESS VARCHAR2(50),-- 客户端请求 IPCREATE_TIMEDATEDEFAULTSYSDATENOTNULL-- 日志产生时间);-- 表注释COMMENTONTABLEMESFLXRPT.HJL_API_APPLOGISHJL项目WebAPI应用日志表替代服务器文本日志保留180天后自动清理;-- 字段注释COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.IDIS主键程序生成的32位大写GUID;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.LOG_LEVELIS日志级别Trace/Debug/Info/Warning/Error/Critical。生产环境建议只存Info及以上;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.CATEGORYIS日志来源Logger名称通常是类的全限定名;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.MESSAGEIS日志正文内容CLOB大字段支持长文本;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.EXCEPTIONIS异常堆栈详情仅Error/Critical级别时可能有值;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.CLASS_NAMEIS产生日志的完整类名含命名空间;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.METHOD_NAMEIS产生日志的方法名WebAPI项目使用aspnet-mvc-action获取Action名称;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.THREAD_IDIS当前托管线程ID用于分析并发问题;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.TRACE_IDISHTTP请求追踪标识HttpContext.TraceIdentifier实现全链路追踪;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.IP_ADDRESSIS客户端请求IP地址用于安全审计;COMMENTONCOLUMNMESFLXRPT.HJL_API_APPLOG.CREATE_TIMEIS日志产生时间默认SYSDATE作为数据清理和查询的时间依据;-- 索引按时间查询和清理最常用CREATEINDEXIDX_HJL_LOG_TIMEONMESFLXRPT.HJL_API_APPLOG(CREATE_TIME);-- 索引按日志级别筛选查Error时提速CREATEINDEXIDX_HJL_LOG_LEVELONMESFLXRPT.HJL_API_APPLOG(LOG_LEVEL);-- 索引按业务模块筛选查特定Controller日志时提速CREATEINDEXIDX_HJL_LOG_CATEGORYONMESFLXRPT.HJL_API_APPLOG(CATEGORY);3.2 设计说明为什么用 CLOB异常堆栈可能很长VARCHAR2 存不下。为什么必须建时间索引日志查询和定时清理都按CREATE_TIME过滤没有索引全表扫描性能极差。为什么保留 180 天生产环境半年前的日志参考价值低定期清理防止表膨胀。四、第二步安装 NLog 相关 NuGet 包在 WebAPI 项目上安装以下包# NLog 核心包dotnetaddpackage NLog# ASP.NET Core 扩展提供 TraceId、IP 等变量dotnetaddpackage NLog.Web.AspNetCore# 数据库支持DatabaseTargetdotnetaddpackage NLog.Database# Oracle 驱动.NET 8 用 Core 版本dotnetaddpackage Oracle.ManagedDataAccess.Core五、第三步配置 appsettings.json在appsettings.json中配置 Oracle 连接串不要写死在 NLog 配置文件里。{Logging:{LogLevel:{Default:Information,Microsoft.AspNetCore:Warning}},AllowedHosts:*,ConnectionStrings:{strConnFlexMesInterface:Data Source你的TNS;User Id用户名;Password密码;}}安全提示生产环境使用appsettings.Production.json并加入.gitignore避免密码提交到版本库。六、第四步配置 nlog.config在项目根目录创建nlog.config属性设置为复制到输出目录 → 始终复制。?xml version1.0 encodingutf-8 ?nlogxmlnshttp://www.nlog-project.org/schemas/NLog.xsdxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.nlog-project.org/schemas/NLog.xsd NLog.xsdautoReloadtruethrowExceptionsfalseinternalLogLevelWarninternalLogFile${basedir}/NLog/internal.log!-- 启用 ASP.NET Core 扩展提供 TraceId 和 IP 变量 --extensionsaddassemblyNLog.Web.AspNetCore//extensionstargets!-- 数据库日志目标三层包装 第1层AsyncWrapper异步缓冲 - 日志先存入内存队列攒够 50 条或每 1 秒批量写入数据库 - 避免每条日志都等待数据库 IO提升 API 响应速度 第2层RetryingWrapper失败重试 - 数据库断线或锁表时自动重试 3 次 第3层DatabaseTarget数据库写入 - 执行 Oracle INSERT 语句 --targetxsi:typeAsyncWrappernamedb_asyncbatchSize50timeToSleepBetweenBatches1000queueLimit10000overflowActionGrowtargetxsi:typeRetryingWrapperretryCount3retryDelayMilliseconds500targetxsi:typeDatabasenamedb_log!--【重要】程序集名必须是Oracle.ManagedDataAccessNuGet包名是Oracle.ManagedDataAccess.Core但程序集名没有.Core后缀这是最常见的坑写错会导致Couldnotloadfileorassembly错误。--dbProviderOracle.ManagedDataAccess.Client.OracleConnection, Oracle.ManagedDataAccess connectionString${configsetting:itemConnectionStrings.strConnFlexMesInterface} commandTextINSERT INTO MESFLXRPT.HJL_API_APPLOG ( ID, LOG_LEVEL, CATEGORY, MESSAGE, EXCEPTION, CLASS_NAME, METHOD_NAME, THREAD_ID, TRACE_ID, IP_ADDRESS, CREATE_TIME ) VALUES ( :Id, :LogLevel, :Category, :Message, :Exception, :ClassName, :MethodName, :ThreadId, :TraceId, :IpAddress, TO_DATE(:CreateTime, YYYY-MM-DD HH24:MI:SS) )!-- 32位大写GUID --parameternameIdlayout${guid:formatN:uppercasetrue}/!-- 日志级别大写INFO/ERROR/WARNING --parameternameLogLevellayout${level:uppercasetrue}/!-- 日志来源Logger名称 --parameternameCategorylayout${logger}/!-- 日志正文 --parameternameMessagelayout${message}/!-- 异常堆栈。Oracle CLOB 足够大不需要截断 --parameternameExceptionlayout${exception:formattoString,Data}/!-- 完整类名 --parameternameClassNamelayout${logger}/!-- 【重要】WebAPI 项目获取 Action 方法名 使用 ${aspnet-mvc-action} 直接从 ASP.NET Core 路由上下文读取 比 ${callsite} 更适合 async Controller避免抓到编译器生成的状态机方法名。 --parameternameMethodNamelayout${aspnet-mvc-action}/!-- 线程ID --parameternameThreadIdlayout${threadid}/!-- HTTP 请求追踪 ID。 whenEmpty 处理非 HTTP 场景如后台线程从 MDLC 上下文读取。 --parameternameTraceIdlayout${aspnet-TraceIdentifier:whenEmpty${mdlc:itemTraceId}}/!-- 客户端IP --parameternameIpAddresslayout${aspnet-request-ip:whenEmpty${mdlc:itemClientIp}}/!-- 日志时间 --parameternameCreateTimelayout${date:formatyyyy-MM-dd HHmmss}//target/target/target/targetsrules!-- 过滤 Microsoft 框架的噪音日志如 Kestrel 启动信息 --loggernameMicrosoft.*minlevelTracefinaltrue/!-- 业务日志 Info 及以上写入数据库 --loggername*minlevelInfowriteTodb_async//rules/nlog6.1 关键踩坑点坑点错误写法正确写法Oracle 程序集名Oracle.ManagedDataAccess.CoreOracle.ManagedDataAccess没有 .Coreasync 方法名获取${callsite:methodNametrue}${aspnet-mvc-action}WebAPI 专用时间格式冒号HH:mm:ssHHmmss避免 NLog 解析冲突七、第五步配置 Program.csusingNLog.Web;varbuilderWebApplication.CreateBuilder(args);// 注册 HttpContext 访问器NLog 获取 TraceId 和 IP 必须builder.Services.AddHttpContextAccessor();// 清除默认日志提供器启用 NLogbuilder.Logging.ClearProviders();builder.Host.UseNLog();varappbuilder.Build();app.Run();八、第六步Oracle 定时清理 Job8.1 创建 Job-- -- 创建 HJL 应用日志定时清理 Job-- 每天凌晨 2 点删除 180 天前的日志-- -- 安全删除旧 Job如果存在BEGINDBMS_SCHEDULER.drop_job(JOB_CLEAN_HJL_APPLOG);EXCEPTIONWHENOTHERSTHENIFSQLCODE!-27475THENRAISE;ENDIF;END;/-- 创建新 JobBEGINDBMS_SCHEDULER.create_job(job_nameJOB_CLEAN_HJL_APPLOG,job_typePLSQL_BLOCK,-- 清理逻辑删除 180 天前的数据job_actionBEGIN DELETE FROM MESFLXRPT.HJL_API_APPLOG WHERE CREATE_TIME SYSDATE - 180; COMMIT; END;,-- 首次执行今天凌晨 2 点start_dateTRUNC(SYSDATE)INTERVAL2HOUR,-- 重复规则每天凌晨 2 点repeat_intervalFREQDAILY; BYHOUR2; BYMINUTE0; BYSECOND0,enabledTRUE,commentsHJL WebAPI 日志自动清理每天凌晨2点删除180天前的应用日志);END;/8.2 验证 Job 状态-- 查看 Job 是否创建成功SELECTjob_name,state,next_run_date,commentsFROMuser_scheduler_jobsWHEREjob_nameJOB_CLEAN_HJL_APPLOG;预期结果stateSCHEDULEDnext_run_date 明天凌晨 2 点九、第七步测试清理 Job9.1 插入测试数据-- 今天的数据应该保留INSERTINTOMESFLXRPT.HJL_API_APPLOG(ID,LOG_LEVEL,CATEGORY,MESSAGE,CLASS_NAME,METHOD_NAME,THREAD_ID,TRACE_ID,IP_ADDRESS,CREATE_TIME)VALUES(TEST_KEEP,INFO,Test,今天的数据应该保留,Test,Test,1,TRACE001,127.0.0.1,SYSDATE);-- 5 天前的数据应该被删除INSERTINTOMESFLXRPT.HJL_API_APPLOG(ID,LOG_LEVEL,CATEGORY,MESSAGE,CLASS_NAME,METHOD_NAME,THREAD_ID,TRACE_ID,IP_ADDRESS,CREATE_TIME)VALUES(TEST_DEL1,INFO,Test,5天前的数据应该被删除,Test,Test,1,TRACE002,127.0.0.1,SYSDATE-5);INSERTINTOMESFLXRPT.HJL_API_APPLOG(ID,LOG_LEVEL,CATEGORY,MESSAGE,CLASS_NAME,METHOD_NAME,THREAD_ID,TRACE_ID,IP_ADDRESS,CREATE_TIME)VALUES(TEST_DEL2,INFO,Test,也是5天前的数据应该被删除,Test,Test,1,TRACE003,127.0.0.1,SYSDATE-5);COMMIT;9.2 查看测试数据SELECTID,MESSAGE,CREATE_TIME,CASEWHENCREATE_TIMESYSDATE-1THEN会被删除ELSE保留ENDAS预期FROMMESFLXRPT.HJL_API_APPLOGWHEREIDLIKETEST%ORDERBYCREATE_TIME;9.3 手动执行清理 SQL模拟 Job 逻辑-- 模拟 Job 的清理逻辑把时间改成 1 天方便验证DELETEFROMMESFLXRPT.HJL_API_APPLOGWHERECREATE_TIMESYSDATE-1;COMMIT;9.4 验证结果SELECTID,MESSAGE,CREATE_TIMEFROMMESFLXRPT.HJL_API_APPLOGWHEREIDLIKETEST%ORDERBYCREATE_TIME;预期结果TEST_KEEP今天保留✅TEST_DEL1、TEST_DEL25天前已删除✅9.5 清理测试数据DELETEFROMMESFLXRPT.HJL_API_APPLOGWHEREIDLIKETEST%;COMMIT;十、第八步验证日志写入启动 WebAPI 项目调用任意接口如登录接口然后查询数据库SELECTID,LOG_LEVEL,MESSAGE,METHOD_NAME,TRACE_ID,IP_ADDRESS,CREATE_TIMEFROMMESFLXRPT.HJL_API_APPLOGORDERBYCREATE_TIMEDESCFETCHFIRST5ROWSONLY;预期结果METHOD_NAME显示AdminLogin或你的 Action 名称TRACE_ID有值如0HNM4V...IP_ADDRESS有值本机调试显示::1十一、总结步骤操作关键注意点1数据库建表表名HJL_API_APPLOG字段用 CLOB 存长文本2安装 NuGet 包4 个包NLog、NLog.Web.AspNetCore、NLog.Database、Oracle 驱动3配置 appsettings.json连接串集中管理不暴露密码4配置 nlog.config程序集名没有 .Core、用aspnet-mvc-action取方法名5配置 Program.cs必须加AddHttpContextAccessor()6创建 Oracle Job每天凌晨 2 点清理 180 天前数据7测试 Job造测试数据 → 执行 DELETE → 验证 → 清理8验证日志写入调接口后查数据库确认字段完整十二、后续维护查看 Job 执行历史SELECTjob_name,status,actual_start_date,run_durationFROMuser_scheduler_job_run_detailsWHEREjob_nameJOB_CLEAN_HJL_APPLOGORDERBYactual_start_dateDESC;手动触发 Job排查问题时用BEGINDBMS_SCHEDULER.run_job(JOB_CLEAN_HJL_APPLOG);END;删除 Job如需停用BEGINDBMS_SCHEDULER.drop_job(JOB_CLEAN_HJL_APPLOG);END;以上即为 HJL WebAPI 项目日志入库的完整操作流程。