diff --git a/src/Controllers/ManagementController.cs b/src/Controllers/ManagementController.cs
index 81cd778..3ebbd49 100644
--- a/src/Controllers/ManagementController.cs
+++ b/src/Controllers/ManagementController.cs
@@ -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
});
}
}
+
+ ///
+ /// 获取系统日志
+ ///
+ [Authorize(UserMask.SuperAdmin)]
+ [HttpGet("GetSystemLogs")]
+ public async Task> 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(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 = "获取系统日志失败"
+ });
+ }
+ }
}
}
diff --git a/src/Models/SystemLog.cs b/src/Models/SystemLog.cs
new file mode 100644
index 0000000..4472ce1
--- /dev/null
+++ b/src/Models/SystemLog.cs
@@ -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; }
+}
\ No newline at end of file
diff --git a/src/Program.cs b/src/Program.cs
index c941411..98dd5ef 100644
--- a/src/Program.cs
+++ b/src/Program.cs
@@ -127,6 +127,9 @@ namespace iFileProxy
app.MapControllers();
+ var dbGateService = app.Services.GetRequiredService();
+ SerilogConfig.CreateLogger(dbGateService);
+
app.Run();
}
}
diff --git a/src/SerilogConfig.cs b/src/SerilogConfig.cs
index 3d0b0af..3cbeeb1 100644
--- a/src/SerilogConfig.cs
+++ b/src/SerilogConfig.cs
@@ -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()
diff --git a/src/Services/DatabaseGateService.cs b/src/Services/DatabaseGateService.cs
index c803adc..33ba47b 100644
--- a/src/Services/DatabaseGateService.cs
+++ b/src/Services/DatabaseGateService.cs
@@ -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 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
+ {
+ { "@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> 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();
+
+ // 添加日志级别过滤
+ 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(countSql, parameters);
+ var logs = await ExecuteQueryAsync(sql.ToString(), parameters);
+
+ return new PagedResult
+ {
+ Total = totalCount,
+ PageSize = pageSize,
+ CurrentPage = page,
+ TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize),
+ Data = logs
+ };
+ }
}
}
diff --git a/src/Sinks/DatabaseLogSink.cs b/src/Sinks/DatabaseLogSink.cs
new file mode 100644
index 0000000..63c5526
--- /dev/null
+++ b/src/Sinks/DatabaseLogSink.cs
@@ -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
+ {
+ // 记录日志失败时不抛出异常,避免影响应用程序运行
+ }
+ }
+ }
+}
\ No newline at end of file