Skip to content

Commit 1b0af6f

Browse files
committed
chore: simplify logging configuration and storage paths
1 parent 0c1fa97 commit 1b0af6f

File tree

20 files changed

+560
-115
lines changed

20 files changed

+560
-115
lines changed
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
## 1. Implementation
2-
- [ ] 1.1 Add shared `Logging` configuration for console/file sinks (enable flags, levels, path, rolling/retention) with appsettings + environment overrides in both hosts.
3-
- [ ] 1.2 Wire Serilog via `Serilog.Extensions.Logging` + `Serilog.Sinks.File` as the file sink (no custom writers) and initialize a single logging pipeline (console + file) from configuration; pass the configured `ILoggerFactory` into Modulus runtime components instead of ad-hoc factories/null loggers.
4-
- [ ] 1.3 Enrich log scopes with host type and module identity and ensure runtime components use these scopes during module discovery, load, initialization, and shutdown.
5-
- [ ] 1.4 Add structured lifecycle logs for manifest validation, dependency graph building, module load/init/unload, and host startup/shutdown with clear levels/event IDs.
6-
- [ ] 1.5 Add validation/smoke coverage that a log file is created, rolling/retention works, console output remains available when enabled, and the Serilog file sink is active (e.g., host-level test or manual check guidance).
7-
- [ ] 1.6 Ensure modules resolve `ILogger<T>`/`ILoggerFactory` from DI (shared host pipeline) and cannot add/replace logging providers or change logging configuration; document or enforce rejection/ignore behavior for module-level reconfiguration attempts.
2+
- [x] 1.1 Add shared `Logging` configuration for console/file sinks (enable flags, levels, path, rolling/retention) with appsettings + environment overrides in both hosts.
3+
- [x] 1.2 Wire Serilog via `Serilog.Extensions.Logging` + `Serilog.Sinks.File` as the file sink (no custom writers) and initialize a single logging pipeline (console + file) from configuration; pass the configured `ILoggerFactory` into Modulus runtime components instead of ad-hoc factories/null loggers.
4+
- [x] 1.3 Enrich log scopes with host type and module identity and ensure runtime components use these scopes during module discovery, load, initialization, and shutdown.
5+
- [x] 1.4 Add structured lifecycle logs for manifest validation, dependency graph building, module load/init/unload, and host startup/shutdown with clear levels/event IDs.
6+
- [x] 1.5 Add validation/smoke coverage that a log file is created, rolling/retention works, console output remains available when enabled, and the Serilog file sink is active (e.g., host-level test or manual check guidance).
7+
- [x] 1.6 Ensure modules resolve `ILogger<T>`/`ILoggerFactory` from DI (shared host pipeline) and cannot add/replace logging providers or change logging configuration; document or enforce rejection/ignore behavior for module-level reconfiguration attempts.
88

src/Hosts/Modulus.Host.Avalonia/App.axaml.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
using Avalonia.Controls.ApplicationLifetimes;
33
using Avalonia.Markup.Xaml;
44
using Avalonia.Styling;
5+
using Microsoft.Extensions.Configuration;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Microsoft.Extensions.Logging;
58
using Modulus.Core;
69
using Modulus.Core.Data;
710
using Modulus.Core.Installation;
11+
using Modulus.Core.Logging;
812
using Modulus.Core.Runtime;
913
using Modulus.Host.Avalonia.Services;
1014
using Modulus.Host.Avalonia.Shell.Services;
@@ -13,10 +17,6 @@
1317
using Modulus.Infrastructure.Data.Repositories;
1418
using Modulus.Sdk;
1519
using Modulus.UI.Abstractions;
16-
using Microsoft.Extensions.DependencyInjection;
17-
using Microsoft.Extensions.Configuration;
18-
using Microsoft.Extensions.Logging;
19-
using Microsoft.Extensions.Logging.Abstractions;
2020
using System;
2121
using System.IO;
2222
using System.Threading.Tasks;
@@ -72,10 +72,17 @@ public override void OnFrameworkInitializationCompleted()
7272
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
7373
{
7474
var services = new ServiceCollection();
75-
76-
// Add Logging
77-
services.AddLogging();
78-
75+
76+
// Configuration
77+
var configuration = new ConfigurationBuilder()
78+
.SetBasePath(AppContext.BaseDirectory)
79+
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
80+
.AddEnvironmentVariables()
81+
.Build();
82+
83+
var loggerFactory = ModulusLogging.CreateLoggerFactory(configuration, HostType.Avalonia);
84+
ModulusLogging.AddLoggerFactory(services, loggerFactory);
85+
7986
// Module Providers - load from Modules/ directory
8087
var providers = new System.Collections.Generic.List<IModuleProvider>();
8188

@@ -88,32 +95,25 @@ public override void OnFrameworkInitializationCompleted()
8895
if (Directory.Exists(artifactsModules))
8996
{
9097
// User modules from artifacts - NOT system modules
91-
providers.Add(new DirectoryModuleProvider(artifactsModules, NullLogger.Instance, isSystem: false));
98+
providers.Add(new DirectoryModuleProvider(artifactsModules, loggerFactory.CreateLogger<DirectoryModuleProvider>(), isSystem: false));
9299
}
93100
}
94101
#else
95102
// Production: Load from {AppBaseDir}/Modules/
96103
var appModules = Path.Combine(AppContext.BaseDirectory, "Modules");
97104
if (Directory.Exists(appModules))
98105
{
99-
providers.Add(new DirectoryModuleProvider(appModules, NullLogger.Instance, isSystem: true));
106+
providers.Add(new DirectoryModuleProvider(appModules, loggerFactory.CreateLogger<DirectoryModuleProvider>(), isSystem: true));
100107
}
101108
#endif
102109

103110
// User-installed modules (for runtime installation)
104111
var userModules = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Modulus", "Modules");
105112
if (Directory.Exists(userModules))
106113
{
107-
providers.Add(new DirectoryModuleProvider(userModules, NullLogger.Instance, isSystem: false));
114+
providers.Add(new DirectoryModuleProvider(userModules, loggerFactory.CreateLogger<DirectoryModuleProvider>(), isSystem: false));
108115
}
109116

110-
// Configuration
111-
var configuration = new ConfigurationBuilder()
112-
.SetBasePath(AppContext.BaseDirectory)
113-
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
114-
.AddEnvironmentVariables()
115-
.Build();
116-
117117
// Database (configurable name; defaults to framework/solution name)
118118
var dbName = configuration["Modulus:DatabaseName"] ?? "Modulus";
119119
var dbPath = DatabaseServiceExtensions.GetDefaultDatabasePath(dbName);
@@ -129,7 +129,7 @@ public override void OnFrameworkInitializationCompleted()
129129

130130
// Bootstrap Modulus
131131
var appTask = Task.Run(async () =>
132-
await ModulusApplicationFactory.CreateAsync<AvaloniaHostModule>(services, providers, HostType.Avalonia, dbPath)
132+
await ModulusApplicationFactory.CreateAsync<AvaloniaHostModule>(services, providers, HostType.Avalonia, dbPath, configuration, loggerFactory)
133133
);
134134
_modulusApp = appTask.GetAwaiter().GetResult();
135135

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
{
22
"Modulus": {
33
"DatabaseName": "Modulus"
4+
},
5+
"Serilog": {
6+
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
7+
"MinimumLevel": {
8+
"Default": "Information",
9+
"Override": {
10+
"Microsoft": "Warning",
11+
"System": "Warning"
12+
}
13+
},
14+
"WriteTo": [
15+
{
16+
"Name": "Console",
17+
"Args": {
18+
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}] [Host:{HostType}|Module:{ModuleName}|Src:{SourceContext}] {Message:lj}{NewLine}{Exception}"
19+
}
20+
},
21+
{
22+
"Name": "File",
23+
"Args": {
24+
"path": "logs/AvaloniaApp/log-.txt",
25+
"rollingInterval": "Day",
26+
"retainedFileCountLimit": 7,
27+
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}] [Host:{HostType}|Module:{ModuleName}|Src:{SourceContext}] {Message:lj}{NewLine}{Exception}"
28+
}
29+
}
30+
]
431
}
532
}
633

src/Hosts/Modulus.Host.Blazor/MauiProgram.cs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
using Microsoft.Extensions.DependencyInjection;
1+
using Microsoft.Extensions.Configuration;
2+
using Microsoft.Extensions.DependencyInjection;
23
using Microsoft.Extensions.Logging;
3-
using Microsoft.Extensions.Logging.Abstractions;
4-
using Microsoft.Extensions.Configuration;
54
using MudBlazor.Services;
65
using Modulus.Core;
76
using Modulus.Core.Data;
87
using Modulus.Core.Installation;
8+
using Modulus.Core.Logging;
99
using Modulus.Core.Runtime;
1010
using Modulus.Host.Blazor.Services;
1111
using Modulus.Host.Blazor.Shell.Services;
@@ -68,30 +68,33 @@ public static MauiApp CreateMauiApp()
6868
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
6969
});
7070

71+
// Configuration
72+
var configuration = new ConfigurationBuilder()
73+
.SetBasePath(AppContext.BaseDirectory)
74+
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
75+
.AddEnvironmentVariables()
76+
.Build();
77+
78+
var loggerFactory = ModulusLogging.CreateLoggerFactory(configuration, HostType.Blazor);
79+
ModulusLogging.AddLoggerFactory(builder.Services, loggerFactory);
80+
7181
// Module Providers - load from Modules/ directory relative to executable
7282
var providers = new List<IModuleProvider>();
7383

7484
// App Modules: {AppBaseDir}/Modules/ (populated by nuke build)
7585
var appModules = Path.Combine(AppContext.BaseDirectory, "Modules");
7686
if (Directory.Exists(appModules))
7787
{
78-
providers.Add(new DirectoryModuleProvider(appModules, NullLogger.Instance, isSystem: true));
88+
providers.Add(new DirectoryModuleProvider(appModules, loggerFactory.CreateLogger<DirectoryModuleProvider>(), isSystem: true));
7989
}
8090

8191
// User-installed modules (for runtime installation)
8292
var userModules = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Modulus", "Modules");
8393
if (Directory.Exists(userModules))
8494
{
85-
providers.Add(new DirectoryModuleProvider(userModules, NullLogger.Instance, isSystem: false));
95+
providers.Add(new DirectoryModuleProvider(userModules, loggerFactory.CreateLogger<DirectoryModuleProvider>(), isSystem: false));
8696
}
8797

88-
// Configuration
89-
var configuration = new ConfigurationBuilder()
90-
.SetBasePath(AppContext.BaseDirectory)
91-
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
92-
.AddEnvironmentVariables()
93-
.Build();
94-
9598
// Database (configurable name; defaults to framework/solution name)
9699
var dbName = configuration["Modulus:DatabaseName"] ?? "Modulus";
97100
var dbPath = DatabaseServiceExtensions.GetDefaultDatabasePath(dbName);
@@ -103,7 +106,7 @@ public static MauiApp CreateMauiApp()
103106
builder.Services.AddScoped<HostModuleSeeder>();
104107

105108
// Create Modulus App (use same DB path to align migrations and runtime)
106-
var appTask = ModulusApplicationFactory.CreateAsync<BlazorHostModule>(builder.Services, providers, HostType.Blazor, dbPath);
109+
var appTask = ModulusApplicationFactory.CreateAsync<BlazorHostModule>(builder.Services, providers, HostType.Blazor, dbPath, configuration, loggerFactory);
107110
var modulusApp = appTask.GetAwaiter().GetResult();
108111

109112
// Register the app as IModulusApplication so it can be injected
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
{
22
"Modulus": {
33
"DatabaseName": "Modulus"
4+
},
5+
"Serilog": {
6+
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
7+
"MinimumLevel": {
8+
"Default": "Information",
9+
"Override": {
10+
"Microsoft": "Warning",
11+
"System": "Warning"
12+
}
13+
},
14+
"WriteTo": [
15+
{
16+
"Name": "Console",
17+
"Args": {
18+
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}] [Host:{HostType}|Module:{ModuleName}|Src:{SourceContext}] {Message:lj}{NewLine}{Exception}"
19+
}
20+
},
21+
{
22+
"Name": "File",
23+
"Args": {
24+
"path": "logs/BlazorApp/log-.txt",
25+
"rollingInterval": "Day",
26+
"retainedFileCountLimit": 7,
27+
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}] [Host:{HostType}|Module:{ModuleName}|Src:{SourceContext}] {Message:lj}{NewLine}{Exception}"
28+
}
29+
}
30+
]
431
}
532
}
633

src/Modules/ComponentsDemo/ComponentsDemo.Core/ComponentsDemo.Core.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
<ItemGroup>
99
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
10+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
11+
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0" />
1012
</ItemGroup>
1113

1214
<PropertyGroup>

src/Modules/ComponentsDemo/ComponentsDemo.Core/ComponentsDemoModule.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Logging;
23
using Modulus.Modules.ComponentsDemo.ViewModels;
34
using Modulus.Sdk;
45

@@ -25,5 +26,12 @@ public override void ConfigureServices(IModuleLifecycleContext context)
2526
context.Services.AddTransient<KeyboardDemoViewModel>();
2627
context.Services.AddTransient<LifecycleDemoViewModel>();
2728
}
29+
30+
public override Task OnApplicationInitializationAsync(IModuleInitializationContext context, CancellationToken cancellationToken = default)
31+
{
32+
var logger = context.ServiceProvider.GetService<ILogger<ComponentsDemoModule>>();
33+
logger?.LogInformation("ComponentsDemo module initialized.");
34+
return Task.CompletedTask;
35+
}
2836
}
2937

src/Modules/EchoPlugin/EchoPlugin.Core/EchoPlugin.Core.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
<ProjectReference Include="..\..\..\Modulus.UI.Abstractions\Modulus.UI.Abstractions.csproj" />
66
</ItemGroup>
77

8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
10+
</ItemGroup>
11+
812
<PropertyGroup>
913
<TargetFramework>net10.0</TargetFramework>
1014
<ImplicitUsings>enable</ImplicitUsings>

src/Modules/EchoPlugin/EchoPlugin.Core/EchoPluginModule.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Logging;
23
using Modulus.Sdk;
34

45
namespace Modulus.Modules.EchoPlugin;
@@ -15,4 +16,11 @@ public override void ConfigureServices(IModuleLifecycleContext context)
1516
{
1617
context.Services.AddTransient<ViewModels.EchoViewModel>();
1718
}
19+
20+
public override Task OnApplicationInitializationAsync(IModuleInitializationContext context, CancellationToken cancellationToken = default)
21+
{
22+
var logger = context.ServiceProvider.GetService<ILogger<EchoPluginModule>>();
23+
logger?.LogInformation("EchoPlugin module initialized.");
24+
return Task.CompletedTask;
25+
}
1826
}

src/Modules/SimpleNotes/SimpleNotes.Core/SimpleNotes.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
1212
<PackageReference Include="MediatR" Version="14.0.0" />
1313
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
14+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
1415
</ItemGroup>
1516

1617
<ItemGroup>

0 commit comments

Comments
 (0)