Skip to content

Commit

Permalink
Fix/improve log streaming from agent.
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbound committed Jul 27, 2023
1 parent d0e6bce commit afc4f6e
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 127 deletions.
9 changes: 6 additions & 3 deletions Agent/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Microsoft.Extensions.Hosting;
using System.Linq;
using Microsoft.Win32;
using System.Reflection;

namespace Remotely.Agent;

Expand Down Expand Up @@ -47,7 +48,8 @@ public static async Task Main(string[] args)
catch (Exception ex)
{
var version = AppVersionHelper.GetAppVersion();
var logger = new FileLogger("Remotely_Agent", version, "Main");
var componentName = Assembly.GetExecutingAssembly().GetName().Name;
var logger = new FileLogger($"{componentName}", version, "Main");
logger.LogError(ex, "Error during agent startup.");
throw;
}
Expand Down Expand Up @@ -86,10 +88,10 @@ private static void RegisterServices(IServiceCollection services)
{
builder.AddConsole().AddDebug();
var version = AppVersionHelper.GetAppVersion();
builder.AddProvider(new FileLoggerProvider("Remotely_Agent", version));
var componentName = Assembly.GetExecutingAssembly().GetName().Name;
builder.AddProvider(new FileLoggerProvider($"{componentName}", version));
});

// TODO: All these should be registered as interfaces.
services.AddSingleton<IAgentHubConnection, AgentHubConnection>();
services.AddSingleton<ICpuUtilizationSampler, CpuUtilizationSampler>();
services.AddSingleton<IWakeOnLanService, WakeOnLanService>();
Expand All @@ -102,6 +104,7 @@ private static void RegisterServices(IServiceCollection services)
services.AddScoped<IScriptExecutor, ScriptExecutor>();
services.AddScoped<IProcessInvoker, ProcessInvoker>();
services.AddScoped<IUpdateDownloader, UpdateDownloader>();
services.AddSingleton<IFileLogsManager, FileLogsManager>();

if (OperatingSystem.IsWindows())
{
Expand Down
65 changes: 18 additions & 47 deletions Agent/Services/AgentHubConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class AgentHubConnection : IAgentHubConnection, IDisposable
private readonly IHttpClientFactory _httpFactory;
private readonly IWakeOnLanService _wakeOnLanService;
private readonly ILogger<AgentHubConnection> _logger;
private readonly IEnumerable<ILoggerProvider> _loggerProviders;
private readonly IFileLogsManager _fileLogsManager;
private readonly IScriptExecutor _scriptExecutor;
private readonly IUninstaller _uninstaller;
private readonly IUpdater _updater;
Expand All @@ -48,7 +48,6 @@ public class AgentHubConnection : IAgentHubConnection, IDisposable
private HubConnection? _hubConnection;
private Timer? _heartbeatTimer;
private bool _isServerVerified;
private FileLogger? _fileLogger;

public AgentHubConnection(
IConfigService configService,
Expand All @@ -60,7 +59,7 @@ public AgentHubConnection(
IDeviceInformationService deviceInfoService,
IHttpClientFactory httpFactory,
IWakeOnLanService wakeOnLanService,
IEnumerable<ILoggerProvider> loggerProviders,
IFileLogsManager fileLogsManager,
ILogger<AgentHubConnection> logger)
{
_configService = configService;
Expand All @@ -73,7 +72,7 @@ public AgentHubConnection(
_httpFactory = httpFactory;
_wakeOnLanService = wakeOnLanService;
_logger = logger;
_loggerProviders = loggerProviders;
_fileLogsManager = fileLogsManager;
}

public bool IsConnected => _hubConnection?.State == HubConnectionState.Connected;
Expand Down Expand Up @@ -303,14 +302,7 @@ private void RegisterMessageHandlers()

_hubConnection.On("DeleteLogs", () =>
{
if (TryGetFileLogger(out var fileLogger))
{
fileLogger.DeleteLogs();
}
if (_fileLogger is FileLogger logger)
{
logger.DeleteLogs();
}
_fileLogsManager.DeleteLogs();
});


Expand Down Expand Up @@ -373,26 +365,24 @@ private void RegisterMessageHandlers()

_hubConnection.On("GetLogs", async (string senderConnectionId) =>
{
if (_fileLogger is not FileLogger logger)
{
await _hubConnection.InvokeAsync("SendLogs", "Logger is not of expected type.", senderConnectionId).ConfigureAwait(false);
return;
}

var logBytes = await logger.ReadAllBytes();

if (!logBytes.Any())
try
{
var message = "There are no log entries written.";
if (!await _fileLogsManager.AnyLogsExist())
{
var message = "There are no log entries written.";
await _hubConnection.InvokeAsync("SendLogs", message, senderConnectionId).ConfigureAwait(false);
return;
}

await _hubConnection.InvokeAsync("SendLogs", message, senderConnectionId).ConfigureAwait(false);
return;
await foreach (var chunk in _fileLogsManager.ReadAllBytes())
{
var lines = Encoding.UTF8.GetString(chunk);
await _hubConnection.InvokeAsync("SendLogs", lines, senderConnectionId).ConfigureAwait(false);
}
}

for (var i = 0; i < logBytes.Length; i += 50_000)
catch (Exception ex)
{
var chunk = Encoding.UTF8.GetString(logBytes.Skip(i).Take(50_000).ToArray());
await _hubConnection.InvokeAsync("SendLogs", chunk, senderConnectionId).ConfigureAwait(false);
_logger.LogError(ex, "Error while retrieving logs.");
}
});

Expand Down Expand Up @@ -544,25 +534,6 @@ private void RegisterMessageHandlers()
});
}

private bool TryGetFileLogger([NotNullWhen(true)] out FileLogger? fileLogger)
{
if (_fileLogger is null)
{
var logger = _loggerProviders
.OfType<FileLoggerProvider>()
.FirstOrDefault()
?.CreateLogger(nameof(AgentHubConnection));

if (logger is FileLogger loggerImpl)
{
_fileLogger = loggerImpl;
}
}

fileLogger = _fileLogger;
return fileLogger is not null;
}

private async Task<bool> VerifyServer()
{
if (_connectionInfo is null || _hubConnection is null)
Expand Down
85 changes: 85 additions & 0 deletions Agent/Services/FileLogsManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Remotely.Shared.Services;

public interface IFileLogsManager
{
Task<bool> AnyLogsExist();
Task DeleteLogs();
IAsyncEnumerable<byte[]> ReadAllBytes();
}

public class FileLogsManager : IFileLogsManager
{
public async Task<bool> AnyLogsExist()
{
using var logLock = await FileLoggerDefaults.AcquireLock();

var componentName = Assembly.GetExecutingAssembly().GetName().Name;
var directory = Path.Combine(FileLoggerDefaults.LogsFolderPath, $"{componentName}");

if (Directory.Exists(directory))
{
foreach (var file in Directory.GetFiles(directory))
{
if (new FileInfo(file).Length > 0)
{
return true;
}
}
}
return false;
}

public async Task DeleteLogs()
{
using var logLock = await FileLoggerDefaults.AcquireLock();

var componentName = Assembly.GetExecutingAssembly().GetName().Name;
var directory = Path.Combine(FileLoggerDefaults.LogsFolderPath, $"{componentName}");

if (Directory.Exists(directory))
{
foreach (var file in Directory.GetFiles(directory))
{
try
{
File.Delete(file);
}
catch { }
}
}
}

public async IAsyncEnumerable<byte[]> ReadAllBytes()
{
using var logLock = await FileLoggerDefaults.AcquireLock();

var componentName = Assembly.GetExecutingAssembly().GetName().Name;
var directory = Path.Combine(FileLoggerDefaults.LogsFolderPath, $"{componentName}");

if (!Directory.Exists(directory))
{
yield break;
}

var files = Directory
.GetFiles(directory)
.OrderBy(File.GetCreationTime);

foreach (var file in files)
{
foreach (var chunk in File.ReadAllBytes(file).Chunk(50_000))
{
yield return File.ReadAllBytes(file);
}
}
}
}
82 changes: 5 additions & 77 deletions Shared/Services/FileLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace Remotely.Shared.Services;
public class FileLogger : ILogger
{
private static readonly ConcurrentStack<string> _scopeStack = new();
private static readonly SemaphoreSlim _writeLock = new(1, 1);
private readonly string _categoryName;
private readonly string _componentName;
private readonly string _componentVersion;
Expand All @@ -27,62 +26,15 @@ public FileLogger(string componentName, string componentVersion, string category
_categoryName = categoryName;
}

private static string LogsFolderPath
{
get
{
if (OperatingSystem.IsWindows())
{
var logsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"Remotely",
"Logs");

if (EnvironmentHelper.IsDebug)
{
logsPath += "_Debug";
}
return logsPath;
}

if (OperatingSystem.IsLinux())
{
if (EnvironmentHelper.IsDebug)
{
return "/var/log/remotely_debug";
}
return "/var/log/remotely";
}

throw new PlatformNotSupportedException();
}
}
private string LogPath => Path.Combine(LogsFolderPath, _componentName, $"LogFile_{DateTime.Now:yyyy-MM-dd}.log");
private string LogPath => FileLoggerDefaults.GetLogPath(_componentName);

public IDisposable? BeginScope<TState>(TState state)
where TState : notnull
public IDisposable? BeginScope<TState>(TState state)
where TState : notnull
{
_scopeStack.Push($"{state}");
return new NoopDisposable();
}

public void DeleteLogs()
{
try
{
_writeLock.Wait();

if (File.Exists(LogPath))
{
File.Delete(LogPath);
}
}
catch { }
finally
{
_writeLock.Release();
}
}

public bool IsEnabled(LogLevel logLevel)
{
Expand All @@ -93,10 +45,9 @@ public bool IsEnabled(LogLevel logLevel)
_ => false,
};
}

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
public async void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
_writeLock.Wait();
using var logLock = await FileLoggerDefaults.AcquireLock();

try
{
Expand All @@ -110,32 +61,9 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
{
Console.WriteLine($"Error writing log entry: {ex.Message}");
}
finally
{
_writeLock.Release();
}
}

public async Task<byte[]> ReadAllBytes()
{
try
{
_writeLock.Wait();

CheckLogFileExists();

return await File.ReadAllBytesAsync(LogPath);
}
catch (Exception ex)
{
this.LogError(ex, "Error while reading all bytes from logs.");
return Array.Empty<byte>();
}
finally
{
_writeLock.Release();
}
}

private void CheckLogFileExists()
{
Expand Down
Loading

0 comments on commit afc4f6e

Please sign in to comment.