using Serilog.Events;
using Serilog.Sinks.PeriodicBatching;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using IdentityServer4.Models;
using Mysqlx.Crud;
using Azure.Core;
using Consul;
using Google.Protobuf.Collections;
using IdentityModel.Client;
using log4net.Core;
using MySqlX.XDevAPI.Common;
using System.Collections.Concurrent;
using System.Data;
using System.Runtime.Serialization;
using NPlatform.Infrastructure.IdGenerators;
using BZPT.Domains.Application;
using NuGet.Protocol.Core.Types;
namespace BZPT
{
///
/// 日志记录类
///
public class LoggingWithDMSink : PeriodicBatchingSink
{
private readonly string _connectionString;
private readonly string _sysName;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly SqlSugarScope _db;
public LoggingWithDMSink(
string connectionString,
string sysName,
IHttpContextAccessor httpContextAccessor,
int batchSizeLimit = 200, // 增大批量大小
TimeSpan period = default)
: base(batchSizeLimit, period == default ? TimeSpan.FromSeconds(15) : period)
{
_connectionString = connectionString;
_sysName = sysName;
_httpContextAccessor = httpContextAccessor;
// 使用 SqlSugarScope 确保线程安全
_db = new SqlSugarScope(new ConnectionConfig
{
ConnectionString = _connectionString,
DbType = SqlSugar.DbType.Dm,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute,
ConfigId= "LoggingID"
});
}
protected override async Task EmitBatchAsync(IEnumerable events)
{
try
{
// 批量分离错误日志和操作日志
var errorLogs = new List();
var operationLogs = new List();
foreach (var logEvent in events)
{
var (errorLog, operationLog) = ParseLogEvent(logEvent);
if (logEvent.Level >= LogEventLevel.Error && errorLog != null)
{
errorLogs.Add(errorLog);
MessageApplication.SendErrorMessage($"{_sysName}:{errorLog.ErrorMessage}");
}
else if(logEvent.Level== LogEventLevel.Warning && errorLog != null)
{
errorLogs.Add(errorLog);
var message = errorLog.Description ?? errorLog.ErrorMessage;
if (message!=null&&message.StartsWith("SqlExecutionTime"))
{
MessageApplication.SendErrorMessage($"{_sysName}发现慢查询:" +message);
}
}
else if (operationLog != null && logEvent.Level == LogEventLevel.Information)
operationLogs.Add(operationLog);
}
// 并行批量写入
var tasks = new List();
if (errorLogs.Count > 0)
{
tasks.Add(_db.Fastest().BulkCopyAsync(errorLogs)); // 达梦批量插入
}
if (operationLogs.Count > 0)
{
tasks.Add(_db.Fastest().BulkCopyAsync(operationLogs));
}
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
// 错误处理(可记录到本地文件)
Console.WriteLine($"日志写入失败: {ex.Message}");
}
}
private (Sys_ErrorLog?, Sys_OperationLog?) ParseLogEvent(LogEvent logEvent)
{
var httpContext = _httpContextAccessor.HttpContext;
var id = GetPropertyValue(logEvent, "RequestID");
if (logEvent.Level >= LogEventLevel.Warning)
{
if(logEvent.RenderMessage().Contains("SameSite=None"))return (null,null);
return (new Sys_ErrorLog
{
SysName = _sysName,
OperationType = GetPropertyValue(logEvent, "OperationType"),
ModuleName = GetPropertyValue(logEvent, "ModuleName"),
Description = logEvent.RenderMessage(),
UserID = GetPropertyValue(logEvent, "UserID"),
UserName = GetPropertyValue(logEvent, "UserName"),
CreateUser = GetPropertyValue(logEvent, "UserName"),
CreateTime = logEvent.Timestamp.DateTime,
IP = httpContext?.Connection?.RemoteIpAddress?.ToString(),
UserAgent = httpContext?.Request?.Headers["User-Agent"].ToString(),
RequestMethod = httpContext?.Request?.Method,
RequestUri = httpContext?.Request?.Path.ToString(),
Parameters = GetPropertyValue(logEvent, "Parameters"),
Result = GetPropertyValue(logEvent, "Result"),
Status = GetIntPropertyValue(logEvent, "Status"),
ErrorCode = GetPropertyValue(logEvent, "ErrorCode"),
RequestID = id,
ObjID= GetPropertyValue(logEvent, "ObjID"),
ObjType = GetPropertyValue(logEvent, "ObjType"),
ErrorMessage = logEvent.Exception?.ToString(),
CostTime = GetIntPropertyValue(logEvent, "CostTime")
}, null);
}
else if (!string.IsNullOrEmpty(id)) //只记录请求日志
{
return (null, new Sys_OperationLog
{
SysName = _sysName,
OperationType = GetPropertyValue(logEvent, "OperationType"),
ModuleName = GetPropertyValue(logEvent, "ModuleName"),
Description = logEvent.RenderMessage(),
UserID = GetPropertyValue(logEvent, "UserID"),
UserName = GetPropertyValue(logEvent, "UserName"),
CreateUser = GetPropertyValue(logEvent, "UserName"),
CreateTime = logEvent.Timestamp.DateTime,
IP = GetPropertyValue(logEvent, "IP"),
UserAgent = GetPropertyValue(logEvent, "UserAgent"),
RequestMethod = httpContext?.Request?.Method,
RequestUri = httpContext?.Request?.Path.ToString(),
Parameters = GetPropertyValue(logEvent, "Parameters"),
Result = GetPropertyValue(logEvent, "Result"),
Status = GetIntPropertyValue(logEvent, "Status"),
ErrorCode = GetPropertyValue(logEvent, "ErrorCode"),
ErrorMessage = logEvent.MessageTemplate.ToStrNoNull(),
RequestID =id,
ObjID = GetPropertyValue(logEvent, "ObjID"),
ObjType = GetPropertyValue(logEvent, "ObjType"),
CostTime = GetIntPropertyValue(logEvent, "CostTime")
});
}
else
{
return (null,null);
}
}
private string GetPropertyValue(LogEvent logEvent, string propertyName)
{
if (logEvent.Properties.TryGetValue(propertyName, out var value))
{
return value.ToString().Trim('"');
}
return string.Empty;
}
private int? GetIntPropertyValue(LogEvent logEvent, string propertyName)
{
if (logEvent.Properties.TryGetValue(propertyName, out var value) && int.TryParse(value.ToString().Trim('"'), out var result))
{
return result;
}
return null;
}
}
///
///OperationLog,操作日志(一个月)
///
[Serializable]
[Table(name: "Sys_OperationLog")]
public partial class Sys_OperationLog : EntityBase
{
///
/// 操作类型(如 LOGIN, CREATE, UPDATE, DELETE, EXPORT, API_CALL 等)
///
[Display(Name = "操作类型(如 LOGIN, CREATE, UPDATE, DELETE, EXPORT, API_CALL 等)")]
[StringLength(200)]
public string OperationType { get; set; }
///
/// 系统名称
///
[Display(Name = "系统名称")]
[StringLength(400)]
public string SysName { get; set; }
///
/// 模块名称
///
[Display(Name = "模块名称")]
[StringLength(400)]
public string ModuleName { get; set; }
///
/// 操作描述(如 用户登录系统, 删除ID: 1001)
///
[Display(Name = "操作描述(如 用户登录系统, 删除ID: 1001)")]
[StringLength(2000)]
public string Description { get; set; }
///
/// 操作用户ID
///
[Display(Name = "操作用户ID")]
[StringLength(144)]
public string UserID { get; set; }
///
/// 用户名称
///
[Display(Name = "用户名称")]
[StringLength(144)]
public string UserName { get; set; }
///
/// IP
///
[Display(Name = "IP")]
[StringLength(180)]
public string IP { get; set; }
///
/// 用户客户端信息(浏览器、设备等)
///
[Display(Name = "用户客户端信息(浏览器、设备等)")]
[StringLength(2000)]
public string UserAgent { get; set; }
///
/// HTTP请求方法(如 GET, POST, DELETE)
///
[Display(Name = "HTTP请求方法(如 GET, POST, DELETE)")]
[StringLength(40)]
public string RequestMethod { get; set; }
///
/// 请求url
///
[Display(Name = "请求url")]
[StringLength(2000)]
public string RequestUri { get; set; }
///
/// 请求参数或操作详情(JSON格式,记录请求体或关键参数)
///
[Display(Name = "请求参数或操作详情(JSON格式,记录请求体或关键参数)")]
[StringLength(2147483647)]
public string Parameters { get; set; }
///
/// 操作结果
///
[Display(Name = "操作结果")]
[StringLength(2147483647)]
public string Result { get; set; }
///
/// 操作状态
///
[Display(Name = "操作状态")]
public int? Status { get; set; }
///
/// 错误码
///
[Display(Name = "错误码")]
[StringLength(200)]
public string ErrorCode { get; set; }
///
/// 错误详情
///
[Display(Name = "错误详情")]
[StringLength(2147483647)]
public string ErrorMessage { get; set; }
///
/// 对象ID
///
[Display(Name = "对象ID")]
[StringLength(144)]
public string RequestID { get; set; }
///
/// 耗时
///
[Display(Name = "耗时")]
public int? CostTime { get; set; }
public string ObjID { get; set; }
public string ObjType { get; set; }
}
///
///ErrorLog,异常日志
///
[Serializable]
[Table(name: "Sys_ErrorLog")]
public partial class Sys_ErrorLog : EntityBase
{
///
/// 操作类型(如 LOGIN, CREATE, UPDATE, DELETE, EXPORT, API_CALL 等)
///
[Display(Name = "操作类型(如 LOGIN, CREATE, UPDATE, DELETE, EXPORT, API_CALL 等)")]
[StringLength(200)]
public string OperationType { get; set; }
///
/// 系统名称
///
[Display(Name = "系统名称")]
[StringLength(400)]
public string SysName { get; set; }
///
/// 模块名称
///
[Display(Name = "模块名称")]
[StringLength(400)]
public string ModuleName { get; set; }
///
/// 操作描述(如 用户登录系统, 删除ID: 1001)
///
[Display(Name = "操作描述(如 用户登录系统, 删除ID: 1001)")]
[StringLength(2000)]
public string Description { get; set; }
///
/// 操作用户ID
///
[Display(Name = "操作用户ID")]
[StringLength(144)]
public string UserID { get; set; }
///
/// 用户名称
///
[Display(Name = "用户名称")]
[StringLength(144)]
public string UserName { get; set; }
///
/// IP
///
[Display(Name = "IP")]
[StringLength(180)]
public string IP { get; set; }
///
/// 用户客户端信息(浏览器、设备等)
///
[Display(Name = "用户客户端信息(浏览器、设备等)")]
[StringLength(2000)]
public string UserAgent { get; set; }
///
/// HTTP请求方法(如 GET, POST, DELETE)
///
[Display(Name = "HTTP请求方法(如 GET, POST, DELETE)")]
[StringLength(40)]
public string RequestMethod { get; set; }
///
/// 请求url
///
[Display(Name = "请求url")]
[StringLength(2000)]
public string RequestUri { get; set; }
///
/// 请求参数或操作详情(JSON格式,记录请求体或关键参数)
///
[Display(Name = "请求参数或操作详情(JSON格式,记录请求体或关键参数)")]
[StringLength(2147483647)]
public string Parameters { get; set; }
///
/// 操作结果
///
[Display(Name = "操作结果")]
[StringLength(2147483647)]
public string Result { get; set; }
///
/// 操作状态
///
[Display(Name = "操作状态")]
public int? Status { get; set; }
///
/// 错误码
///
[Display(Name = "错误码")]
[StringLength(200)]
public string ErrorCode { get; set; }
///
/// 错误详情
///
[Display(Name = "错误详情")]
[StringLength(2147483647)]
public string ErrorMessage { get; set; }
///
/// 对象ID
///
[Display(Name = "对象ID")]
[StringLength(144)]
public string RequestID { get; set; }
///
/// 耗时
///
[Display(Name = "耗时")]
public int? CostTime { get; set; }
public string ObjID { get; set; }
public string ObjType { get; set; }
}
}
/// CREATE TABLESPACE LOGS_DATA DATAFILE 'LOGS_DATA01.DBF' SIZE 1024 AUTOEXTEND ON;
///ALTER TABLE Sys_ErrorLog MOVE TABLESPACE LOGS_DATA;
/// ALTER TABLE Sys_OperationLog MOVE TABLESPACE LOGS_DATA;
/// ALTER TABLE SYS_OPERATIONLOGHIS MOVE TABLESPACE LOGS_DATA;
/// GRANT USE TABLESPACE LOGS_DATA TO BZPT;
///-- 则需要授予对这些表的具体操作权限,例如查询、插入、更新、删除等
///GRANT SELECT, INSERT, UPDATE, DELETE ON Sys_ErrorLog TO BZPT;
///GRANT SELECT, INSERT, UPDATE, DELETE ON Sys_OperationLog TO BZPT;
///GRANT SELECT, INSERT, UPDATE, DELETE ON SYS_OPERATIONLOGHIS TO BZPT;
//--删除表和索引
//DROP TABLE IF EXISTS Sys_OperationLog;
//DROP INDEX IF EXISTS idx_module_type;
//DROP INDEX IF EXISTS idx_resource;
//DROP INDEX IF EXISTS Index_USER_TIME;
//DROP INDEX IF EXISTS Index_OPTime;
//DROP INDEX IF EXISTS Index_SysTimeOP;
//--创建表 Sys_OperationLog(修正后的分区语法)
//CREATE TABLE Sys_OperationLog (
// ID BIGINT NOT NULL COMMENT 'ID',
// OperationType VARCHAR(50) COMMENT '操作类型',
// SysName VARCHAR(100) COMMENT '系统名称',
// ModuleName VARCHAR(100) COMMENT '模块名称',
// Description VARCHAR(500) COMMENT '操作描述',
// UserID VARCHAR(36) COMMENT '用户ID',
// UserName VARCHAR(36) COMMENT '用户名称',
// OperationTime TIMESTAMP COMMENT '操作时间(精确到毫秒)',
// IP VARCHAR(45) COMMENT 'IP',
// UserAgent VARCHAR(500) COMMENT '客户端信息',
// RequestMethod VARCHAR(10) COMMENT 'HTTP方法',
// RequestUri VARCHAR(500) COMMENT '请求URI',
// Parameters TEXT COMMENT '请求参数',
// Result1 TEXT COMMENT '操作结果',
// Status INT COMMENT '状态',
// ErrorCode VARCHAR(50) COMMENT '错误码',
// ErrorMessage TEXT COMMENT '错误详情',
// ObjType VARCHAR(200) COMMENT '对象类型',
// ObjID VARCHAR(36) COMMENT '对象ID',
// CostTime INT COMMENT '耗时',
// PRIMARY KEY (ID)
//)
//TABLESPACE LOGS_DATA
//PARTITION BY RANGE (OperationTime) ( -- 直接使用日期字符串
// PARTITION P2025H1 VALUES LESS THAN (TO_TIMESTAMP('2025-07-01', 'YYYY-MM-DD')),
// PARTITION P2025H2 VALUES LESS THAN (TO_TIMESTAMP('2026-01-01', 'YYYY-MM-DD')),
// PARTITION P2026H1 VALUES LESS THAN (TO_TIMESTAMP('2026-07-01', 'YYYY-MM-DD')),
// PARTITION P2026H2 VALUES LESS THAN (TO_TIMESTAMP('2027-01-01', 'YYYY-MM-DD')),
// PARTITION P2027H1 VALUES LESS THAN (TO_TIMESTAMP('2027-07-01', 'YYYY-MM-DD')),
// PARTITION P2027H2 VALUES LESS THAN (TO_TIMESTAMP('2028-01-01', 'YYYY-MM-DD')),
// PARTITION P2028H1 VALUES LESS THAN (TO_TIMESTAMP('2028-07-01', 'YYYY-MM-DD')),
// PARTITION P2028H2 VALUES LESS THAN (TO_TIMESTAMP('2029-01-01', 'YYYY-MM-DD')),
// PARTITION P2029H1 VALUES LESS THAN (TO_TIMESTAMP('2029-07-01', 'YYYY-MM-DD')),
// PARTITION P2029H2 VALUES LESS THAN (TO_TIMESTAMP('2030-01-01', 'YYYY-MM-DD')),
// PARTITION P2030H1 VALUES LESS THAN (TO_TIMESTAMP('2030-07-01', 'YYYY-MM-DD')),
// PARTITION P2030H2 VALUES LESS THAN (TO_TIMESTAMP('2031-01-01', 'YYYY-MM-DD'))
//);
//--添加表注释
//COMMENT ON TABLE Sys_OperationLog IS '操作日志(一个月)';
//--创建索引(保持原样)
//CREATE INDEX Index_SysTimeOP ON Sys_OperationLog(SysName, OperationTime DESC);
//CREATE INDEX Index_OPTime ON Sys_OperationLog(OperationTime DESC);
//CREATE INDEX Index_USER_TIME ON Sys_OperationLog(UserID, OperationTime DESC);
//CREATE INDEX idx_resource ON Sys_OperationLog(ObjType, ObjID);
//CREATE INDEX idx_module_type ON Sys_OperationLog(ModuleName, OperationType);