Skip to content

chore: add logging to mutagen controller #79

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 1, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions App/Services/MutagenController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
using Coder.Desktop.Vpn.Utilities;
using Grpc.Core;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Serilog;
using DaemonTerminateRequest = Coder.Desktop.MutagenSdk.Proto.Service.Daemon.TerminateRequest;
using MutagenProtocol = Coder.Desktop.MutagenSdk.Proto.Url.Protocol;
using SynchronizationTerminateRequest = Coder.Desktop.MutagenSdk.Proto.Service.Synchronization.TerminateRequest;
using Microsoft.Extensions.Hosting;

namespace Coder.Desktop.App.Services;

Expand Down Expand Up @@ -110,6 +113,8 @@ public sealed class MutagenController : ISyncSessionController
// Protects all private non-readonly class members.
private readonly RaiiSemaphoreSlim _lock = new(1, 1);

private readonly ILogger<MutagenController> _logger;

private readonly CancellationTokenSource _stateUpdateCts = new();
private Task? _stateUpdateTask;

Expand Down Expand Up @@ -139,15 +144,19 @@ public sealed class MutagenController : ISyncSessionController

private string MutagenDaemonLog => Path.Combine(_mutagenDataDirectory, "daemon.log");

public MutagenController(IOptions<MutagenControllerConfig> config)
public MutagenController(IOptions<MutagenControllerConfig> config, ILogger<MutagenController> logger)
{
_mutagenExecutablePath = config.Value.MutagenExecutablePath;
_logger = logger;
}

public MutagenController(string executablePath, string dataDirectory)
{
_mutagenExecutablePath = executablePath;
_mutagenDataDirectory = dataDirectory;
var builder = Host.CreateApplicationBuilder();
builder.Services.AddSerilog();
_logger = (ILogger<MutagenController>)builder.Build().Services.GetService(typeof(ILogger<MutagenController>))!;
}

public event EventHandler<SyncSessionControllerStateModel>? StateChanged;
Expand Down Expand Up @@ -440,9 +449,9 @@ private async Task<MutagenClient> EnsureDaemon(CancellationToken ct)
{
await StopDaemon(cts.Token);
}
catch
catch (Exception stopEx)
{
// ignored
_logger.LogError(stopEx, "failed to stop daemon");
}

ReplaceState(new SyncSessionControllerStateModel
Expand Down Expand Up @@ -494,6 +503,8 @@ private async Task<MutagenClient> StartDaemon(CancellationToken ct)
}
catch (Exception e) when (e is not OperationCanceledException)
{
_logger.LogWarning(e, "failed to start daemon process, attempt {attempt} of {maxAttempts}", attempts,
maxAttempts);
if (attempts == maxAttempts)
throw;
// back off a little and try again.
Expand Down Expand Up @@ -548,8 +559,11 @@ private void StartDaemonProcess()
// https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.environment?view=net-8.0
_daemonProcess.StartInfo.UseShellExecute = false;
_daemonProcess.StartInfo.RedirectStandardError = true;
// TODO: log exited process
// _daemonProcess.Exited += ...
_daemonProcess.EnableRaisingEvents = true;
_daemonProcess.Exited += (object? sender, EventArgs e) =>
{
_logger.LogInformation("mutagen daemon exited with code {exitCode}", _daemonProcess?.ExitCode);
};
if (!_daemonProcess.Start())
throw new InvalidOperationException("Failed to start mutagen daemon process, Start returned false");

Expand All @@ -564,6 +578,7 @@ private void StartDaemonProcess()
/// </summary>
private async Task StopDaemon(CancellationToken ct)
{
_logger.LogDebug("stopping mutagen daemon");
var process = _daemonProcess;
var client = _mutagenClient;
var writer = _logWriter;
Expand All @@ -576,28 +591,34 @@ private async Task StopDaemon(CancellationToken ct)
if (client == null)
{
if (process == null) return;
_logger.LogDebug("no client; killing daemon process");
process.Kill(true);
}
else
{
try
{
_logger.LogDebug("sending DaemonTerminateRequest");
await client.Daemon.TerminateAsync(new DaemonTerminateRequest(), cancellationToken: ct);
}
catch
catch (Exception e)
{
_logger.LogError(e, "failed to gracefully terminate agent");
if (process == null) return;
_logger.LogDebug("killing daemon process after failed graceful termination");
process.Kill(true);
}
}

if (process == null) return;
var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
cts.CancelAfter(TimeSpan.FromSeconds(5));
_logger.LogDebug("waiting for process to exit");
await process.WaitForExitAsync(cts.Token);
}
finally
{
_logger.LogDebug("cleaning up daemon process objects");
client?.Dispose();
process?.Dispose();
writer?.Dispose();
Expand Down