Files
ServerManagementUtils/robin/Services/Monitoring/MonitoringService.cs
T
Alexandru-Victor Macocian 0816fdcd50 Small fix
2025-12-02 19:54:58 +01:00

132 lines
4.2 KiB
C#

using System.Extensions;
using System.Extensions.Core;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Robin.Options;
using Robin.Services.Persistence;
namespace Robin.Services.Monitoring;
public sealed class MonitoringService(
PositionPersistenceService persistenceService,
IOptions<MonitorOptions> options,
ILogger<MonitoringService> logger
) : IHostedLifecycleService
{
private readonly PositionPersistenceService persistenceService = persistenceService;
private readonly MonitorOptions options = options.Value;
private readonly CancellationTokenSource cancellationTokenSource = new();
private readonly ILogger<MonitoringService> logger = logger;
private readonly List<Task> watchers = [];
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StartedAsync(CancellationToken cancellationToken) =>
this.MonitorFiles(cancellationToken);
public Task StartingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken) => this.CancelMonitors();
public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task CancelMonitors()
{
this.cancellationTokenSource.Cancel();
return Task.WhenAll(this.watchers);
}
public async Task MonitorFiles(CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
scopedLogger.LogDebug(
"Starting file monitoring service. Parsing provided paths {paths}",
string.Join(", ", this.options.Paths)
);
var expandedPaths = ExpandPaths(this.options.Paths);
scopedLogger.LogInformation(
"Monitoring the following files: {files}",
string.Join(", ", expandedPaths)
);
var cts = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,
this.cancellationTokenSource.Token
);
foreach (var filePath in expandedPaths)
{
this.watchers.Add(Task.Run(() => this.MonitorFile(filePath, cts.Token), cts.Token));
}
}
private async Task MonitorFile(string filePath, CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
if (!File.Exists(filePath))
{
scopedLogger.LogWarning("File does not exist: {filePath}", filePath);
return;
}
scopedLogger.LogInformation("Starting to monitor file: {filePath}", filePath);
var previousPosition = this.persistenceService.GetPosition(filePath);
using var fs = new FileStream(
filePath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite | FileShare.Delete
);
if (fs.Length < previousPosition)
{
previousPosition = 0;
}
if (previousPosition > 0)
{
fs.Seek(previousPosition, SeekOrigin.Begin);
}
using var reader = new StreamReader(fs);
while (
(await reader.ReadLineAsync(cancellationToken)) is string line
&& !line.IsNullOrEmpty()
)
{
scopedLogger.LogDebug("Read line: {line}", line);
//TODO: Publish to IPublishingService
await this.persistenceService.SavePositionAsync(
filePath,
fs.Position,
cancellationToken
);
}
}
private static List<string> ExpandPaths(List<string>? patterns)
{
if (patterns is null || patterns.Count == 0)
{
return [];
}
var results = new List<string>();
foreach (var pattern in patterns)
{
var directory = Path.GetDirectoryName(pattern);
var filePattern = Path.GetFileName(pattern);
if (string.IsNullOrEmpty(directory) || !Directory.Exists(directory))
{
continue;
}
results.AddRange(Directory.GetFiles(directory, filePattern));
}
return results;
}
}