- SwapCalcTrace 改为双模式:保留全局静态开关(单元测试用 IsEnabled/Dump),
新增请求级 AsyncLocal 缓冲(EnableForRequest/DumpForRequest/ClearForRequest),
由中间件按请求开关,不跨请求串扰。
- 新增 SwapCalcTraceMiddleware:开启方式三选一——URL ?swaptrace=1 /
请求头 X-SwapCalc-Trace:1 / 标记文件 App_Data/swapcalctrace.on(无需重启);
Response.OnStarting 时把逐日计息过程写入响应头 X-SwapCalc-Trace(超长落
logs/swapcalctrace-YYYYMMDD.log 并在头给路径),同时写服务器日志。
开发者在浏览器“网络”面板即可查看,零前端改码、零行为影响、性能可忽略。
- Program.cs 在 ExceptionMiddleware 后注册该中间件。
- 删除 CalcDailyCompoundInterestByEod 遗留垃圾日志 LogFactory.GetLogger("test").Error("lksafhasdhfjas")。
- CalcDailySimpleInterest 两处追踪守卫改为 IsEnabled||IsRequestEnabled。
- 既有 PrepaidPrincipalCloseTraceTest / ClosingChainTraceTest 2/2 仍全绿。
回答用户两点:① dynomicPrincipal 公式字段含义;② 原 Trace 需重启才能开,
现改为前端 otcdebug 式(?swaptrace=1) 即可在响应头看到逐步计息过程。
104 lines
3.7 KiB
C#
104 lines
3.7 KiB
C#
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
using System;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using YLErp.Helpers;
|
|
using YLErp.Modules.SwapModule;
|
|
|
|
namespace YLErp.Web.Middleware
|
|
{
|
|
/// <summary>
|
|
/// 计息计算过程追踪中间件(运行时排障用,无需重启、无需改前端)。
|
|
///
|
|
/// 开启方式(三选一):
|
|
/// 1) 请求 URL 追加 ?swaptrace=1
|
|
/// 2) 请求头带 X-SwapCalc-Trace: 1
|
|
/// 3) 站点根目录 App_Data/swapcalctrace.on 标记文件存在(对所有请求开启,谨慎使用)
|
|
///
|
|
/// 开启后,SwapCalcTrace 会在计息函数中逐日记录过程。本中间件在响应发出前把追踪文本写入:
|
|
/// - 响应头 X-SwapCalc-Trace(开发者在浏览器"网络"面板即可看到,超长则写入日志并在头部给文件路径)
|
|
/// - 服务器日志 logs/swapcalctrace-YYYYMMDD.log(完整文本,便于 tail/检索)
|
|
///
|
|
/// 性能影响可忽略(仅被显式开启的少数请求触发,且每次只是拼字符串)。
|
|
/// </summary>
|
|
public class SwapCalcTraceMiddleware
|
|
{
|
|
private readonly RequestDelegate _next;
|
|
private readonly IWebHostEnvironment _env;
|
|
|
|
public SwapCalcTraceMiddleware(RequestDelegate next, IWebHostEnvironment env)
|
|
{
|
|
_next = next;
|
|
_env = env;
|
|
}
|
|
|
|
public async Task InvokeAsync(HttpContext context)
|
|
{
|
|
bool on = context.Request.Query["swaptrace"] == "1"
|
|
|| context.Request.Headers["X-SwapCalc-Trace"] == "1";
|
|
if (!on) on = MarkerFileExists();
|
|
if (on) SwapCalcTrace.EnableForRequest(true);
|
|
|
|
try
|
|
{
|
|
if (on)
|
|
{
|
|
// OnStarting 在响应体写出前触发,确保整个请求(含计息)都已被追踪
|
|
context.Response.OnStarting(() =>
|
|
{
|
|
FlushTrace(context);
|
|
return Task.CompletedTask;
|
|
});
|
|
}
|
|
await _next(context);
|
|
}
|
|
finally
|
|
{
|
|
if (on) SwapCalcTrace.ClearForRequest();
|
|
}
|
|
}
|
|
|
|
private void FlushTrace(HttpContext context)
|
|
{
|
|
var trace = SwapCalcTrace.DumpForRequest();
|
|
if (string.IsNullOrEmpty(trace)) return;
|
|
|
|
var logger = LogFactory.GetLogger("SwapCalcTrace");
|
|
logger.Info($"[SwapCalcTrace] {context.Request.Path}\n{trace}");
|
|
|
|
var headerVal = trace;
|
|
if (trace.Length > 4000)
|
|
{
|
|
try
|
|
{
|
|
var dir = Path.Combine(_env.ContentRootPath, "logs");
|
|
Directory.CreateDirectory(dir);
|
|
var file = Path.Combine(dir, $"swapcalctrace-{DateTime.Now:yyyyMMdd}.log");
|
|
File.AppendAllText(file,
|
|
$"=== {DateTime.Now:yyyy-MM-dd HH:mm:ss} {context.Request.Path} ===\n{trace}\n\n");
|
|
headerVal = $"(trace truncated, full in {file})\n" + trace.Substring(0, 4000);
|
|
}
|
|
catch
|
|
{
|
|
headerVal = trace.Substring(0, 4000);
|
|
}
|
|
}
|
|
|
|
try { context.Response.Headers["X-SwapCalc-Trace"] = headerVal; } catch { }
|
|
}
|
|
|
|
private bool MarkerFileExists()
|
|
{
|
|
try
|
|
{
|
|
return File.Exists(Path.Combine(_env.ContentRootPath, "App_Data", "swapcalctrace.on"));
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|