添加调试页

This commit is contained in:
root 2024-12-02 21:39:01 +08:00
parent d34b491c11
commit d314597e8d
6 changed files with 242 additions and 5 deletions

View file

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
using Serilog;
using System.Text.Json;
using iFileProxy.Attributes;
using Serilog.Events;
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
View 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; }
}

View file

@ -127,6 +127,9 @@ namespace iFileProxy
app.MapControllers();
var dbGateService = app.Services.GetRequiredService<DatabaseGateService>();
SerilogConfig.CreateLogger(dbGateService);
app.Run();
}
}

View file

@ -3,11 +3,16 @@
using Serilog;
using Serilog.Events;
using System.Net;
using iFileProxy.Sinks;
using iFileProxy.Services;
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 appName = AppDomain.CurrentDomain.FriendlyName;
@ -17,7 +22,7 @@
Directory.CreateDirectory(baseLogPath);
}
Log.Logger = new LoggerConfiguration()
var loggerConfiguration = new LoggerConfiguration()
#if RELEASE
.MinimumLevel.Information()
#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}",
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 1073741824))
.Enrich.WithProperty("node_ip", GetIpAddress())
.CreateLogger();
.Enrich.WithProperty("node_ip", GetIpAddress());
// 只有在提供了 DatabaseGateService 时才添加数据库 Sink
if (_dbGateService != null)
{
loggerConfiguration.WriteTo.Sink(
new DatabaseLogSink(
_dbGateService,
LogEventLevel.Error
)
);
}
Log.Logger = loggerConfiguration.CreateLogger();
}
public static void RefreshLogger()
@ -71,7 +88,7 @@
{
Log.CloseAndFlush();
}
CreateLogger();
CreateLogger(_dbGateService); // 使用保存的实例
}
private static string GetIpAddress()

View file

@ -6,6 +6,7 @@ using MySql.Data.MySqlClient;
using iFileProxy.Models;
using Newtonsoft.Json;
using System.Text;
using Serilog.Events;
namespace iFileProxy.Services
{
@ -65,6 +66,20 @@ namespace iFileProxy.Services
) 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)
{
_logger.Information("Initializing DatabaseGateService...");
@ -966,5 +981,89 @@ namespace iFileProxy.Services
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
};
}
}
}

View 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
{
// 记录日志失败时不抛出异常,避免影响应用程序运行
}
}
}
}