Repository navigation
logging
CHDSharp is silent by default. It integrates with Microsoft.Extensions.Logging so you can route internal diagnostics to any compatible provider.
Set the static Chd.LoggerFactory before performing library operations:
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Extensions.Logging;
var serilogLogger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
Chd.LoggerFactory = new SerilogLoggerFactory(serilogLogger);
// All subsequent Chd/ChdFile operations log through Serilog
var result = Chd.CheckFile(File.OpenRead("game.chd"), "game.chd", deepCheck: true);Any ILoggerFactory-compatible provider works:
-
Serilog (
Serilog.Extensions.Logging) -
NLog (
NLog.Extensions.Logging) Microsoft.Extensions.Logging.Console- your own
ILoggerFactoryimplementation
| Area | Level | Examples |
|---|---|---|
| Verification | Information / Debug | progress percentages, array-pool statistics, compression-type statistics per CHD |
| Metadata | Debug | tag + length + ASCII payload of every metadata entry |
| Errors | Warning / Error | failed metadata reads, precache failures, decompression exceptions (with the inner exception and hunk number) |
| Per-codec | Debug | block summaries, repeated-block counts |
Because every log call is a pre-compiled LoggerMessage.Define delegate, the overhead is negligible when logging is disabled.
To disable logging again (e.g. in tests):
Chd.LoggerFactory = null;Chd.LoggerFactory = new SerilogLoggerFactory(
new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.File("chdsharp.log", rollingInterval: RollingInterval.Day)
.CreateLogger());The library stays silent unless you opt in, but the two front ends (CHDSharpCli, CHDSharpTester) always configure Serilog at startup and route all logging through it.
Both apps build the pipeline the same way:
-
MinimumLevel.Debug()— everything is captured; what the user sees vs. what is stored depends on the sink. -
Chd.LoggerFactory = new SerilogLoggerFactory(Log.Logger)— library diagnostics flow into the same pipeline. - Logger is flushed on exit (
Log.CloseAndFlush()in the CLIfinallyblock,App.OnExitin the Tester).
| App | Sinks |
|---|---|
CHDSharpCli (Program.Main) |
Console ({Message}{NewLine}{Exception}) + File (rolling day) + BugReportSink
|
CHDSharpTester (App.OnStartup) |
Debug + File (rolling day) + BugReportSink
|
Rolling-day files under %LocalAppData%:
- CLI:
%LocalAppData%\CHDSharp\logs\chdsharp-<date>.log - Tester:
%LocalAppData%\CHDSharpTester\logs\chdsharp-tester-<date>.log
File template: {Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}, InvariantCulture.
- No direct
Console/Debuglogging for diagnostics. The onlyConsolewrites left in the CLI arechdman-parity report output (e.g.info) and the double-click "Press any key to exit" pause; the onlyDebug.WriteLinecalls left in the Tester are fallbacks for failures thrown before the logger exists (OnStartup) or while it is being flushed (OnExit). The Tester's on-screen log (MainViewModel.AddLog) also forwards each entry toLog.Information, so the file log mirrors the UI. - Top-level and public entry points are wrapped in try/catch +
Log: CLIMain(Log.Fatal, exit code 3), TesterOnStartup/OnExit,RunAsync,ExportPdfAsync,RelayCommand.Execute, and the publicChdmanWrappermethods (GetInfo,Verify,ExtractRaw,Copy,CopyVerbosereturn safe fallbacks on failure). - Existing
catchblocks log with the exception attached where one is available (Log.Warning(ex, …)/Log.Error(ex, …)). Expected-failure probes log atDebugso they stay out of bug reports: codec-default detection, source-header reads, file-size stats,GetHunkCodecNameprobes, parent IDENT/GDDD reads, temp-file cleanup, and cancellation (OperationCanceledExceptionisInformation/Debug, never an error).
Both apps attach a BugReportSink (CHDSharpCli/BugReportSink.cs, CHDSharpTester/Services/BugReportSink.cs) that POSTs every log event at Warning or above to https://www.purelogiccode.com/bugreport/api/send-bug-report (X-API-KEY header, JSON body).
- Fire-and-forget (
Task.Run, 10 sHttpClienttimeout): delivery never blocks or throws into the logging pipeline. OnLog.CloseAndFlush(), the sink waits up to 5 s for in-flight reports so short-lived CLI sessions and crash shutdowns still deliver them. -
OperationCanceledException/TaskCanceledExceptionevents are skipped (user cancellation is not a bug). - Server throttling (
HTTP 429) and any transport failure are silently tolerated.
Each report embeds the same sections:
-
=== Environment Details ===— Date, Application Name, Application Version, OS Version, Architecture, Bitness, the platform version (Windows, Linux or MacOsX Versionin the CLI sink;Windows Versionin the WPF tester, which is Windows-only), Processor Count, Base Directory, Temp Path (the CLI sink also adds Runtime/Session/Elevated context). -
=== Error Details ===— the rendered log message. -
=== Exception Details ===— Type, Message, Source, StackTrace (plus inner exceptions;(none)placeholders when the event carries no exception, e.g. a usage warning). - The JSON payload also carries
applicationName,version,environment, andstackTraceper the Bug Report API.
-
LoggerFactoryis read lazily per operation, so you can swap providers at runtime; for predictable behavior, set it once at startup. - CI/scripted runs: set
CHDSHARP_NO_BUG_REPORTS=1to suppress report delivery andCHDSHARP_NO_TELEMETRY=1to skip the launch-stats hit and the GitHub version check (the repository CI workflow and the test harness set both). - The logging package (
Microsoft.Extensions.Logging.Abstractions) is the library's only non-Zstd dependency and is marked optional in the sense that the library functions perfectly with it never set.
CHDSharp
Format & internals
API & usage
CLI & writing
Parity & validation
Operations
Reference