Albatross.CommandLine.Defaults
The Albatross.CommandLine.Defaults package provides pre-configured integrations for configuration management and Serilog logging. It eliminates boilerplate setup by providing sensible, opinionated defaults for most command-line scenarios.
Design Philosophy
The Core Library: Maximum Compatibility
The main Albatross.CommandLine library is designed with broad compatibility as its primary goal. It targets .NET Standard 2.1 and keeps its dependency surface small:
<!-- Albatross.CommandLine dependencies -->
<PackageReference Include="System.CommandLine" Version="3.0.0-preview.5.26302.115" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
This approach ensures that:
- Applications targeting .NET 8 or newer can consume the library
- Version conflicts with other dependencies in your project are minimized
- The library integrates smoothly into existing projects with established dependency trees
Note
Because v9 references the System.CommandLine v3 prerelease, Albatross.CommandLine v9 is published on the prerelease channel until System.CommandLine v3 reaches GA. The v8.x line remains the current stable release for consumers who cannot take a prerelease dependency.
The Defaults Library: Opinionated Integrations
In contrast, Albatross.CommandLine.Defaults is opinionated. It targets net8.0 and pulls in the heavier integrations (Serilog, Albatross.Config) that the core deliberately leaves out, so the core stays dependency-light while applications that want batteries-included setup get it with one call.
The trade-off is intentional: if you have strict dependency version requirements, or want a different logging framework, use the core Albatross.CommandLine library directly and configure services yourself. If you are building a CLI application and want sensible defaults, use Albatross.CommandLine.Defaults.
What the Library Provides
The Defaults library provides three entry points:
- Configuration Management via
WithConfig() - File-Based Serilog Logging via
WithSerilog() - Combined Setup via
WithDefaults()(calls both)
A defining v9 behavior: logging goes to a file, never the console. The console (stdout/stderr) is reserved for the command's own output. See Logging & Verbosity for the full model.
Quick Start
WithSerilog() writes logs to IApplicationPath.LogRoot. Registering an IApplicationPath before the host is built lets you control that location; if none is registered, WithSerilog() falls back to a DefaultApplicationPath (logging to a log folder under the application base directory) so logging works with no extra setup. The typical pattern still creates the ApplicationPath up front, registers it, and passes its ConfigRoot to WithDefaults():
using Albatross.CommandLine;
using Albatross.CommandLine.Defaults;
using Albatross.Config;
using Microsoft.Extensions.DependencyInjection;
static async Task<int> Main(string[] args) {
var appPath = new ApplicationPath(false, ["myapp"], "myapp", null, args);
appPath.Init(); // creates ConfigRoot / LogRoot / DataRoot
await using var host = new CommandHost("My CLI App")
.RegisterServices((result, services) => {
services.AddSingleton<IApplicationPath>(appPath);
services.RegisterCommands();
})
.AddCommands()
.Parse(args)
.WithDefaults(appPath.ConfigRoot) // config + file logging
.Build();
return await host.InvokeAsync();
}
Extension Methods
WithDefaults()
WithDefaults() is a convenience method that applies both configuration and Serilog logging:
public static CommandHost WithDefaults(this CommandHost commandHost, string? configDirectory = null)
=> commandHost.WithConfig(configDirectory).WithSerilog();
The optional configDirectory is forwarded to WithConfig(). Passing appPath.ConfigRoot makes configuration load from the stable, writable config directory rather than the application's bin folder. This is the recommended approach for most applications.
WithConfig()
The WithConfig() method sets up JSON-based configuration with environment support:
host.Parse(args)
.WithConfig(appPath.ConfigRoot)
.Build();
What it configures:
JSON Configuration Files
- Loads
appsettings.jsonfromconfigDirectory(or the application base directory when omitted) - Loads an environment-specific file such as
appsettings.Development.jsonorappsettings.Production.json - Configuration files are optional (loading won't fail if they are missing)
- Loads
Environment Variables
- Adds the environment variable configuration provider; environment variables can override JSON settings
Environment Detection
- Uses the
DOTNET_ENVIRONMENTenvironment variable to choose the environment-specific config file (Development,Staging,Production, …)
- Uses the
Registered Services
EnvironmentSetting— the current environment nameProgramSetting— wraps theIConfigurationinstanceIHostEnvironment— the standard host environment abstraction
Note
WithConfig(configDirectory) replaces the base path — it does not layer the bin folder and configDirectory together. When you pass appPath.ConfigRoot, configuration is read solely from ConfigRoot.
Example appsettings.json:
{
"ConnectionStrings": {
"Database": "Server=localhost;Database=MyApp"
},
"AppSettings": {
"MaxRetries": 3,
"Timeout": "00:00:30"
}
}
Using configuration in a handler:
public class MyHandler : BaseHandler<MyParams> {
private readonly IConfiguration configuration;
public MyHandler(
IConfiguration configuration,
ParseResult result,
MyParams parameters) : base(result, parameters) {
this.configuration = configuration;
}
public override Task<int> InvokeAsync(CancellationToken token) {
var connectionString = configuration.GetConnectionString("Database");
var maxRetries = configuration.GetValue<int>("AppSettings:MaxRetries");
// ...
}
}
WithSerilog()
WithSerilog() configures Serilog as the logging provider, writing to a file — there is no console sink.
host.Parse(args)
.WithSerilog()
.Build();
What it configures:
File Sink under
IApplicationPath.LogRoot- Resolves
IApplicationPathfrom the service container. If none is registered, it falls back to aDefaultApplicationPath, whoseLogRootis alogfolder under the application base directory — logging works with zero setup. - Writes a daily-rolling file named
{entryAssemblyName}-.log(Serilog inserts the date, e.g.myapp-20260708.log). - Uses the
DefaultOutputTemplateconstant for formatting.
- Resolves
Code Baseline (works with zero configuration)
- Minimum level
Information Enrich.FromLogContext()andEnrich.WithThreadId()
- Minimum level
Configuration Overrides
- Layers
ReadFrom.Configuration(...)on top of the baseline. ASerilogsection inappsettings.json(loaded byWithConfig()) can raise or lower the level, add per-namespaceMinimumLevel:Overrides, and add enrichers or sinks — all editable at deploy time without recompiling.
- Layers
Because the baseline is applied first and ReadFrom.Configuration second, a Serilog section overrides only the keys it specifies; an absent or empty section leaves the code baseline intact.
Note
A sink whose secret is supplied through an environment variable (e.g. Serilog__WriteTo__Slack__Args__…) still needs its Name element defined in a JSON file. ReadFrom.Configuration fails fast at startup if a WriteTo entry has no Name.
Order of Operations
The extension methods must be called after Parse():
await using var host = new CommandHost("My App")
.RegisterServices(RegisterServices) // 1. Register your services (incl. IApplicationPath)
.AddCommands() // 2. Add generated commands
.Parse(args) // 3. Parse command line
.WithDefaults(appPath.ConfigRoot) // 4. Configure defaults (after parse)
.Build(); // 5. Build the host
Configuration and logging are wired into the host builder, so they must be in place before Build().
Using Components Separately
If you only need one of the integrations:
// Configuration only (no logging)
host.Parse(args)
.WithConfig(appPath.ConfigRoot)
.Build();
// Logging only (no configuration)
host.Parse(args)
.WithSerilog()
.Build();
Dependencies
The Defaults package brings in these libraries:
| Package | Version | Purpose |
|---|---|---|
| Albatross.Config | 8.0.0-rc.51 | Application paths (IApplicationPath), configuration, environment handling |
| Serilog | 4.3.0 | Structured logging core |
| Serilog.Extensions.Hosting | 8.0.0 | UseSerilog host integration |
| Serilog.Settings.Configuration | 8.0.4 | ReadFrom.Configuration support |
| Serilog.Sinks.File | 6.0.0 | Rolling-file sink |
| Serilog.Enrichers.Thread | 4.0.0 | WithThreadId enricher |
| Microsoft.Extensions.Hosting | 8.0.1 | Host builder abstractions |
| Microsoft.Extensions.DependencyInjection | 8.0.1 | Service container integration |
Note
v9 configures Serilog directly — the Albatross.Logging dependency used in v8 has been removed.
When to Use (and When Not To)
Use Albatross.CommandLine.Defaults when:
- Building a standalone CLI application
- You want sensible, file-based logging defaults without boilerplate
- Serilog is your preferred logging framework
- You are using
Albatross.ConfigIApplicationPathfor path management
Configure services manually when:
- You have specific dependency version requirements
- You need a different logging framework (NLog, log4net, etc.)
- You want console logging or a custom sink topology out of the box
- You need custom configuration providers (Azure Key Vault, AWS Secrets Manager, etc.)
- You are integrating into an existing application with established DI configuration