添加调试页
This commit is contained in:
parent
d34b491c11
commit
d314597e8d
6 changed files with 242 additions and 5 deletions
|
@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using iFileProxy.Attributes;
|
using iFileProxy.Attributes;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace iFileProxy.Controllers
|
namespace iFileProxy.Controllers
|
||||||
{
|
{
|
||||||
|
@ -386,5 +387,54 @@ namespace iFileProxy.Controllers
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取系统日志
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(UserMask.SuperAdmin)]
|
||||||
|
[HttpGet("GetSystemLogs")]
|
||||||
|
public async Task<ActionResult<CommonRsp>> GetSystemLogs(
|
||||||
|
[FromQuery] int page = 1,
|
||||||
|
[FromQuery] int pageSize = 10,
|
||||||
|
[FromQuery] string? level = null,
|
||||||
|
[FromQuery] string? keyword = null,
|
||||||
|
[FromQuery] DateTime? startTime = null,
|
||||||
|
[FromQuery] DateTime? endTime = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 解析日志级别
|
||||||
|
LogEventLevel? logLevel = null;
|
||||||
|
if (!string.IsNullOrEmpty(level) && Enum.TryParse<LogEventLevel>(level, true, out var parsedLevel))
|
||||||
|
{
|
||||||
|
logLevel = parsedLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _dbGateService.GetPagedSystemLogsAsync(
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
logLevel,
|
||||||
|
keyword,
|
||||||
|
startTime,
|
||||||
|
endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
return Ok(new CommonRsp
|
||||||
|
{
|
||||||
|
Retcode = 0,
|
||||||
|
Message = "success",
|
||||||
|
Data = result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "获取系统日志失败");
|
||||||
|
return Ok(new CommonRsp
|
||||||
|
{
|
||||||
|
Retcode = 1,
|
||||||
|
Message = "获取系统日志失败"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
22
src/Models/SystemLog.cs
Normal file
22
src/Models/SystemLog.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
public class SystemLog
|
||||||
|
{
|
||||||
|
[JsonProperty("log_id")]
|
||||||
|
public string LogId { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
[JsonProperty("level")]
|
||||||
|
public string Level { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonProperty("message")]
|
||||||
|
public string Message { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonProperty("exception")]
|
||||||
|
public string? Exception { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("properties")]
|
||||||
|
public string Properties { get; set; } = "{}";
|
||||||
|
|
||||||
|
[JsonProperty("timestamp")]
|
||||||
|
public DateTime Timestamp { get; set; }
|
||||||
|
}
|
|
@ -127,6 +127,9 @@ namespace iFileProxy
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
|
var dbGateService = app.Services.GetRequiredService<DatabaseGateService>();
|
||||||
|
SerilogConfig.CreateLogger(dbGateService);
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,16 @@
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using iFileProxy.Sinks;
|
||||||
|
using iFileProxy.Services;
|
||||||
|
|
||||||
public static class SerilogConfig
|
public static class SerilogConfig
|
||||||
{
|
{
|
||||||
public static void CreateLogger()
|
private static DatabaseGateService? _dbGateService;
|
||||||
|
|
||||||
|
public static void CreateLogger(DatabaseGateService? dbGateService = null)
|
||||||
{
|
{
|
||||||
|
_dbGateService = dbGateService; // 保存实例以供后续使用
|
||||||
var baseLogPath = Path.Combine(AppContext.BaseDirectory, "logs");
|
var baseLogPath = Path.Combine(AppContext.BaseDirectory, "logs");
|
||||||
var appName = AppDomain.CurrentDomain.FriendlyName;
|
var appName = AppDomain.CurrentDomain.FriendlyName;
|
||||||
|
|
||||||
|
@ -17,7 +22,7 @@
|
||||||
Directory.CreateDirectory(baseLogPath);
|
Directory.CreateDirectory(baseLogPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
var loggerConfiguration = new LoggerConfiguration()
|
||||||
#if RELEASE
|
#if RELEASE
|
||||||
.MinimumLevel.Information()
|
.MinimumLevel.Information()
|
||||||
#else
|
#else
|
||||||
|
@ -61,8 +66,20 @@
|
||||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}",
|
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}",
|
||||||
rollingInterval: RollingInterval.Day,
|
rollingInterval: RollingInterval.Day,
|
||||||
fileSizeLimitBytes: 1073741824))
|
fileSizeLimitBytes: 1073741824))
|
||||||
.Enrich.WithProperty("node_ip", GetIpAddress())
|
.Enrich.WithProperty("node_ip", GetIpAddress());
|
||||||
.CreateLogger();
|
|
||||||
|
// 只有在提供了 DatabaseGateService 时才添加数据库 Sink
|
||||||
|
if (_dbGateService != null)
|
||||||
|
{
|
||||||
|
loggerConfiguration.WriteTo.Sink(
|
||||||
|
new DatabaseLogSink(
|
||||||
|
_dbGateService,
|
||||||
|
LogEventLevel.Error
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger = loggerConfiguration.CreateLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RefreshLogger()
|
public static void RefreshLogger()
|
||||||
|
@ -71,7 +88,7 @@
|
||||||
{
|
{
|
||||||
Log.CloseAndFlush();
|
Log.CloseAndFlush();
|
||||||
}
|
}
|
||||||
CreateLogger();
|
CreateLogger(_dbGateService); // 使用保存的实例
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetIpAddress()
|
private static string GetIpAddress()
|
||||||
|
|
|
@ -6,6 +6,7 @@ using MySql.Data.MySqlClient;
|
||||||
using iFileProxy.Models;
|
using iFileProxy.Models;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace iFileProxy.Services
|
namespace iFileProxy.Services
|
||||||
{
|
{
|
||||||
|
@ -65,6 +66,20 @@ namespace iFileProxy.Services
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
private const string CREATE_SYSTEM_LOGS_TABLE_SQL = """
|
||||||
|
CREATE TABLE IF NOT EXISTS `t_system_logs` (
|
||||||
|
`log_id` varchar(36) NOT NULL,
|
||||||
|
`level` varchar(20) NOT NULL,
|
||||||
|
`message` text NOT NULL,
|
||||||
|
`exception` text,
|
||||||
|
`properties` json,
|
||||||
|
`timestamp` datetime NOT NULL,
|
||||||
|
PRIMARY KEY (`log_id`),
|
||||||
|
KEY `idx_timestamp` (`timestamp`),
|
||||||
|
KEY `idx_level` (`level`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
""";
|
||||||
|
|
||||||
public DatabaseGateService(AppConfig appConfig)
|
public DatabaseGateService(AppConfig appConfig)
|
||||||
{
|
{
|
||||||
_logger.Information("Initializing DatabaseGateService...");
|
_logger.Information("Initializing DatabaseGateService...");
|
||||||
|
@ -966,5 +981,89 @@ namespace iFileProxy.Services
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CreateSystemLogAsync(SystemLog log)
|
||||||
|
{
|
||||||
|
var sql = @"INSERT INTO t_system_logs
|
||||||
|
(log_id, level, message, exception, properties, timestamp)
|
||||||
|
VALUES
|
||||||
|
(@logId, @level, @message, @exception, @properties, @timestamp)";
|
||||||
|
|
||||||
|
var parameters = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "@logId", log.LogId },
|
||||||
|
{ "@level", log.Level },
|
||||||
|
{ "@message", log.Message },
|
||||||
|
{ "@exception", log.Exception },
|
||||||
|
{ "@properties", JsonConvert.SerializeObject(log.Properties) },
|
||||||
|
{ "@timestamp", log.Timestamp }
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await ExecuteNonQueryAsync(sql, parameters);
|
||||||
|
return result > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PagedResult<SystemLog>> GetPagedSystemLogsAsync(
|
||||||
|
int page,
|
||||||
|
int pageSize,
|
||||||
|
LogEventLevel? level = null,
|
||||||
|
string? keyword = null,
|
||||||
|
DateTime? startTime = null,
|
||||||
|
DateTime? endTime = null)
|
||||||
|
{
|
||||||
|
// 构建基础SQL
|
||||||
|
var sql = new StringBuilder("SELECT * FROM t_system_logs WHERE 1=1");
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// 添加日志级别过滤
|
||||||
|
if (level.HasValue)
|
||||||
|
{
|
||||||
|
sql.Append(" AND level = @level");
|
||||||
|
parameters.Add("@level", level.Value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加关键词搜索
|
||||||
|
if (!string.IsNullOrEmpty(keyword))
|
||||||
|
{
|
||||||
|
sql.Append(" AND (message LIKE @keyword OR exception LIKE @keyword)");
|
||||||
|
parameters.Add("@keyword", $"%{keyword}%");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加时间范围过滤
|
||||||
|
if (startTime.HasValue)
|
||||||
|
{
|
||||||
|
sql.Append(" AND timestamp >= @startTime");
|
||||||
|
parameters.Add("@startTime", startTime.Value);
|
||||||
|
}
|
||||||
|
if (endTime.HasValue)
|
||||||
|
{
|
||||||
|
sql.Append(" AND timestamp <= @endTime");
|
||||||
|
parameters.Add("@endTime", endTime.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加分页和排序
|
||||||
|
sql.Append(" ORDER BY timestamp DESC LIMIT @offset, @limit");
|
||||||
|
parameters.Add("@offset", (page - 1) * pageSize);
|
||||||
|
parameters.Add("@limit", pageSize);
|
||||||
|
|
||||||
|
// 获取总记录数
|
||||||
|
var countSql = "SELECT COUNT(*) FROM t_system_logs WHERE 1=1" +
|
||||||
|
(level.HasValue ? " AND level = @level" : "") +
|
||||||
|
(!string.IsNullOrEmpty(keyword) ? " AND (message LIKE @keyword OR exception LIKE @keyword)" : "") +
|
||||||
|
(startTime.HasValue ? " AND timestamp >= @startTime" : "") +
|
||||||
|
(endTime.HasValue ? " AND timestamp <= @endTime" : "");
|
||||||
|
|
||||||
|
var totalCount = await ExecuteScalarAsync<long>(countSql, parameters);
|
||||||
|
var logs = await ExecuteQueryAsync<SystemLog>(sql.ToString(), parameters);
|
||||||
|
|
||||||
|
return new PagedResult<SystemLog>
|
||||||
|
{
|
||||||
|
Total = totalCount,
|
||||||
|
PageSize = pageSize,
|
||||||
|
CurrentPage = page,
|
||||||
|
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize),
|
||||||
|
Data = logs
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
46
src/Sinks/DatabaseLogSink.cs
Normal file
46
src/Sinks/DatabaseLogSink.cs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using iFileProxy.Services;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace iFileProxy.Sinks
|
||||||
|
{
|
||||||
|
public class DatabaseLogSink : ILogEventSink
|
||||||
|
{
|
||||||
|
private readonly DatabaseGateService _dbGateService;
|
||||||
|
private readonly LogEventLevel _restrictedToMinimumLevel;
|
||||||
|
|
||||||
|
public DatabaseLogSink(DatabaseGateService dbGateService, LogEventLevel restrictedToMinimumLevel)
|
||||||
|
{
|
||||||
|
_dbGateService = dbGateService;
|
||||||
|
_restrictedToMinimumLevel = restrictedToMinimumLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Emit(LogEvent logEvent)
|
||||||
|
{
|
||||||
|
if (logEvent.Level < _restrictedToMinimumLevel) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var log = new SystemLog
|
||||||
|
{
|
||||||
|
Level = logEvent.Level.ToString(),
|
||||||
|
Message = logEvent.RenderMessage(),
|
||||||
|
Exception = logEvent.Exception?.ToString(),
|
||||||
|
Properties = JsonConvert.SerializeObject(logEvent.Properties.ToDictionary(
|
||||||
|
kvp => kvp.Key,
|
||||||
|
kvp => kvp.Value.ToString()
|
||||||
|
)),
|
||||||
|
Timestamp = logEvent.Timestamp.DateTime
|
||||||
|
};
|
||||||
|
|
||||||
|
// 异步写入数据库
|
||||||
|
Task.Run(async () => await _dbGateService.CreateSystemLogAsync(log));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 记录日志失败时不抛出异常,避免影响应用程序运行
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue