Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 86b541a

Browse files
committedApr 24, 2025·
feat: add logging to App
1 parent a58864e commit 86b541a

File tree

8 files changed

+695
-564
lines changed

8 files changed

+695
-564
lines changed
 

‎App/App.csproj

+6-3
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,13 @@
6161
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
6262
</PackageReference>
6363
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.2.0" />
64-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.1" />
65-
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
66-
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.1" />
64+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
65+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
66+
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.4" />
6767
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250108002" />
68+
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
69+
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
70+
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
6871
<PackageReference Include="WinUIEx" Version="2.5.1" />
6972
</ItemGroup>
7073

‎App/App.xaml.cs

+55-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics;
33
using System.IO;
4+
using System.Linq;
45
using System.Threading;
56
using System.Threading.Tasks;
67
using Coder.Desktop.App.Models;
@@ -16,6 +17,8 @@
1617
using Microsoft.Win32;
1718
using Microsoft.Windows.AppLifecycle;
1819
using Windows.ApplicationModel.Activation;
20+
using Microsoft.Extensions.Logging;
21+
using Serilog;
1922

2023
namespace 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
}

‎App/packages.lock.json

+238-182
Large diffs are not rendered by default.

‎Installer/Installer.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
</ItemGroup>
1818

1919
<ItemGroup>
20+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.4" />
2021
<PackageReference Include="WixSharp_wix4" Version="2.6.0" />
2122
<PackageReference Include="WixSharp_wix4.bin" Version="2.6.0" />
2223
<PackageReference Include="CommandLineParser" Version="2.9.1" />

‎Installer/Program.cs

+28-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using System.Text;
56
using CommandLine;
7+
using Microsoft.Extensions.Configuration;
68
using WixSharp;
79
using WixSharp.Bootstrapper;
810
using WixSharp.CommonTasks;
@@ -138,6 +140,7 @@ public class Program
138140
private const string Manufacturer = "Coder Technologies Inc.";
139141
private const string HelpUrl = "https://coder.com/docs";
140142
private const string RegistryKey = @"SOFTWARE\Coder Desktop";
143+
private const string AppConfigRegistryKey = RegistryKey + @"\App";
141144

142145
private const string DotNetCheckName = "DOTNET_RUNTIME_CHECK";
143146
private const RollForward DotNetCheckRollForward = RollForward.minor;
@@ -256,6 +259,7 @@ private static int BuildMsiPackage(MsiOptions opts)
256259
project.AddDir(programFiles64Folder);
257260

258261

262+
259263
project.AddRegValues(
260264
// Add registry values that are consumed by the manager. Note that these
261265
// should not be changed. See Vpn.Service/Program.cs and
@@ -266,12 +270,32 @@ private static int BuildMsiPackage(MsiOptions opts)
266270
new RegValue(RegistryHive, RegistryKey, "Manager:LogFileLocation",
267271
@"[INSTALLFOLDER]coder-desktop-service.log"),
268272
new RegValue(RegistryHive, RegistryKey, "Manager:TunnelBinarySignatureSigner", "Coder Technologies Inc."),
269-
new RegValue(RegistryHive, RegistryKey, "Manager:TunnelBinaryAllowVersionMismatch", "false"),
270-
// Add registry values that are consumed by the App MutagenController. See App/Services/MutagenController.cs
271-
new RegValue(RegistryHive, RegistryKey, "AppMutagenController:MutagenExecutablePath",
272-
@"[INSTALLFOLDER]mutagen.exe")
273+
new RegValue(RegistryHive, RegistryKey, "Manager:TunnelBinaryAllowVersionMismatch", "false")
273274
);
274275

276+
// rather than adding individual reg values, it's much clearer to write the whole config as JSON then
277+
// programmatically spit out the registry values.
278+
var appConfigJson = """
279+
{
280+
"MutagenController": {
281+
"MutagenExecutablePath": "[INSTALLFOLDER]mutagen.exe"
282+
},
283+
"Serilog": {
284+
"Using": ["Serilog.Sinks.File"],
285+
"MinimumLevel": "Information",
286+
"Enrich": [ "FromLogContext" ],
287+
}
288+
}
289+
""";
290+
var appConfig =
291+
new ConfigurationBuilder().AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(appConfigJson))).Build();
292+
293+
foreach (var kvp in appConfig.AsEnumerable())
294+
{
295+
if (kvp.Value is null) continue;
296+
project.AddRegValue(new RegValue(RegistryHive, AppConfigRegistryKey, kvp.Key, kvp.Value));
297+
}
298+
275299
// Note: most of this control panel info will not be visible as this
276300
// package is usually hidden in favor of the bootstrapper showing
277301
// instead.

‎Tests.Vpn.Service/packages.lock.json

+182-182
Large diffs are not rendered by default.

‎Vpn.Service/Vpn.Service.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
</ItemGroup>
2626

2727
<ItemGroup>
28-
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
29-
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.1" />
30-
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="9.0.1" />
28+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
29+
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.4" />
30+
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="9.0.4" />
3131
<PackageReference Include="Microsoft.Security.Extensions" Version="1.3.0" />
3232
<PackageReference Include="Semver" Version="3.0.0" />
3333
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />

‎Vpn.Service/packages.lock.json

+182-182
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.