diff --git a/IRC.Core.SCP/Service/CStoreSCPService.cs b/IRC.Core.SCP/Service/CStoreSCPService.cs index fe1f91e00..1f2dae205 100644 --- a/IRC.Core.SCP/Service/CStoreSCPService.cs +++ b/IRC.Core.SCP/Service/CStoreSCPService.cs @@ -315,7 +315,7 @@ namespace IRaCIS.Core.SCP.Service _SCPStudyIdList.Add(scpStudyId); } - var series = await _seriesRepository.FindAsync(seriesId); + var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId); //没有缩略图 if (series != null && string.IsNullOrEmpty(series.ImageResizePath)) diff --git a/IRC.Core.SCP/Service/DicomArchiveService.cs b/IRC.Core.SCP/Service/DicomArchiveService.cs index ac0edd173..e522f6690 100644 --- a/IRC.Core.SCP/Service/DicomArchiveService.cs +++ b/IRC.Core.SCP/Service/DicomArchiveService.cs @@ -75,9 +75,9 @@ namespace IRaCIS.Core.SCP.Service //using (@lock.Acquire()) { var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId ); - var findStudy = await _studyRepository.FindAsync(studyId); - var findSerice = await _seriesRepository.FindAsync(seriesId); - var findInstance = await _instanceRepository.FindAsync(instanceId); + var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId); + var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId); + var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId); DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.StudyTime).TimeOfDay); @@ -307,7 +307,7 @@ namespace IRaCIS.Core.SCP.Service await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize }); } - //await _studyRepository.SaveChangesAsync(); + await _studyRepository.SaveChangesAsync(); return findStudy.Id; } diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.csproj b/IRaCIS.Core.API/IRaCIS.Core.API.csproj index 34d17bbd7..b35be1db0 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.csproj +++ b/IRaCIS.Core.API/IRaCIS.Core.API.csproj @@ -76,7 +76,7 @@ - + diff --git a/IRaCIS.Core.API/Progranm.cs b/IRaCIS.Core.API/Progranm.cs index 419c98313..0863231cd 100644 --- a/IRaCIS.Core.API/Progranm.cs +++ b/IRaCIS.Core.API/Progranm.cs @@ -80,6 +80,7 @@ builder.Services.AddExceptionHandler(); //健康检查 builder.Services.AddHealthChecks(); +builder.Services.AddSerilog(); //本地化 builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources"); @@ -197,7 +198,7 @@ app.UseExceptionHandler(o => { }); app.UseIRacisHostStaticFileStore(env); //本地化 - await app.UseLocalization(app.Services); +await app.UseLocalization(app.Services); app.UseForwardedHeaders(); diff --git a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs index e4f418057..6492c25ae 100644 --- a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs +++ b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs @@ -1,7 +1,10 @@ using IRaCIS.Core.API._PipelineExtensions.Serilog; +using IRaCIS.Core.Domain.Share; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Serilog; +using System.Linq; namespace IRaCIS.Core.API { @@ -17,8 +20,66 @@ namespace IRaCIS.Core.API app.UseSerilogRequestLogging(opts => { - opts.MessageTemplate = "{TokenUserRealName} {TokenUserTypeShortName} {ClientIp} {LocalIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms"; - opts.EnrichDiagnosticContext = SerilogHelper.EnrichFromRequest; + + opts.MessageTemplate = "{FullName} {UserType} {UserIp} {Host} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms"; + + opts.EnrichDiagnosticContext = (diagnosticContext, httpContext) => + { + + var request = httpContext.Request; + + // Set all the common properties available for every request + diagnosticContext.Set("Host", request.Host.Value); + + + // 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("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.RealName)?.Value); + + diagnosticContext.Set("UserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value); + + var clientIp = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ?? + httpContext.Connection.RemoteIpAddress?.ToString(); + + if (clientIp.StartsWith("::ffff:")) + { + clientIp = clientIp.Substring(7); // 移除前缀 + } + + diagnosticContext.Set("UserIp", clientIp); + + #region 非必要不记录 + //diagnosticContext.Set("Protocol", request.Protocol); + //diagnosticContext.Set("Scheme", request.Scheme); + //// Retrieve the IEndpointFeature selected for the request + //var endpoint = httpContext.GetEndpoint(); + //if (endpoint is object) // endpoint != null + //{ + // diagnosticContext.Set("EndpointName", endpoint.DisplayName); + //} + // Set the content-type of the Response at this point + //diagnosticContext.Set("ContentType", httpContext.Response.ContentType); + #endregion + + + #region old 未用 + //这种获取的Ip不准 配置服务才行 + //diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString()); + + //这种方式可以,但是serilog提供了 就不用了 + //diagnosticContext.Set("TestIP", httpContext.GetUserIp()); + + //这种方式不行 读取的body为空字符串 必须在中间件中读取 + //diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request)); + //diagnosticContext.Set("RequestBody", RequestPayload); + #endregion + + + }; }); diff --git a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs deleted file mode 100644 index 7df3c4267..000000000 --- a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs +++ /dev/null @@ -1,56 +0,0 @@ -using IRaCIS.Core.Domain.Share; -using Microsoft.AspNetCore.Http; -using Serilog; - -namespace IRaCIS.Core.API -{ - public class SerilogHelper - { - //public static string RequestPayload = ""; - - public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext) - { - var request = httpContext.Request; - - // Set all the common properties available for every request - diagnosticContext.Set("Host", request.Host); - - diagnosticContext.Set("Protocol", request.Protocol); - diagnosticContext.Set("Scheme", request.Scheme); - - #region old 未用 - //这种获取的Ip不准 配置服务才行 - //diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString()); - - //这种方式可以,但是serilog提供了 就不用了 - //diagnosticContext.Set("TestIP", httpContext.GetUserIp()); - - //这种方式不行 读取的body为空字符串 必须在中间件中读取 - //diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request)); - //diagnosticContext.Set("RequestBody", RequestPayload); - #endregion - - // 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); - } - - // Set the content-type of the Response at this point - diagnosticContext.Set("ContentType", httpContext.Response.ContentType); - - diagnosticContext.Set("TokenUserRealName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.RealName)?.Value); - - diagnosticContext.Set("TokenUserTypeShortName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value); - - // Retrieve the IEndpointFeature selected for the request - var endpoint = httpContext.GetEndpoint(); - if (endpoint is object) // endpoint != null - { - diagnosticContext.Set("EndpointName", endpoint.DisplayName); - } - } - - - } -} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Serilog/EnricherExtensions.cs b/IRaCIS.Core.API/_ServiceExtensions/Serilog/EnricherExtensions.cs deleted file mode 100644 index a6f10b4eb..000000000 --- a/IRaCIS.Core.API/_ServiceExtensions/Serilog/EnricherExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Serilog; -using Serilog.Configuration; -using Serilog.Core; -using Serilog.Events; -using System; - -namespace IRaCIS.Core.API -{ - public static class EnricherExtensions - { - public static LoggerConfiguration WithHttpContextInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider) - { - if (enrich == null) - throw new ArgumentNullException(nameof(enrich)); - - return enrich.With(new HttpContextEnricher(serviceProvider)); - } - public static LoggerConfiguration WithHttpContextInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider, Action enrichAction) - { - if (enrich == null) - throw new ArgumentNullException(nameof(enrich)); - - return enrich.With(new HttpContextEnricher(serviceProvider, enrichAction)); - } - - } - -} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Serilog/HttpContextEnricher.cs b/IRaCIS.Core.API/_ServiceExtensions/Serilog/HttpContextEnricher.cs deleted file mode 100644 index 2f67772d4..000000000 --- a/IRaCIS.Core.API/_ServiceExtensions/Serilog/HttpContextEnricher.cs +++ /dev/null @@ -1,86 +0,0 @@ -using IRaCIS.Core.Domain.Share; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Serilog.Core; -using Serilog.Events; -using System; -using System.IO; -using System.Threading.Tasks; - -namespace IRaCIS.Core.API -{ - public class HttpContextEnricher : ILogEventEnricher - { - private readonly IServiceProvider _serviceProvider; - private readonly Action _enrichAction; - - public HttpContextEnricher(IServiceProvider serviceProvider) : this(serviceProvider, null) - { } - - public HttpContextEnricher(IServiceProvider serviceProvider, Action enrichAction) - { - _serviceProvider = serviceProvider; - if (enrichAction == null) - { - _enrichAction = (logEvent, propertyFactory, httpContext) => - { - logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", httpContext.Connection.RemoteIpAddress.ToString())); - - logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("LocalIP", httpContext.Connection.LocalIpAddress.MapToIPv4().ToString())); - - logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value)); - logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value)); - - - //这样读取没用 - //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestBody", await ReadRequestBody(httpContext.Request))); - //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", IPHelper.GetIP(httpContext.Request) )); - //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Referer", httpContext.Request.Headers["Referer"].ToString())); - //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path)); - //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method)); - //if (httpContext.Response.HasStarted) - //{ - // logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("response_status", httpContext.Response.StatusCode)); - //} - }; - } - else - { - _enrichAction = enrichAction; - } - } - - - public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) - { - var httpContext = _serviceProvider.GetService()?.HttpContext; - if (null != httpContext) - { - _enrichAction.Invoke(logEvent, propertyFactory, httpContext); - } - } - - private async Task ReadRequestBody(HttpRequest request) - { - // Ensure the request's body can be read multiple times (for the next middlewares in the pipeline). - request.EnableBuffering(); - - using var streamReader = new StreamReader(request.Body, leaveOpen: true); - var requestBody = await streamReader.ReadToEndAsync(); - - // Reset the request's body stream position for next middleware in the pipeline. - request.Body.Position = 0; - return requestBody == null ? String.Empty : requestBody.Trim(); - } - - private async Task ReadResponseBody(HttpResponse response) - { - response.Body.Seek(0, SeekOrigin.Begin); - string responseBody = await new StreamReader(response.Body).ReadToEndAsync(); - response.Body.Seek(0, SeekOrigin.Begin); - - return $"{responseBody}"; - } - } - -} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/SerilogSetup.cs similarity index 59% rename from IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs rename to IRaCIS.Core.API/_ServiceExtensions/SerilogSetup.cs index b7576f849..7622f1c24 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/SerilogSetup.cs @@ -1,5 +1,7 @@ using Serilog; using Serilog.Events; +using Serilog.Formatting.Compact; + //using Serilog.Sinks.Email; using System; @@ -16,18 +18,32 @@ namespace IRaCIS.Core.API .MinimumLevel.Override("Microsoft", LogEventLevel.Information) // Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条 .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning) .MinimumLevel.Override("Hangfire", LogEventLevel.Warning) - .MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning) - .Enrich.WithClientIp() + //如果有反向代理并不会获取到用户的真实IP + //.Enrich.WithClientIp() + //.Enrich.WithRequestHeader("User-Agent") .Enrich.FromLogContext() .Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health")) - //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型 - .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning, - outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}") + //https://github.com/serilog/serilog-formatting-compact + // 控制台输出 JSON 格式 + .WriteTo.Console(formatter: new CompactJsonFormatter(),LogEventLevel.Warning) - .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day, - outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}"); + // 文件输出 JSON 格式 + .WriteTo.File(new CompactJsonFormatter(), $"{AppContext.BaseDirectory}Serilogs/.json", rollingInterval: RollingInterval.Day); + + ////控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型 + //.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning, + // outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {ClientIp} {SourceContext:l} || {Message} || {Exception} ||end {NewLine}") + + //.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day, + // outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {ClientIp} {SourceContext:l} || {Message} || {Exception} ||end {NewLine}"); + + + Log.Logger = config.CreateLogger(); //.WriteTo.MSSqlServer("Data Source=DESKTOP-4TU9A6M;Initial Catalog=CoreFrame;User ID=sa;Password=123456", "logs", autoCreateSqlTable: true, restrictedToMinimumLevel: LogEventLevel.Information)//从左至右四个参数分别是数据库连接字符串、表名、如果表不存在是否创建、最低等级。Serilog会默认创建一些列。 @@ -46,7 +62,7 @@ namespace IRaCIS.Core.API //} //扩展方法 获取上下文的ip 用户名 用户类型 - Log.Logger = config.Enrich.WithHttpContextInfo(serviceProvider).CreateLogger(); + //Log.Logger = config.Enrich.WithHttpContextInfo(serviceProvider).CreateLogger(); } }