Simplify Program.cs and LoggingService to remove database dependencies
This commit is contained in:
parent
d919516f2d
commit
3f9875cb1a
@ -1,72 +1,38 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using TransmissionRssManager.Core;
|
using TransmissionRssManager.Core;
|
||||||
using TransmissionRssManager.Data;
|
|
||||||
using TransmissionRssManager.Data.Models;
|
|
||||||
using TransmissionRssManager.Data.Repositories;
|
|
||||||
using TransmissionRssManager.Services;
|
using TransmissionRssManager.Services;
|
||||||
using Serilog;
|
|
||||||
using Serilog.Events;
|
|
||||||
|
|
||||||
// Configure Serilog
|
|
||||||
var logsDirectory = Path.Combine(AppContext.BaseDirectory, "logs");
|
|
||||||
Directory.CreateDirectory(logsDirectory);
|
|
||||||
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
|
||||||
.MinimumLevel.Information()
|
|
||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
|
||||||
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
|
|
||||||
.Enrich.FromLogContext()
|
|
||||||
.WriteTo.Console()
|
|
||||||
.WriteTo.File(
|
|
||||||
Path.Combine(logsDirectory, "log-.txt"),
|
|
||||||
rollingInterval: RollingInterval.Day,
|
|
||||||
retainedFileCountLimit: 31,
|
|
||||||
fileSizeLimitBytes: 10 * 1024 * 1024)
|
|
||||||
.CreateLogger();
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add Serilog to the application
|
// Add logging
|
||||||
builder.Host.UseSerilog();
|
builder.Logging.AddConsole();
|
||||||
|
builder.Logging.AddDebug();
|
||||||
|
|
||||||
|
// Create logs directory for file logging
|
||||||
|
var logsDirectory = Path.Combine(AppContext.BaseDirectory, "logs");
|
||||||
|
Directory.CreateDirectory(logsDirectory);
|
||||||
|
|
||||||
// Add services to the container
|
// Add services to the container
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
// Configure database
|
// Add custom services as singletons for simplicity
|
||||||
var connectionString = builder.Configuration["ConnectionStrings:DefaultConnection"]
|
builder.Services.AddSingleton<IConfigService, ConfigService>();
|
||||||
?? "Host=localhost;Database=torrentmanager;Username=postgres;Password=postgres";
|
builder.Services.AddSingleton<ITransmissionClient, TransmissionClient>();
|
||||||
builder.Services.AddDbContext<TorrentManagerContext>(options =>
|
builder.Services.AddSingleton<IRssFeedManager, RssFeedManager>();
|
||||||
options.UseNpgsql(connectionString));
|
builder.Services.AddSingleton<IPostProcessor, PostProcessor>();
|
||||||
|
builder.Services.AddSingleton<IMetricsService, MetricsService>();
|
||||||
|
builder.Services.AddSingleton<ILoggingService, LoggingService>();
|
||||||
|
|
||||||
// Register repositories
|
// Add background services
|
||||||
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
|
||||||
builder.Services.AddScoped<ITorrentRepository, TorrentRepository>();
|
|
||||||
builder.Services.AddScoped<DataMigrationService>();
|
|
||||||
|
|
||||||
// Add custom services (now scoped instead of singleton to work with DbContext)
|
|
||||||
builder.Services.AddScoped<IConfigService, ConfigService>();
|
|
||||||
builder.Services.AddScoped<ITransmissionClient, TransmissionClient>();
|
|
||||||
builder.Services.AddScoped<IRssFeedManager, RssFeedManager>();
|
|
||||||
builder.Services.AddScoped<IPostProcessor, PostProcessor>();
|
|
||||||
builder.Services.AddScoped<ILoggingService, LoggingService>();
|
|
||||||
builder.Services.AddScoped<IMetricsService, MetricsService>();
|
|
||||||
builder.Services.AddScoped<ISchedulerService, SchedulerService>();
|
|
||||||
|
|
||||||
// Add background services (these remain singleton)
|
|
||||||
builder.Services.AddHostedService<RssFeedBackgroundService>();
|
builder.Services.AddHostedService<RssFeedBackgroundService>();
|
||||||
builder.Services.AddHostedService<PostProcessingBackgroundService>();
|
builder.Services.AddHostedService<PostProcessingBackgroundService>();
|
||||||
builder.Services.AddHostedService<FeedSchedulerBackgroundService>();
|
|
||||||
// MetricsBackgroundService is not needed with the simplified implementation
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
@ -82,38 +48,12 @@ app.UseRouting();
|
|||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
// Initialize database and run migrations
|
|
||||||
using (var scope = app.Services.CreateScope())
|
|
||||||
{
|
|
||||||
var services = scope.ServiceProvider;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var context = services.GetRequiredService<TorrentManagerContext>();
|
|
||||||
context.Database.Migrate();
|
|
||||||
|
|
||||||
// Run data migration from file-based storage if needed
|
|
||||||
if (!context.UserPreferences.Any())
|
|
||||||
{
|
|
||||||
var migrationService = services.GetRequiredService<DataMigrationService>();
|
|
||||||
await migrationService.MigrateDataAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
|
||||||
logger.LogError(ex, "An error occurred during database initialization or migration");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await app.RunAsync();
|
await app.RunAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Fatal(ex, "Application terminated unexpectedly");
|
var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||||
}
|
logger.LogError(ex, "Application terminated unexpectedly");
|
||||||
finally
|
|
||||||
{
|
|
||||||
Log.CloseAndFlush();
|
|
||||||
}
|
}
|
189
src/Services/LoggingService.cs
Normal file
189
src/Services/LoggingService.cs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using TransmissionRssManager.Core;
|
||||||
|
|
||||||
|
namespace TransmissionRssManager.Services
|
||||||
|
{
|
||||||
|
public interface ILoggingService
|
||||||
|
{
|
||||||
|
void Configure(UserPreferences preferences);
|
||||||
|
Task<List<LogEntry>> GetLogsAsync(LogFilterOptions options);
|
||||||
|
Task ClearLogsAsync(DateTime? olderThan = null);
|
||||||
|
Task<byte[]> ExportLogsAsync(LogFilterOptions options);
|
||||||
|
void Log(LogLevel level, string message, string context = null, Dictionary<string, string> properties = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LogFilterOptions
|
||||||
|
{
|
||||||
|
public string Level { get; set; } = "All";
|
||||||
|
public string Search { get; set; } = "";
|
||||||
|
public DateTime? StartDate { get; set; }
|
||||||
|
public DateTime? EndDate { get; set; }
|
||||||
|
public string Context { get; set; } = "";
|
||||||
|
public int Limit { get; set; } = 100;
|
||||||
|
public int Offset { get; set; } = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LoggingService : ILoggingService
|
||||||
|
{
|
||||||
|
private readonly ILogger<LoggingService> _logger;
|
||||||
|
private readonly string _logFilePath;
|
||||||
|
private readonly object _logLock = new object();
|
||||||
|
private List<LogEntry> _inMemoryLogs = new List<LogEntry>();
|
||||||
|
private readonly int _maxLogEntries = 1000;
|
||||||
|
|
||||||
|
public LoggingService(ILogger<LoggingService> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
// Prepare log directory and file
|
||||||
|
var logsDirectory = Path.Combine(AppContext.BaseDirectory, "logs");
|
||||||
|
Directory.CreateDirectory(logsDirectory);
|
||||||
|
_logFilePath = Path.Combine(logsDirectory, "application_logs.json");
|
||||||
|
|
||||||
|
// Initialize log file if it doesn't exist
|
||||||
|
if (!File.Exists(_logFilePath))
|
||||||
|
{
|
||||||
|
File.WriteAllText(_logFilePath, "[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load existing logs into memory
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = File.ReadAllText(_logFilePath);
|
||||||
|
_inMemoryLogs = JsonSerializer.Deserialize<List<LogEntry>>(json) ?? new List<LogEntry>();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to load logs from file");
|
||||||
|
_inMemoryLogs = new List<LogEntry>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(UserPreferences preferences)
|
||||||
|
{
|
||||||
|
// No-op in simplified version
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<LogEntry>> GetLogsAsync(LogFilterOptions options)
|
||||||
|
{
|
||||||
|
var filteredLogs = _inMemoryLogs.AsEnumerable();
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
if (!string.IsNullOrEmpty(options.Level) && options.Level != "All")
|
||||||
|
{
|
||||||
|
filteredLogs = filteredLogs.Where(l => l.Level == options.Level);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(options.Search))
|
||||||
|
{
|
||||||
|
filteredLogs = filteredLogs.Where(l =>
|
||||||
|
l.Message.Contains(options.Search, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.StartDate.HasValue)
|
||||||
|
{
|
||||||
|
filteredLogs = filteredLogs.Where(l => l.Timestamp >= options.StartDate.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.EndDate.HasValue)
|
||||||
|
{
|
||||||
|
filteredLogs = filteredLogs.Where(l => l.Timestamp <= options.EndDate.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(options.Context))
|
||||||
|
{
|
||||||
|
filteredLogs = filteredLogs.Where(l => l.Context == options.Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort, paginate and return
|
||||||
|
return Task.FromResult(
|
||||||
|
filteredLogs
|
||||||
|
.OrderByDescending(l => l.Timestamp)
|
||||||
|
.Skip(options.Offset)
|
||||||
|
.Take(options.Limit)
|
||||||
|
.ToList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ClearLogsAsync(DateTime? olderThan = null)
|
||||||
|
{
|
||||||
|
lock (_logLock)
|
||||||
|
{
|
||||||
|
if (olderThan.HasValue)
|
||||||
|
{
|
||||||
|
_inMemoryLogs.RemoveAll(l => l.Timestamp < olderThan.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_inMemoryLogs.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<byte[]> ExportLogsAsync(LogFilterOptions options)
|
||||||
|
{
|
||||||
|
var logs = await GetLogsAsync(options);
|
||||||
|
var json = JsonSerializer.Serialize(logs, new JsonSerializerOptions { WriteIndented = true });
|
||||||
|
return Encoding.UTF8.GetBytes(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log(LogLevel level, string message, string context = null, Dictionary<string, string> properties = null)
|
||||||
|
{
|
||||||
|
var levelString = level.ToString();
|
||||||
|
|
||||||
|
// Log to standard logger
|
||||||
|
_logger.Log(level, message);
|
||||||
|
|
||||||
|
// Store in our custom log system
|
||||||
|
var entry = new LogEntry
|
||||||
|
{
|
||||||
|
Id = _inMemoryLogs.Count > 0 ? _inMemoryLogs.Max(l => l.Id) + 1 : 1,
|
||||||
|
Timestamp = DateTime.UtcNow,
|
||||||
|
Level = levelString,
|
||||||
|
Message = message,
|
||||||
|
Context = context ?? "System",
|
||||||
|
Properties = properties != null ? JsonSerializer.Serialize(properties) : "{}"
|
||||||
|
};
|
||||||
|
|
||||||
|
lock (_logLock)
|
||||||
|
{
|
||||||
|
_inMemoryLogs.Add(entry);
|
||||||
|
|
||||||
|
// Keep log size under control
|
||||||
|
if (_inMemoryLogs.Count > _maxLogEntries)
|
||||||
|
{
|
||||||
|
_inMemoryLogs = _inMemoryLogs
|
||||||
|
.OrderByDescending(l => l.Timestamp)
|
||||||
|
.Take(_maxLogEntries)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveLogs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveLogs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(_inMemoryLogs);
|
||||||
|
File.WriteAllText(_logFilePath, json);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to save logs to file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user