17
17
using Coder . Desktop . Vpn . Utilities ;
18
18
using Grpc . Core ;
19
19
using Microsoft . Extensions . Options ;
20
+ using Microsoft . Extensions . Logging ;
21
+ using Serilog ;
20
22
using DaemonTerminateRequest = Coder . Desktop . MutagenSdk . Proto . Service . Daemon . TerminateRequest ;
21
23
using MutagenProtocol = Coder . Desktop . MutagenSdk . Proto . Url . Protocol ;
22
24
using SynchronizationTerminateRequest = Coder . Desktop . MutagenSdk . Proto . Service . Synchronization . TerminateRequest ;
25
+ using Microsoft . Extensions . Hosting ;
23
26
24
27
namespace Coder . Desktop . App . Services ;
25
28
@@ -113,6 +116,8 @@ public sealed class MutagenController : ISyncSessionController
113
116
// Protects all private non-readonly class members.
114
117
private readonly RaiiSemaphoreSlim _lock = new ( 1 , 1 ) ;
115
118
119
+ private readonly ILogger < MutagenController > _logger ;
120
+
116
121
private readonly CancellationTokenSource _stateUpdateCts = new ( ) ;
117
122
private Task ? _stateUpdateTask ;
118
123
@@ -142,15 +147,19 @@ public sealed class MutagenController : ISyncSessionController
142
147
143
148
private string MutagenDaemonLog => Path . Combine ( _mutagenDataDirectory , "daemon.log" ) ;
144
149
145
- public MutagenController ( IOptions < MutagenControllerConfig > config )
150
+ public MutagenController ( IOptions < MutagenControllerConfig > config , ILogger < MutagenController > logger )
146
151
{
147
152
_mutagenExecutablePath = config . Value . MutagenExecutablePath ;
153
+ _logger = logger ;
148
154
}
149
155
150
156
public MutagenController ( string executablePath , string dataDirectory )
151
157
{
152
158
_mutagenExecutablePath = executablePath ;
153
159
_mutagenDataDirectory = dataDirectory ;
160
+ var builder = Host . CreateApplicationBuilder ( ) ;
161
+ builder . Services . AddSerilog ( ) ;
162
+ _logger = ( ILogger < MutagenController > ) builder . Build ( ) . Services . GetService ( typeof ( ILogger < MutagenController > ) ) ! ;
154
163
}
155
164
156
165
public event EventHandler < SyncSessionControllerStateModel > ? StateChanged ;
@@ -447,9 +456,9 @@ private async Task<MutagenClient> EnsureDaemon(CancellationToken ct)
447
456
{
448
457
await StopDaemon ( cts . Token ) ;
449
458
}
450
- catch
459
+ catch ( Exception stopEx )
451
460
{
452
- // ignored
461
+ _logger . LogError ( stopEx , "failed to stop daemon" ) ;
453
462
}
454
463
455
464
ReplaceState ( new SyncSessionControllerStateModel
@@ -501,6 +510,8 @@ private async Task<MutagenClient> StartDaemon(CancellationToken ct)
501
510
}
502
511
catch ( Exception e ) when ( e is not OperationCanceledException )
503
512
{
513
+ _logger . LogWarning ( e , "failed to start daemon process, attempt {attempt} of {maxAttempts}" , attempts ,
514
+ maxAttempts ) ;
504
515
if ( attempts == maxAttempts )
505
516
throw ;
506
517
// back off a little and try again.
@@ -556,8 +567,11 @@ private void StartDaemonProcess()
556
567
// https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.environment?view=net-8.0
557
568
_daemonProcess . StartInfo . UseShellExecute = false ;
558
569
_daemonProcess . StartInfo . RedirectStandardError = true ;
559
- // TODO: log exited process
560
- // _daemonProcess.Exited += ...
570
+ _daemonProcess . EnableRaisingEvents = true ;
571
+ _daemonProcess . Exited += ( object ? sender , EventArgs e ) =>
572
+ {
573
+ _logger . LogInformation ( "mutagen daemon exited with code {exitCode}" , _daemonProcess ? . ExitCode ) ;
574
+ } ;
561
575
if ( ! _daemonProcess . Start ( ) )
562
576
throw new InvalidOperationException ( "Failed to start mutagen daemon process, Start returned false" ) ;
563
577
@@ -572,6 +586,7 @@ private void StartDaemonProcess()
572
586
/// </summary>
573
587
private async Task StopDaemon ( CancellationToken ct )
574
588
{
589
+ _logger . LogDebug ( "stopping mutagen daemon" ) ;
575
590
var process = _daemonProcess ;
576
591
var client = _mutagenClient ;
577
592
var writer = _logWriter ;
@@ -584,28 +599,34 @@ private async Task StopDaemon(CancellationToken ct)
584
599
if ( client == null )
585
600
{
586
601
if ( process == null ) return ;
602
+ _logger . LogDebug ( "no client; killing daemon process" ) ;
587
603
process . Kill ( true ) ;
588
604
}
589
605
else
590
606
{
591
607
try
592
608
{
609
+ _logger . LogDebug ( "sending DaemonTerminateRequest" ) ;
593
610
await client . Daemon . TerminateAsync ( new DaemonTerminateRequest ( ) , cancellationToken : ct ) ;
594
611
}
595
- catch
612
+ catch ( Exception e )
596
613
{
614
+ _logger . LogError ( e , "failed to gracefully terminate agent" ) ;
597
615
if ( process == null ) return ;
616
+ _logger . LogDebug ( "killing daemon process after failed graceful termination" ) ;
598
617
process . Kill ( true ) ;
599
618
}
600
619
}
601
620
602
621
if ( process == null ) return ;
603
622
var cts = CancellationTokenSource . CreateLinkedTokenSource ( ct ) ;
604
623
cts . CancelAfter ( TimeSpan . FromSeconds ( 5 ) ) ;
624
+ _logger . LogDebug ( "waiting for process to exit" ) ;
605
625
await process . WaitForExitAsync ( cts . Token ) ;
606
626
}
607
627
finally
608
628
{
629
+ _logger . LogDebug ( "cleaning up daemon process objects" ) ;
609
630
client ? . Dispose ( ) ;
610
631
process ? . Dispose ( ) ;
611
632
writer ? . Dispose ( ) ;
0 commit comments