1
1
using System ;
2
2
using System . Diagnostics ;
3
3
using System . IO ;
4
+ using System . Linq ;
4
5
using System . Threading ;
5
6
using System . Threading . Tasks ;
6
7
using Coder . Desktop . App . Models ;
16
17
using Microsoft . Win32 ;
17
18
using Microsoft . Windows . AppLifecycle ;
18
19
using Windows . ApplicationModel . Activation ;
20
+ using Microsoft . Extensions . Logging ;
21
+ using Serilog ;
19
22
20
23
namespace Coder . Desktop . App ;
21
24
@@ -24,22 +27,51 @@ public partial class App : Application
24
27
private readonly IServiceProvider _services ;
25
28
26
29
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}" ;
27
34
28
35
#if ! DEBUG
29
- private const string MutagenControllerConfigSection = "AppMutagenController" ;
36
+ private const string ConfigSubKey = @"SOFTWARE\Coder Desktop\App" ;
37
+ private const string logFilename = "app.log" ;
30
38
#else
31
- private const string MutagenControllerConfigSection = "DebugAppMutagenController" ;
39
+ private const string ConfigSubKey = @"SOFTWARE\Coder Desktop\DebugApp" ;
40
+ private const string logFilename = "debug-app.log" ;
32
41
#endif
33
42
43
+ private readonly ILogger < App > _logger ;
44
+
34
45
public App ( )
35
46
{
36
47
var builder = Host . CreateApplicationBuilder ( ) ;
37
48
38
49
( builder . Configuration as IConfigurationBuilder ) . Add (
39
- new RegistryConfigurationSource ( Registry . LocalMachine , @"SOFTWARE\Coder Desktop" ) ) ;
50
+ new RegistryConfigurationSource ( Registry . LocalMachine , ConfigSubKey ) ) ;
40
51
41
52
var services = builder . Services ;
42
53
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
+
43
75
services . AddSingleton < ICredentialManager , CredentialManager > ( ) ;
44
76
services . AddSingleton < IRpcController , RpcController > ( ) ;
45
77
@@ -69,6 +101,7 @@ public App()
69
101
services . AddTransient < TrayWindow > ( ) ;
70
102
71
103
_services = services . BuildServiceProvider ( ) ;
104
+ _logger = ( ILogger < App > ) ( _services . GetService ( typeof ( ILogger < App > ) ) ! ) ;
72
105
73
106
InitializeComponent ( ) ;
74
107
}
@@ -87,6 +120,7 @@ public async Task ExitApplication()
87
120
88
121
protected override void OnLaunched ( Microsoft . UI . Xaml . LaunchActivatedEventArgs args )
89
122
{
123
+ _logger . LogInformation ( "new instance launched" ) ;
90
124
// Start connecting to the manager in the background.
91
125
var rpcController = _services . GetRequiredService < IRpcController > ( ) ;
92
126
if ( rpcController . GetState ( ) . RpcLifecycle == RpcLifecycle . Disconnected )
@@ -110,13 +144,15 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
110
144
_ = credentialManager . LoadCredentials ( credentialManagerCts . Token ) . ContinueWith ( t =>
111
145
{
112
146
// TODO: log
113
- #if DEBUG
114
147
if ( t . Exception != null )
115
148
{
149
+ _logger . LogError ( t . Exception , "failed to load credentials" ) ;
150
+ #if DEBUG
116
151
Debug . WriteLine ( t . Exception ) ;
117
152
Debugger . Break ( ) ;
118
- }
119
153
#endif
154
+ }
155
+
120
156
credentialManagerCts . Dispose ( ) ;
121
157
} , CancellationToken . None ) ;
122
158
@@ -126,9 +162,13 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
126
162
_ = syncSessionController . RefreshState ( syncSessionCts . Token ) . ContinueWith ( t =>
127
163
{
128
164
// TODO: log
165
+ if ( t . IsCanceled || t . Exception != null )
166
+ {
167
+ _logger . LogError ( t . Exception , "failed to refresh sync state (canceled = {canceled}" , t . IsCanceled ) ;
129
168
#if DEBUG
130
- if ( t . IsCanceled || t . Exception != null ) Debugger . Break ( ) ;
169
+ Debugger . Break ( ) ;
131
170
#endif
171
+ }
132
172
syncSessionCts . Dispose ( ) ;
133
173
} , CancellationToken . None ) ;
134
174
@@ -148,17 +188,24 @@ public void OnActivated(object? sender, AppActivationArguments args)
148
188
{
149
189
case ExtendedActivationKind . Protocol :
150
190
var protoArgs = args . Data as IProtocolActivatedEventArgs ;
191
+ if ( protoArgs == null )
192
+ {
193
+ _logger . LogWarning ( "URI activation with null data" ) ;
194
+ return ;
195
+ }
196
+
151
197
HandleURIActivation ( protoArgs . Uri ) ;
152
198
break ;
153
199
154
200
default :
155
- // TODO: log
201
+ _logger . LogWarning ( "activation for {kind}, which is unhandled" , args . Kind ) ;
156
202
break ;
157
203
}
158
204
}
159
205
160
206
public void HandleURIActivation ( Uri uri )
161
207
{
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 ) ;
163
210
}
164
211
}
0 commit comments