Browse Source

更新日志记录和客户端信息处理功能

- 添加了对Serilog.Events命名空间的引用。
- 使用简化的对象初始化语法配置SqlSugarClient。
- 在日志配置中增加请求路径过滤,调整日志消息模板以包含客户端IP和用户代理信息。
- 增强HTTP请求的日志记录,记录请求内容类型和长度。
- 在CommonFun类中新增获取客户端IP地址的方法,处理代理和负载均衡情况。
- 增加用户代理检测功能,识别操作系统类型并解析移动设备和PC设备的版本信息。
Lyyyi 2 weeks ago
parent
commit
b667e91bdc
2 changed files with 164 additions and 5 deletions
  1. 53 5
      OASystem/OASystem.Api/Program.cs
  2. 111 0
      OASystem/OASystem.Infrastructure/Tools/CommonFun.cs

+ 53 - 5
OASystem/OASystem.Api/Program.cs

@@ -15,7 +15,7 @@ using Quartz;
 using Quartz.Impl;
 using Quartz.Spi;
 using QuzrtzJob.Factory;
-using Serilog.Formatting.Compact;
+using Serilog.Events;
 
 Console.Title = $"FMGJ OASystem Server";
 var builder = WebApplication.CreateBuilder(args);
@@ -122,13 +122,13 @@ builder.Services.AddScoped(options =>
 {
     return new SqlSugarClient(new List<ConnectionConfig>()
     {
-        new ConnectionConfig() {
+        new() {
             ConfigId = DBEnum.OA2023DB,
             ConnectionString = _config.GetConnectionString("OA2023DB"),
             DbType = DbType.SqlServer,
             IsAutoCloseConnection = true
         },
-        new ConnectionConfig()
+        new()
         {
             ConfigId = DBEnum.OA2014DB,
             ConnectionString = _config.GetConnectionString("OA2014DB"),
@@ -290,8 +290,21 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
 #endregion
 
 #region 初始化日志
+var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
 Log.Logger = new LoggerConfiguration()
-       .MinimumLevel.Debug()
+        //不记录定时访问API
+        .Filter.ByIncludingOnly(logEvent =>
+        {
+            if (logEvent.Properties.TryGetValue("RequestPath", out var pathValue))
+            {
+                var path = pathValue.ToString().Trim('"');
+                return !path.StartsWith("/api/System/PotsMessageUnreadTotalCount");
+            }
+            return true;
+        })
+       .MinimumLevel.Information()
+       .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
+       .MinimumLevel.Override("System", LogEventLevel.Warning)
        .Enrich.FromLogContext()
        .WriteTo.Console()
        .WriteTo.File(Path.Combine("Logs", @"Log.txt"), rollingInterval: RollingInterval.Day)
@@ -399,7 +412,42 @@ var app = builder.Build();
 //serilog日志 请求中间管道
 app.UseSerilogRequestLogging(options =>
 {
-    options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000}ms";
+    options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} from {ClientIP} (UA: {UserAgent}, Referer: {Referer}) - {StatusCode} in {Elapsed} ms";
+
+    // 自定义日志级别
+    options.GetLevel = (httpContext, elapsed, ex) =>
+    {
+        if (ex != null) return LogEventLevel.Error;
+        if (httpContext.Response.StatusCode > 499) return LogEventLevel.Error;
+
+        // 对健康检查等端点使用更低级别
+        if (httpContext.Request.Path.StartsWithSegments("/health"))
+            return LogEventLevel.Debug;
+
+        return LogEventLevel.Information;
+    };
+
+    // 丰富日志上下文
+    options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
+    {
+        // 获取客户端IP(处理代理情况)
+        var ipAddress = CommonFun.GetClientIpAddress(httpContext);
+        var userAgent = CommonFun.DetectOS(httpContext.Request.Headers.UserAgent.ToString());
+
+        // 添加IP和其他有用信息到日志上下文
+        diagnosticContext.Set("ClientIP", ipAddress);
+        diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
+        diagnosticContext.Set("UserAgent", userAgent);
+        diagnosticContext.Set("Referer", httpContext.Request.Headers.Referer.ToString());
+
+        // 对于API请求添加额外信息
+        if (httpContext.Request.Path.StartsWithSegments("/api"))
+        {
+            diagnosticContext.Set("RequestContentType", httpContext.Request.ContentType);
+            diagnosticContext.Set("RequestContentLength", httpContext.Request.ContentLength ?? 0);
+        }
+    };
+
 });
 
 AutofacIocManager.Instance.Container = app.UseHostFiltering().ApplicationServices.GetAutofacRoot();//AutofacIocManager

+ 111 - 0
OASystem/OASystem.Infrastructure/Tools/CommonFun.cs

@@ -213,6 +213,117 @@ public static class CommonFun
 
         return ip;
     }
+
+    /// <summary>
+    /// IP地址获取方法(serilog 单独处理)
+    /// </summary>
+    /// <param name="httpContext"></param>
+    /// <returns></returns>
+    public static string GetClientIpAddress(HttpContext httpContext)
+    {
+        // 1. 尝试从X-Forwarded-For获取(处理代理/负载均衡情况)
+        if (httpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var forwardedFor))
+        {
+            // X-Forwarded-For可能包含逗号分隔的IP列表(客户端,代理1,代理2,...)
+            var ip = forwardedFor.FirstOrDefault()?.Split(',').FirstOrDefault()?.Trim();
+            if (!string.IsNullOrEmpty(ip))
+                return ip;
+        }
+
+        // 2. 尝试从X-Real-IP获取
+        if (httpContext.Request.Headers.TryGetValue("X-Real-IP", out var realIp))
+        {
+            var ip = realIp.FirstOrDefault();
+            if (!string.IsNullOrEmpty(ip))
+                return ip;
+        }
+
+        // 3. 使用连接远程IP地址
+        var remoteIp = httpContext.Connection.RemoteIpAddress;
+
+        // 处理IPv6映射的IPv4地址
+        if (remoteIp != null && remoteIp.IsIPv4MappedToIPv6)
+        {
+            remoteIp = remoteIp.MapToIPv4();
+        }
+
+        return remoteIp?.ToString() ?? "unknown";
+    }
+
+    #endregion
+
+    #region UserAgent
+    /// <summary>
+    /// 分层检测策略实现操作系统识别
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    public static string DetectOS(string userAgent)
+    {
+        if (string.IsNullOrEmpty(userAgent)) return "Unknown";
+
+        // 移动端优先检测
+        if (IsMobile(userAgent))
+        {
+            if (userAgent.Contains("iPhone")) return ParseIOSVersion(userAgent);
+            if (userAgent.Contains("Android")) return ParseAndroidVersion(userAgent);
+            if (userAgent.Contains("Windows Phone")) return "Windows Mobile";
+        }
+
+        // PC端检测
+        if (userAgent.Contains("Windows NT")) return ParseWindowsVersion(userAgent);
+        if (userAgent.Contains("Macintosh")) return "macOS";
+        if (userAgent.Contains("Linux")) return "Linux";
+
+        return "Other";
+    }
+
+    /// <summary>
+    /// 移动设备判断
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    private static bool IsMobile(string userAgent)
+    {
+        string[] mobileKeywords = { "iPhone", "Android", "Windows Phone", "iPad", "iPod" };
+        return mobileKeywords.Any(key => userAgent.Contains(key));
+    }
+
+    /// <summary>
+    /// iOS版本解析
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    private static string ParseIOSVersion(string userAgent)
+    {
+        var match = Regex.Match(userAgent, @"iPhone OS (\d+_\d+)");
+        return match.Success ? $"iOS {match.Groups[1].Value.Replace('_', '.')}" : "iOS";
+    }
+
+    /// <summary>
+    ///  Android版本解析
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    private static string ParseAndroidVersion(string userAgent)
+    {
+        var match = Regex.Match(userAgent, @"Android (\d+)");
+        return match.Success ? $"Android {match.Groups[1].Value}" : "Android";
+    }
+
+    /// <summary>
+    /// Windows版本解析
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    private static string ParseWindowsVersion(string userAgent)
+    {
+        if (userAgent.Contains("Windows NT 10.0")) return "Windows 10/11";
+        if (userAgent.Contains("Windows NT 6.3")) return "Windows 8.1";
+        if (userAgent.Contains("Windows NT 6.2")) return "Windows 8";
+        return "Windows";
+    }
+
     #endregion
 
     #region 随机数