150 lines
4.7 KiB
C#
150 lines
4.7 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Options;
|
|
using OTSSignsOrchestrator.Configuration;
|
|
using OTSSignsOrchestrator.Data;
|
|
|
|
namespace OTSSignsOrchestrator.API;
|
|
|
|
[ApiController]
|
|
[Route("api/admin/[controller]")]
|
|
[Authorize(Roles = AppConstants.AdminRole)]
|
|
public class LogsController : ControllerBase
|
|
{
|
|
private readonly FileLoggingOptions _loggingOptions;
|
|
private readonly IWebHostEnvironment _env;
|
|
private readonly ILogger<LogsController> _logger;
|
|
private readonly XiboContext _db;
|
|
|
|
public LogsController(
|
|
IOptions<FileLoggingOptions> loggingOptions,
|
|
IWebHostEnvironment env,
|
|
ILogger<LogsController> logger,
|
|
XiboContext db)
|
|
{
|
|
_loggingOptions = loggingOptions.Value;
|
|
_env = env;
|
|
_logger = logger;
|
|
_db = db;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tail recent log lines with optional filters.
|
|
/// </summary>
|
|
[HttpGet]
|
|
public IActionResult GetLogs([FromQuery] int lines = 100, [FromQuery] string? filter = null, [FromQuery] string? level = null)
|
|
{
|
|
var logPath = _loggingOptions.Path;
|
|
if (!Path.IsPathRooted(logPath))
|
|
logPath = Path.Combine(_env.ContentRootPath, logPath);
|
|
|
|
if (!Directory.Exists(logPath))
|
|
return Ok(new { lines = Array.Empty<string>(), path = logPath, message = "Log directory not found." });
|
|
|
|
// Find latest log file
|
|
var logFiles = Directory.GetFiles(logPath, "app-*.log")
|
|
.OrderByDescending(f => f)
|
|
.ToList();
|
|
|
|
if (logFiles.Count == 0)
|
|
return Ok(new { lines = Array.Empty<string>(), path = logPath, message = "No log files found." });
|
|
|
|
var latestFile = logFiles[0];
|
|
var allLines = ReadLastLines(latestFile, lines * 2); // Read extra for filtering
|
|
|
|
// Apply filters
|
|
if (!string.IsNullOrWhiteSpace(level))
|
|
{
|
|
allLines = allLines.Where(l =>
|
|
l.Contains($"[{level.ToUpperInvariant().PadRight(3)}]", StringComparison.OrdinalIgnoreCase) ||
|
|
l.Contains($"[{level}]", StringComparison.OrdinalIgnoreCase)).ToList();
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(filter))
|
|
{
|
|
allLines = allLines.Where(l =>
|
|
l.Contains(filter, StringComparison.OrdinalIgnoreCase)).ToList();
|
|
}
|
|
|
|
var result = allLines.TakeLast(lines).ToList();
|
|
var fileInfo = new FileInfo(latestFile);
|
|
|
|
return Ok(new
|
|
{
|
|
lines = result,
|
|
path = latestFile,
|
|
sizeBytes = fileInfo.Length,
|
|
lastModified = fileInfo.LastWriteTimeUtc
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download the latest log file.
|
|
/// </summary>
|
|
[HttpGet("download")]
|
|
public IActionResult DownloadLog()
|
|
{
|
|
var logPath = _loggingOptions.Path;
|
|
if (!Path.IsPathRooted(logPath))
|
|
logPath = Path.Combine(_env.ContentRootPath, logPath);
|
|
|
|
var logFiles = Directory.GetFiles(logPath, "app-*.log")
|
|
.OrderByDescending(f => f)
|
|
.ToList();
|
|
|
|
if (logFiles.Count == 0)
|
|
return NotFound("No log files found.");
|
|
|
|
var latestFile = logFiles[0];
|
|
var stream = new FileStream(latestFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
return File(stream, "text/plain", Path.GetFileName(latestFile));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get recent operation logs from the database.
|
|
/// </summary>
|
|
[HttpGet("operations")]
|
|
public async Task<IActionResult> GetOperations([FromQuery] int count = 50)
|
|
{
|
|
var ops = await _db.OperationLogs
|
|
.Include(o => o.Instance)
|
|
.OrderByDescending(o => o.Timestamp)
|
|
.Take(Math.Min(count, 200))
|
|
.Select(o => new
|
|
{
|
|
o.Id,
|
|
o.Operation,
|
|
o.Status,
|
|
o.Message,
|
|
o.Timestamp,
|
|
o.DurationMs,
|
|
o.UserId,
|
|
StackName = o.Instance != null ? o.Instance.StackName : null
|
|
})
|
|
.ToListAsync();
|
|
|
|
return Ok(ops);
|
|
}
|
|
|
|
private static List<string> ReadLastLines(string filePath, int lineCount)
|
|
{
|
|
try
|
|
{
|
|
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
using var reader = new StreamReader(stream);
|
|
var lines = new List<string>();
|
|
string? line;
|
|
while ((line = reader.ReadLine()) != null)
|
|
{
|
|
lines.Add(line);
|
|
}
|
|
return lines.TakeLast(lineCount).ToList();
|
|
}
|
|
catch
|
|
{
|
|
return new List<string>();
|
|
}
|
|
}
|
|
}
|