增加日志详细信息:get请求参数,请求 OS Browser 信息
continuous-integration/drone/push Build is passing Details

Test_IRC_Net10
hang 2026-08-05 14:13:42 +08:00
parent 96650ced2d
commit 62421988c4
7 changed files with 155 additions and 30 deletions

View File

@ -4,30 +4,16 @@ using IRaCIS.Core.API.HostService;
using IRaCIS.Core.Application.BusinessFilter; using IRaCIS.Core.Application.BusinessFilter;
using IRaCIS.Core.Application.BusinessFilter.LegacyController.Database.Api; using IRaCIS.Core.Application.BusinessFilter.LegacyController.Database.Api;
using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service; using IRaCIS.Core.Application.Service;
using IRaCIS.Core.Application.Service.BusinessFilter;
using IRaCIS.Core.Infra.EFCore; using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infrastructure.Extention; using IRaCIS.Core.Infrastructure.Extention;
using MassTransit; using MassTransit;
using MassTransit.NewIdProviders; using MassTransit.NewIdProviders;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog; using Serilog;
using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using UAParser;
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true); AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
@ -95,6 +81,8 @@ builder.Services.AddSingleton<FileSyncQueue>();
builder.Services.AddHostedService<SyncFileRecoveryService>(); builder.Services.AddHostedService<SyncFileRecoveryService>();
builder.Services.AddHostedService<FileSyncWorker>(); builder.Services.AddHostedService<FileSyncWorker>();
builder.Services.AddSingleton(_ => Parser.GetDefault());
//minimal api 异常处理 //minimal api 异常处理
builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
//builder.Services.AddProblemDetails(); //builder.Services.AddProblemDetails();

View File

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Serilog; using Serilog;
using System.Linq; using System.Linq;
using UAParser;
namespace IRaCIS.Core.API namespace IRaCIS.Core.API
{ {
@ -17,11 +18,14 @@ namespace IRaCIS.Core.API
{ {
app.UseMiddleware<RequestResponseLoggingMiddleware>(); app.UseMiddleware<RequestResponseLoggingMiddleware>();
// 从 DI 容器中获取 Parser 实例
var uaParser = app.ApplicationServices.GetRequiredService<Parser>();
app.UseSerilogRequestLogging(opts app.UseSerilogRequestLogging(opts
=> =>
{ {
opts.MessageTemplate = "{FullName} {UserType} {UserIp} {Host} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms"; opts.MessageTemplate = "{FullName} {UserType} {UserIp} {Host} {RequestMethod} {RequestPath}{QueryString} {RequestBody} {OS} {Browser} {DeviceType} responded {StatusCode} in {Elapsed:0.0000} ms";
opts.EnrichDiagnosticContext = (diagnosticContext, httpContext) => opts.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{ {
@ -33,10 +37,9 @@ namespace IRaCIS.Core.API
// Only set it if available. You're not sending sensitive data in a querystring right?! // Only set it if available. You're not sending sensitive data in a querystring right?!
if (request.QueryString.HasValue)
{ diagnosticContext.Set("QueryString", request.QueryString.Value ?? "");
diagnosticContext.Set("QueryString", request.QueryString.Value);
}
diagnosticContext.Set("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.FullName)?.Value); diagnosticContext.Set("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.FullName)?.Value);
@ -52,6 +55,38 @@ namespace IRaCIS.Core.API
diagnosticContext.Set("UserIp", clientIp); diagnosticContext.Set("UserIp", clientIp);
// ==================== 新增:解析 User-Agent ====================
try
{
var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
if (!string.IsNullOrEmpty(userAgent))
{
var clientInfo = uaParser.Parse(userAgent);
diagnosticContext.Set("OS", clientInfo.OS.ToString());
diagnosticContext.Set("Browser", clientInfo.UA.ToString());
//diagnosticContext.Set("DeviceType", clientInfo.Device.ToString());
diagnosticContext.Set("DeviceType", userAgent.Contains("Mobile") ? "Mobile": "Desktop");
}
else
{
diagnosticContext.Set("OS", "Unknown");
diagnosticContext.Set("Browser", "Unknown");
diagnosticContext.Set("DeviceType", "Unknown");
}
}
catch (Exception ex)
{
// 解析失败时记录异常,但不要影响主流程
diagnosticContext.Set("OS", "ParseError");
diagnosticContext.Set("Browser", "ParseError");
diagnosticContext.Set("DeviceType", "ParseError");
}
// ==============================================================
#region 非必要不记录 #region 非必要不记录
//diagnosticContext.Set("Protocol", request.Protocol); //diagnosticContext.Set("Protocol", request.Protocol);
//diagnosticContext.Set("Scheme", request.Scheme); //diagnosticContext.Set("Scheme", request.Scheme);

View File

@ -9032,6 +9032,36 @@
斑块破裂 斑块破裂
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.TargetSegment">
<summary>
靶段
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.TargetSegmentRemarks">
<summary>
靶段备注
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.ROIAllLength">
<summary>
ROI段落总长度
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.PullbackFrameCount">
<summary>
回撤中的图像帧数
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.PlaqueType">
<summary>
斑块类型
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.AdjustReason">
<summary>
访视点备注(如有)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.OCTFCTUploadData.FrameNumber"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.OCTFCTUploadData.FrameNumber">
<summary> <summary>
帧数 帧数
@ -10232,6 +10262,11 @@
问题分类 问题分类
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetFilterTableQuestionInDto.QuestionClassify">
<summary>
问题分类
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.SetOncologyQuestion.EvaluationResult"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.SetOncologyQuestion.EvaluationResult">
<summary> <summary>
评估结果 评估结果
@ -15015,6 +15050,13 @@
<param name="inDto"></param> <param name="inDto"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Core.Application.Service.ReadingImageTaskService.GetFilterTableQuestion(IRaCIS.Core.Application.Service.Reading.Dto.GetFilterTableQuestionInDto)">
<summary>
获取单个表格问题及答案 过滤问题
</summary>
<param name="inDto"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.ReadingImageTaskService.GetCustomTableQuestionAnswer(IRaCIS.Core.Application.Service.Reading.Dto.GetCustomTableQuestionAnswerInDto)"> <member name="M:IRaCIS.Core.Application.Service.ReadingImageTaskService.GetCustomTableQuestionAnswer(IRaCIS.Core.Application.Service.Reading.Dto.GetCustomTableQuestionAnswerInDto)">
<summary> <summary>
获取自定义问题以及答案 获取自定义问题以及答案

View File

@ -1728,10 +1728,10 @@ namespace IRaCIS.Core.Application.Service
//var d = _dicRepository.UpdateFromDTOAsync(new AddOrEditBasicDic() { Id = Guid.Parse("60d86683-c33b-4349-b672-08da1e91b622"), ParentId = null, ChildGroup = null, Code = null }, true, true).Result; //var d = _dicRepository.UpdateFromDTOAsync(new AddOrEditBasicDic() { Id = Guid.Parse("60d86683-c33b-4349-b672-08da1e91b622"), ParentId = null, ChildGroup = null, Code = null }, true, true).Result;
//var a = 123; //var a = 123;
throw new BusinessValidationFailedException(_localizer["TrialIsDoubleCheck{0}", "测试"]); //throw new BusinessValidationFailedException(_localizer["TrialIsDoubleCheck{0}", "测试"]);
var b = _localizer["test{0}", "测试"]; //var b = _localizer["test{0}", "测试"];
return _localizer["test{0}", "测试"]; //return _localizer["test{0}", "测试"];
//var list = _subjectVisitRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.VisitTaskList).ToList(); //var list = _subjectVisitRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.VisitTaskList).ToList();
@ -1741,6 +1741,8 @@ namespace IRaCIS.Core.Application.Service
//var list3 = _visitTaskRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SourceSubjectVisit.VisitTaskList).ToList(); //var list3 = _visitTaskRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SourceSubjectVisit.VisitTaskList).ToList();
//var list2 = _subjectRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SubjectVisitTaskList).ToList(); //var list2 = _subjectRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SubjectVisitTaskList).ToList();
//return _userInfo.LocalIp; //return _userInfo.LocalIp;
return _userInfo.ClientInfo;
} }

View File

@ -63,5 +63,17 @@ namespace IRaCIS.Core.Domain.Share
string BrowserFingerprint { get; } string BrowserFingerprint { get; }
UserWorkLanguage UserWorkLanguage { get; } UserWorkLanguage UserWorkLanguage { get; }
ClientInfo ClientInfo { get; }
} }
public class ClientInfo
{
public string Browser { get; set; }
public string OS { get; set; }
public string DeviceType { get; set; }
}
} }

View File

@ -1,19 +1,15 @@
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using UAParser;
namespace IRaCIS.Core.Domain.Share namespace IRaCIS.Core.Domain.Share
{ {
/// <summary> /// <summary>
/// 用户信息 /// 用户信息
/// </summary> /// </summary>
public class UserInfo : IUserInfo public class UserInfo(IHttpContextAccessor _accessor, Parser parser) : IUserInfo
{ {
private readonly IHttpContextAccessor _accessor;
public UserInfo(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
/// <summary> /// <summary>
/// 用户角色Id /// 用户角色Id
@ -348,6 +344,55 @@ namespace IRaCIS.Core.Domain.Share
} }
public ClientInfo ClientInfo
{
get
{
try
{
var userAgent = _accessor?.HttpContext.Request.Headers["User-Agent"].ToString();
if (!string.IsNullOrEmpty(userAgent))
{
var clientInfo = parser.Parse(userAgent);
return new ClientInfo()
{
OS = clientInfo.OS.ToString(),
Browser = clientInfo.UA.ToString(),
DeviceType = userAgent.Contains("Mobile") ? "Mobile" : "Desktop"
};
}
else
{
return new ClientInfo()
{
OS = "Unknown",
Browser = "Unknown",
DeviceType = "Unknown"
};
}
}
catch (Exception ex)
{
return new ClientInfo()
{
OS = "ParseError",
Browser = "ParseError",
DeviceType = "ParseError"
};
}
}
}
public Guid? SignId public Guid? SignId
{ {
@ -417,7 +462,7 @@ namespace IRaCIS.Core.Domain.Share
public const string PermissionStr = "permissionStr"; public const string PermissionStr = "permissionStr";
public const string UserWorkLanguage = "userWorkLanguage"; public const string UserWorkLanguage = "userWorkLanguage";
} }

View File

@ -15,6 +15,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="EFCore.BulkExtensions" Version="10.0.1" /> <PackageReference Include="EFCore.BulkExtensions" Version="10.0.1" />
<PackageReference Include="EntityFrameworkCore.Triggered" Version="3.2.2" /> <PackageReference Include="EntityFrameworkCore.Triggered" Version="3.2.2" />
<PackageReference Include="EntityFrameworkCore.Projectables" Version="6.0.5" /> <PackageReference Include="EntityFrameworkCore.Projectables" Version="6.0.5" />