using Microsoft.AspNetCore.Http;
|
using SqlSugar;
|
using System;
|
using System.Collections.Concurrent;
|
using System.Collections.Generic;
|
using System.Data;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using WIDESEA_Core.DB;
|
using WIDESEA_Core.Helper;
|
using WIDESEA_Core.HttpContextUser;
|
using WIDESEA_Core.Seed;
|
|
namespace WIDESEA_Core.LogHelper
|
{
|
public static class Logger
|
{
|
public static ConcurrentQueue<dynamic> loggerQueueData = new ConcurrentQueue<dynamic>();
|
static Logger()
|
{
|
Task.Run(() =>
|
{
|
StartWriteLog();
|
});
|
|
}
|
|
/// <summary>
|
/// 日志写入线程的入口方法,负责从队列中取出日志数据并批量写入数据库
|
/// </summary>
|
/// <remarks>
|
/// 该方法会持续运行,每5秒检查一次日志队列:
|
/// 1. 当队列有数据且缓存表行数小于500时,将数据移入缓存表
|
/// 2. 当缓存表有数据时,使用SqlSugar进行批量插入
|
/// 3. 发生异常时会输出错误信息但不会终止线程
|
/// </remarks>
|
static void StartWriteLog()
|
{
|
DataTable queueTable = CreateEmptyTable();
|
while (true)
|
{
|
try
|
{
|
//如果队列中有数据且队列表中的行数小于500,则将队列中的数据写入队列表
|
if (loggerQueueData.Count() > 0 && queueTable.Rows.Count < 500)
|
{
|
DequeueToTable(queueTable); continue;
|
}
|
//每5秒写一次数据
|
Thread.Sleep(5000);
|
|
//如果队列表中的行数为0,则跳过本次循环
|
if (queueTable.Rows.Count == 0) { continue; }
|
|
string str = DBContext.GetMainConnectionDb().Connection;
|
//创建SqlSugarClient对象,用于连接数据库
|
SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig()
|
{
|
ConnectionString = DBContext.GetMainConnectionDb().Connection,
|
IsAutoCloseConnection = true,
|
DbType = DBContext.DbType,
|
});
|
|
//将队列表中的数据批量插入数据库
|
int rows = sugarClient.Fastest<DataTable>().AS("Sys_Log").BulkCopy(queueTable);
|
|
//清空队列表
|
queueTable.Clear();
|
}
|
catch (Exception ex)
|
{
|
//打印异常信息
|
Console.WriteLine(ex.ToString());
|
}
|
}
|
}
|
|
/// <summary>
|
/// 从日志队列中取出日志项并添加到数据表中
|
/// </summary>
|
/// <param name="queueTable">要添加日志数据的目标数据表</param>
|
/// <remarks>
|
/// 1. 处理日志日期:如果BeginDate为空或早于2010年,则设为当前时间;EndDate为空则设为当前时间
|
/// 2. 清理RequestParam和ResponseParam中的换行符
|
/// 3. 计算请求耗时(EndDate - BeginDate的毫秒数)
|
/// 4. 处理UserId为空的情况,默认设为-1
|
/// </remarks>
|
private static void DequeueToTable(DataTable queueTable)
|
{
|
loggerQueueData.TryDequeue(out dynamic log);
|
if (log != null)
|
{
|
DataRow row = queueTable.NewRow();
|
// 如果log的BeginDate为空或者BeginDate的年份小于2010,则将BeginDate设置为当前时间
|
if (log.BeginDate == null || log.BeginDate?.Year < 2010)
|
{
|
log.BeginDate = DateTime.Now;
|
}
|
// 如果log的EndDate为空,则将EndDate设置为当前时间
|
if (log.EndDate == null)
|
{
|
log.EndDate = DateTime.Now;
|
}
|
// row["Id"] = log.Id;
|
// 将RequestParam中的换行符替换为空字符串
|
row["RequestParam"] = log.RequestParam?.Replace("\r\n", "");
|
// 将ResponseParam中的换行符替换为空字符串
|
row["ResponseParam"] = log.ResponseParam?.Replace("\r\n", "");
|
//row["Success"] = log.Success ?? -1;
|
// 将BeginDate设置为log的BeginDate
|
row["BeginDate"] = log.BeginDate;
|
// 将EndDate设置为log的EndDate
|
row["EndDate"] = log.EndDate;
|
// 计算ElapsedTime,即EndDate减去BeginDate的毫秒数
|
row["ElapsedTime"] = ((DateTime)log.EndDate - (DateTime)log.BeginDate).TotalMilliseconds;
|
// 将UserIP设置为log的UserIP
|
row["UserIP"] = log.UserIP;
|
// 将Url设置为log的Url
|
row["Url"] = log.Url;
|
// 如果log的UserId为空,则将UserId设置为-1,否则设置为log的UserId
|
row["UserId"] = log.UserId ?? -1;
|
// 将UserName设置为log的UserName
|
row["UserName"] = log.UserName;
|
// 将row添加到queueTable中
|
queueTable.Rows.Add(row);
|
}
|
}
|
|
/// <summary>
|
/// 创建一个空的DataTable,用于存储日志数据
|
/// </summary>
|
/// <returns>包含日志字段结构的空DataTable</returns>
|
/// <remarks>
|
/// 包含以下列:BeginDate, ElapsedTime, EndDate, RequestParam, ResponseParam,
|
/// Url, UserIP, UserName, UserId。其他注释掉的列为预留字段。
|
/// </remarks>
|
private static DataTable CreateEmptyTable()
|
{
|
DataTable queueTable = new DataTable();
|
queueTable.Columns.Add("BeginDate", Type.GetType("System.DateTime"));
|
queueTable.Columns.Add("ElapsedTime", Type.GetType("System.Int32"));
|
queueTable.Columns.Add("EndDate", Type.GetType("System.DateTime"));
|
queueTable.Columns.Add("RequestParam", typeof(string));
|
queueTable.Columns.Add("ResponseParam", typeof(string));
|
//queueTable.Columns.Add("Success", Type.GetType("System.Int32"));
|
queueTable.Columns.Add("Url", typeof(string));
|
queueTable.Columns.Add("UserIP", typeof(string));
|
queueTable.Columns.Add("UserName", typeof(string));
|
queueTable.Columns.Add("UserId", Type.GetType("System.Int32"));
|
//queueTable.Columns.Add("LogType", typeof(string));
|
//queueTable.Columns.Add("ExceptionInfo", typeof(string));
|
//queueTable.Columns.Add("ServiceIP", typeof(string));
|
//queueTable.Columns.Add("BrowserType", typeof(string));
|
//queueTable.Columns.Add("Role_Id", Type.GetType("System.Int32"));
|
return queueTable;
|
}
|
|
/// <summary>
|
/// 添加系统日志记录
|
/// </summary>
|
/// <param name="url">请求URL地址</param>
|
/// <param name="requestParameter">请求参数</param>
|
/// <param name="responseParameter">响应参数</param>
|
/// <param name="beginDate">请求开始时间</param>
|
/// <remarks>
|
/// 该方法会记录请求的URL、参数、响应以及用户信息,并自动计算请求耗时。
|
/// 如果获取用户信息时发生异常,仍会记录基本请求信息。
|
/// </remarks>
|
public static void Add(string url, string requestParameter, string responseParameter, DateTime beginDate)
|
{
|
dynamic? log = null;
|
try
|
{
|
//获取当前用户
|
IUser? user = App.User;
|
//创建日志对象
|
log = new
|
{
|
//请求开始时间
|
BeginDate = beginDate,
|
//请求结束时间
|
EndDate = DateTime.Now,
|
//请求参数
|
RequestParam = requestParameter,
|
//响应参数
|
ResponseParam = responseParameter,
|
//请求URL
|
Url = url,
|
//客户端IP
|
UserIP = "",
|
//用户ID
|
UserId = user?.UserId,
|
//用户名
|
UserName = user?.UserName
|
};
|
}
|
catch (Exception exception)
|
{
|
//如果发生异常,则创建日志对象
|
log = log ?? new
|
{
|
//请求开始时间
|
BeginDate = DateTime.Now,
|
//请求结束时间
|
EndDate = DateTime.Now,
|
//请求参数
|
RequestParam = requestParameter,
|
//响应参数
|
ResponseParam = responseParameter,
|
};
|
}
|
//添加系统日志
|
loggerQueueData.Enqueue(log);
|
}
|
|
/// <summary>
|
/// 添加请求和响应日志记录
|
/// </summary>
|
/// <param name="requestParameter">请求参数</param>
|
/// <param name="responseParameter">响应参数</param>
|
/// <remarks>
|
/// 该方法会记录HTTP请求的详细信息,包括请求时间、响应时间、请求URL、客户端IP、用户信息等。
|
/// 如果发生异常,仍会记录基本的请求和响应信息。
|
/// 对于OPTIONS请求方法或空HttpContext的情况会直接返回不做记录。
|
/// </remarks>
|
public static void Add(string requestParameter, string responseParameter)
|
{
|
dynamic? log = null;
|
try
|
{
|
//获取当前HttpContext
|
HttpContext? context = App.HttpContext;
|
//如果HttpContext为空,则返回
|
if (context == null)
|
{
|
return;
|
}
|
//如果请求方法为OPTIONS,则返回
|
if (context.Request.Method == "OPTIONS") return;
|
//获取RequestLogModel实例
|
RequestLogModel logModel = (context.RequestServices.GetService(typeof(RequestLogModel)) as RequestLogModel) ?? new RequestLogModel { RequestDate = DateTime.Now };
|
|
//获取当前用户
|
IUser? user = App.User;
|
//创建日志对象
|
log = new
|
{
|
//请求开始时间
|
BeginDate = logModel.RequestDate,
|
//请求结束时间
|
EndDate = DateTime.Now,
|
//请求参数
|
RequestParam = requestParameter,
|
//响应参数
|
ResponseParam = responseParameter,
|
//请求URL
|
Url = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + context.Request.Path,
|
//客户端IP
|
UserIP = GetClientIP(context)?.Replace("::ffff:", ""),
|
//用户ID
|
UserId = user?.UserId,
|
//用户名
|
UserName = user?.UserName
|
};
|
}
|
catch (Exception exception)
|
{
|
//如果发生异常,则创建日志对象
|
log = log ?? new
|
{
|
//请求开始时间
|
BeginDate = DateTime.Now,
|
//请求结束时间
|
EndDate = DateTime.Now,
|
//请求参数
|
RequestParam = requestParameter,
|
//响应参数
|
ResponseParam = responseParameter,
|
};
|
}
|
//添加系统日志
|
loggerQueueData.Enqueue(log);
|
}
|
|
/// <summary>
|
/// 获取客户端的IP地址
|
/// </summary>
|
/// <param name="context">Http上下文对象</param>
|
/// <returns>客户端的IP地址字符串</returns>
|
/// <remarks>
|
/// 优先从X-Forwarded-For请求头中获取IP地址,如果不存在则从连接远程IP地址获取
|
/// </remarks>
|
public static string GetClientIP(HttpContext context)
|
{
|
// 获取请求头中的X-Forwarded-For字段,并将其转换为字符串
|
var ip = context.Request.Headers["X-Forwarded-For"].ObjToString();
|
// 如果X-Forwarded-For字段为空,则获取远程IP地址
|
if (string.IsNullOrEmpty(ip))
|
{
|
ip = context.Connection.RemoteIpAddress.ObjToString();
|
}
|
|
// 返回IP地址
|
return ip;
|
}
|
|
|
}
|
}
|