Files
EonaCat.Logger.LogServer/EonaCat.Logger.LogServer/Services/LogService.cs
2026-01-08 19:03:40 +01:00

193 lines
6.8 KiB
C#

using EonaCat.Logger.Server.Data;
using EonaCat.Logger.Server.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using System.Text.Json;
namespace EonaCat.Logger.Server.Services;
public class LogService : ILogService
{
private readonly LoggerDbContext _context;
private readonly IMemoryCache _cache;
public LogService(LoggerDbContext context, IMemoryCache cache)
{
_context = context;
_cache = cache;
}
public async Task AddLogsAsync(List<LogEntry> entries)
{
await _context.LogEntries.AddRangeAsync(entries);
await _context.SaveChangesAsync();
_cache.Remove("stats");
}
public async Task<LogEntry?> GetLogByIdAsync(string id)
{
return await _context.LogEntries.FindAsync(id);
}
public async Task<PagedResult<LogEntry>> GetLogsAsync(LogQueryParams queryParams)
{
var query = _context.LogEntries.AsQueryable();
if (!string.IsNullOrEmpty(queryParams.Search))
{
var search = queryParams.Search.ToLower();
query = query.Where(l =>
l.Message.ToLower().Contains(search) ||
(l.Exception != null && l.Exception.ToLower().Contains(search)) ||
l.Category.ToLower().Contains(search) ||
l.ApplicationName.ToLower().Contains(search));
}
if (!string.IsNullOrEmpty(queryParams.Application))
query = query.Where(l => l.ApplicationName == queryParams.Application);
if (!string.IsNullOrEmpty(queryParams.Environment))
query = query.Where(l => l.Environment == queryParams.Environment);
if (!string.IsNullOrEmpty(queryParams.Category))
query = query.Where(l => l.Category == queryParams.Category);
if (queryParams.Level.HasValue)
query = query.Where(l => l.Level == queryParams.Level.Value);
if (queryParams.StartDate.HasValue)
query = query.Where(l => l.Timestamp >= queryParams.StartDate.Value);
if (queryParams.EndDate.HasValue)
query = query.Where(l => l.Timestamp <= queryParams.EndDate.Value);
if (!string.IsNullOrEmpty(queryParams.UserId))
query = query.Where(l => l.UserId == queryParams.UserId);
if (!string.IsNullOrEmpty(queryParams.CorrelationId))
query = query.Where(l => l.CorrelationId == queryParams.CorrelationId);
var totalCount = await query.CountAsync();
query = queryParams.SortOrder?.ToLower() == "asc"
? query.OrderBy(l => l.Timestamp)
: query.OrderByDescending(l => l.Timestamp);
var items = await query
.Skip((queryParams.Page - 1) * queryParams.PageSize)
.Take(queryParams.PageSize)
.ToListAsync();
return new PagedResult<LogEntry>
{
Items = items,
TotalCount = totalCount,
Page = queryParams.Page,
PageSize = queryParams.PageSize
};
}
public async Task<Dictionary<string, object>> GetStatsAsync(string? app, string? env)
{
var cacheKey = $"stats_{app}_{env}";
if (_cache.TryGetValue(cacheKey, out Dictionary<string, object>? cachedStats))
return cachedStats!;
var query = _context.LogEntries.AsQueryable();
if (!string.IsNullOrEmpty(app))
query = query.Where(l => l.ApplicationName == app);
if (!string.IsNullOrEmpty(env))
query = query.Where(l => l.Environment == env);
var last24Hours = DateTime.UtcNow.AddHours(-24);
var lastHour = DateTime.UtcNow.AddHours(-1);
var stats = new Dictionary<string, object>
{
["totalLogs"] = await query.CountAsync(),
["last24Hours"] = await query.Where(l => l.Timestamp >= last24Hours).CountAsync(),
["lastHour"] = await query.Where(l => l.Timestamp >= lastHour).CountAsync(),
["errorCount"] = await query.Where(l => l.Level >= 4).CountAsync(),
["warningCount"] = await query.Where(l => l.Level == 3).CountAsync(),
["criticalCount"] = await query.Where(l => l.Level == 5).CountAsync(),
["securityCount"] = await query.Where(l => l.Level == 6).CountAsync(),
["applications"] = await _context.LogEntries.Select(l => l.ApplicationName).Distinct().CountAsync(),
["byLevel"] = await query
.GroupBy(l => l.Level)
.Select(g => new { Level = g.Key, Count = g.Count() })
.ToListAsync(),
["topErrors"] = await query
.Where(l => l.Level >= 4 && l.Timestamp >= last24Hours)
.GroupBy(l => l.Message)
.Select(g => new { Message = g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count)
.Take(5)
.ToListAsync(),
["topApplications"] = await query
.Where(l => l.Timestamp >= last24Hours)
.GroupBy(l => l.ApplicationName)
.Select(g => new { Application = g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count)
.Take(5)
.ToListAsync()
};
_cache.Set(cacheKey, stats, TimeSpan.FromSeconds(30));
return stats;
}
public async Task<bool> ValidateApiKeyAsync(string apiKey)
{
return await _context.Applications.AnyAsync(a => a.ApiKey == apiKey && a.IsActive);
}
public async Task<List<string>> GetApplicationsAsync()
{
return await _context.LogEntries
.Select(l => l.ApplicationName)
.Distinct()
.OrderBy(a => a)
.ToListAsync();
}
public async Task<List<string>> GetEnvironmentsAsync()
{
return await _context.LogEntries
.Select(l => l.Environment)
.Distinct()
.OrderBy(e => e)
.ToListAsync();
}
public async Task<List<TrendPoint>> GetLogTrendAsync(int hours)
{
var startTime = DateTime.UtcNow.AddHours(-hours);
var interval = hours > 24 ? 60 : hours > 6 ? 30 : 15;
var logs = await _context.LogEntries
.Where(l => l.Timestamp >= startTime)
.ToListAsync();
var trend = logs
.GroupBy(l => new DateTime(
l.Timestamp.Year,
l.Timestamp.Month,
l.Timestamp.Day,
l.Timestamp.Hour,
(l.Timestamp.Minute / interval) * interval,
0))
.Select(g => new TrendPoint
{
Timestamp = g.Key,
Count = g.Count(),
ByLevel = g.GroupBy(l => l.Level)
.ToDictionary(lg => lg.Key, lg => lg.Count())
})
.OrderBy(t => t.Timestamp)
.ToList();
return trend;
}
}