From daf94de8e087c4cdab437c28b7dcd26f3525c926 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Mon, 24 Aug 2026 16:37:09 +0200 Subject: [PATCH] feat(core): add log providers for journal and file sources --- src/Vantage.Core/Logs/MockLogProvider.cs | 50 ++++++++++++++++++++++++ src/Vantage.Core/Logs/SshLogProvider.cs | 25 ++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/Vantage.Core/Logs/MockLogProvider.cs create mode 100644 src/Vantage.Core/Logs/SshLogProvider.cs diff --git a/src/Vantage.Core/Logs/MockLogProvider.cs b/src/Vantage.Core/Logs/MockLogProvider.cs new file mode 100644 index 0000000..5340542 --- /dev/null +++ b/src/Vantage.Core/Logs/MockLogProvider.cs @@ -0,0 +1,50 @@ +using Vantage.Core.Models; + +namespace Vantage.Core.Logs; + +public sealed class MockLogProvider : ILogProvider +{ + private static readonly string[] Templates = + { + "Started session {0} of user foksiu.", + "Accepted publickey for foksiu from 10.10.0.4 port {0}", + "GET /api/status 200 {0}ms", + "connection pool exhausted, retrying in {0}ms", + "flushed {0} pending writes to disk", + "renewed certificate, valid for {0} days", + "healthcheck ok, latency {0}ms" + }; + + private readonly Func clock; + + public MockLogProvider(Func? clock = null) + { + this.clock = clock ?? (() => DateTimeOffset.UtcNow); + } + + public Task> ReadAsync( + HostConfig host, + LogSource source, + LogQuery query, + CancellationToken cancellationToken) + { + var random = new Random(source.Target.Length * 31 + query.Lines); + var unit = source.Kind == LogSourceKind.JournalUnit ? source.Target : "file"; + var now = clock(); + var count = Math.Clamp(query.Lines, 1, 200); + + var lines = new List(); + for (var i = count; i > 0; i--) + { + var timestamp = now.AddSeconds(-i * 3); + var message = string.Format(Templates[random.Next(Templates.Length)], random.Next(1, 4000)); + var raw = $"{timestamp:yyyy-MM-ddTHH:mm:sszzz} {host.Name} {unit}[{1000 + i}]: {message}"; + lines.Add(new LogLine(timestamp, unit, message, raw)); + } + + if (!string.IsNullOrWhiteSpace(query.Filter)) + lines = lines.Where(l => l.Raw.Contains(query.Filter!, StringComparison.OrdinalIgnoreCase)).ToList(); + + return Task.FromResult>(lines); + } +} diff --git a/src/Vantage.Core/Logs/SshLogProvider.cs b/src/Vantage.Core/Logs/SshLogProvider.cs new file mode 100644 index 0000000..602c993 --- /dev/null +++ b/src/Vantage.Core/Logs/SshLogProvider.cs @@ -0,0 +1,25 @@ +using Vantage.Core.Models; +using Vantage.Core.Ssh; + +namespace Vantage.Core.Logs; + +public sealed class SshLogProvider : ILogProvider +{ + private readonly ISshCommandRunner runner; + + public SshLogProvider(ISshCommandRunner runner) + { + this.runner = runner; + } + + public async Task> ReadAsync( + HostConfig host, + LogSource source, + LogQuery query, + CancellationToken cancellationToken) + { + var command = LogCommand.Build(source, query); + var output = await runner.RunAsync(host, command, cancellationToken).ConfigureAwait(false); + return LogLineParser.Parse(output); + } +}