11using System ;
22using System . Diagnostics ;
33using System . IO ;
4+ using System . Linq ;
45using System . Threading ;
56using System . Threading . Tasks ;
67using Coder . Desktop . App . Models ;
1617using Microsoft . Win32 ;
1718using Microsoft . Windows . AppLifecycle ;
1819using Windows . ApplicationModel . Activation ;
20+ using Microsoft . Extensions . Logging ;
21+ using Serilog ;
1922
2023namespace Coder . Desktop . App ;
2124
@@ -24,22 +27,51 @@ public partial class App : Application
2427 private readonly IServiceProvider _services ;
2528
2629 private bool _handleWindowClosed = true ;
30+ private const string MutagenControllerConfigSection = "MutagenController" ;
31+
32+ private const string logTemplate =
33+ "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}" ;
2734
2835#if ! DEBUG
29- private const string MutagenControllerConfigSection = "AppMutagenController" ;
36+ private const string ConfigSubKey = @"SOFTWARE\Coder Desktop\App" ;
37+ private const string logFilename = "app.log" ;
3038#else
31- private const string MutagenControllerConfigSection = "DebugAppMutagenController" ;
39+ private const string ConfigSubKey = @"SOFTWARE\Coder Desktop\DebugApp" ;
40+ private const string logFilename = "debug-app.log" ;
3241#endif
3342
43+ private readonly ILogger < App > _logger ;
44+
3445 public App ( )
3546 {
3647 var builder = Host . CreateApplicationBuilder ( ) ;
3748
3849 ( builder . Configuration as IConfigurationBuilder ) . Add (
39- new RegistryConfigurationSource ( Registry . LocalMachine , @"SOFTWARE\Coder Desktop" ) ) ;
50+ new RegistryConfigurationSource ( Registry . LocalMachine , ConfigSubKey ) ) ;
4051
4152 var services = builder . Services ;
4253
54+ // Logging
55+ builder . Services . AddSerilog ( ( _ , loggerConfig ) =>
56+ {
57+ loggerConfig . ReadFrom . Configuration ( builder . Configuration ) ;
58+ var sinkConfig = builder . Configuration . GetSection ( "Serilog" ) . GetSection ( "WriteTo" ) ;
59+ if ( ! sinkConfig . GetChildren ( ) . Any ( ) )
60+ {
61+ // no log sink defined in the registry, so we'll add one here.
62+ // We can't generally define these in the registry because we don't
63+ // know, a priori, what user will execute Coder Desktop, and therefore
64+ // what directories are writable by them. But, it's nice to be able to
65+ // directly customize Serilog via the registry if you know what you are
66+ // doing.
67+ var logPath = Path . Combine (
68+ Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ,
69+ "CoderDesktop" ,
70+ logFilename ) ;
71+ loggerConfig . WriteTo . File ( logPath , outputTemplate : logTemplate , rollingInterval : RollingInterval . Day ) ;
72+ }
73+ } ) ;
74+
4375 services . AddSingleton < ICredentialManager , CredentialManager > ( ) ;
4476 services . AddSingleton < IRpcController , RpcController > ( ) ;
4577
@@ -69,6 +101,7 @@ public App()
69101 services . AddTransient < TrayWindow > ( ) ;
70102
71103 _services = services . BuildServiceProvider ( ) ;
104+ _logger = ( ILogger < App > ) ( _services . GetService ( typeof ( ILogger < App > ) ) ! ) ;
72105
73106 InitializeComponent ( ) ;
74107 }
@@ -87,6 +120,7 @@ public async Task ExitApplication()
87120
88121 protected override void OnLaunched ( Microsoft . UI . Xaml . LaunchActivatedEventArgs args )
89122 {
123+ _logger . LogInformation ( "new instance launched" ) ;
90124 // Start connecting to the manager in the background.
91125 var rpcController = _services . GetRequiredService < IRpcController > ( ) ;
92126 if ( rpcController . GetState ( ) . RpcLifecycle == RpcLifecycle . Disconnected )
@@ -110,13 +144,15 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
110144 _ = credentialManager . LoadCredentials ( credentialManagerCts . Token ) . ContinueWith ( t =>
111145 {
112146 // TODO: log
113- #if DEBUG
114147 if ( t . Exception != null )
115148 {
149+ _logger . LogError ( t . Exception , "failed to load credentials" ) ;
150+ #if DEBUG
116151 Debug . WriteLine ( t . Exception ) ;
117152 Debugger . Break ( ) ;
118- }
119153#endif
154+ }
155+
120156 credentialManagerCts . Dispose ( ) ;
121157 } , CancellationToken . None ) ;
122158
@@ -126,9 +162,13 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
126162 _ = syncSessionController . RefreshState ( syncSessionCts . Token ) . ContinueWith ( t =>
127163 {
128164 // TODO: log
165+ if ( t . IsCanceled || t . Exception != null )
166+ {
167+ _logger . LogError ( t . Exception , "failed to refresh sync state (canceled = {canceled})" , t . IsCanceled ) ;
129168#if DEBUG
130- if ( t . IsCanceled || t . Exception != null ) Debugger . Break ( ) ;
169+ Debugger . Break ( ) ;
131170#endif
171+ }
132172 syncSessionCts . Dispose ( ) ;
133173 } , CancellationToken . None ) ;
134174
@@ -148,17 +188,24 @@ public void OnActivated(object? sender, AppActivationArguments args)
148188 {
149189 case ExtendedActivationKind . Protocol :
150190 var protoArgs = args . Data as IProtocolActivatedEventArgs ;
191+ if ( protoArgs == null )
192+ {
193+ _logger . LogWarning ( "URI activation with null data" ) ;
194+ return ;
195+ }
196+
151197 HandleURIActivation ( protoArgs . Uri ) ;
152198 break ;
153199
154200 default :
155- // TODO: log
201+ _logger . LogWarning ( "activation for {kind}, which is unhandled" , args . Kind ) ;
156202 break ;
157203 }
158204 }
159205
160206 public void HandleURIActivation ( Uri uri )
161207 {
162- // TODO: handle
208+ // don't log the query string as that's where we include some sensitive information like passwords
209+ _logger . LogInformation ( "handling URI activation for {path}" , uri . AbsolutePath ) ;
163210 }
164211}
0 commit comments