-
Notifications
You must be signed in to change notification settings - Fork 1
diagnostics
CheapAvaloniaBlazor includes a comprehensive diagnostic system for troubleshooting application startup, configuration, and runtime issues. The diagnostic system provides visibility into service registration, Blazor server initialization, window setup, JavaScript interop operations, and performance metrics.
Key Features:
- Conditional verbose logging that respects EnableDiagnostics flag
- Service registration and initialization tracking
- Blazor server startup and health monitoring
- Window lifecycle and property initialization logging
- JavaScript bridge status and communication tracking
- File dialog operation logs
- Performance timing information for critical operations
The diagnostic system is designed to be non-intrusive—diagnostic output is only generated when explicitly enabled, keeping production logs clean while providing detailed troubleshooting information during development.
The simplest way to enable comprehensive diagnostics is using the EnableDiagnostics() method on the HostBuilder. This automatically enables both diagnostics and console logging:
using CheapAvaloniaBlazor.Hosting;
using MudBlazor.Services;
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableDiagnostics() // Enables both EnableDiagnostics and EnableConsoleLogging
.AddMudBlazor();
builder.RunApp(args);Effects of EnableDiagnostics():
- Sets
EnableDiagnostics = truein options - Automatically enables console logging
- Logging minimum level adjusted to Debug
- All diagnostic log methods will output messages
An alternative alias method is available:
var builder = new HostBuilder()
.WithTitle("My Application")
.WithDiagnostics() // Alias for EnableDiagnostics()
.AddMudBlazor();
builder.RunApp(args);You can enable console logging independently from diagnostics:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableConsoleLogging(true) // Only enable console output, not diagnostics
.AddMudBlazor();
builder.RunApp(args);For more granular control, directly configure the options:
var builder = new HostBuilder()
.WithTitle("My Application")
.ConfigureOptions(options =>
{
options.EnableDiagnostics = true;
options.EnableConsoleLogging = true;
})
.AddMudBlazor();
builder.RunApp(args);When diagnostics are enabled, the system automatically tracks:
- Summary of registered services
- Dependency injection container status
- Custom service configuration details
- Server initialization timestamp
- Port and protocol configuration (HTTP/HTTPS)
- Content root and web root paths
- Static web assets loading
- Circuit initialization and configuration
- Window creation with type name
- Title, width, and height settings
- Window startup location (centered vs. manual positioning)
- Icon loading success/failure
- Avalonia platform configuration
- Bridge initialization status
- Ready state confirmation
- Interop timeout configuration
- Message channel establishment
- Dialog open/save operations initiated
- File selection results
- Dialog cancellation events
- Path information and file counts
- Service initialization duration
- Blazor server startup time
- Window creation latency
- Bridge readiness time
The DiagnosticLogger is an abstraction layer over ILogger that automatically respects the EnableDiagnostics flag. It provides specialized methods for different logging scenarios:
-
Diagnostic logs: Only output when
EnableDiagnostics = true -
Verbose logs: Only output when
EnableDiagnostics = true - Information, Warning, Error logs: Always output regardless of diagnostic flag
This design keeps production logs clean by filtering diagnostic-only messages while ensuring important warnings and errors are always visible.
Inject IDiagnosticLoggerFactory into your services to create loggers:
using CheapAvaloniaBlazor.Services;
public class MyService
{
private readonly DiagnosticLogger _logger;
public MyService(IDiagnosticLoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<MyService>();
}
}The factory will automatically create a DiagnosticLogger instance bound to your service type.
public class DataProcessingService
{
private readonly DiagnosticLogger _logger;
public DataProcessingService(IDiagnosticLoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<DataProcessingService>();
}
public void ProcessData(string[] items)
{
// Only logs if EnableDiagnostics = true
_logger.LogDiagnostic("Starting data processing for {ItemCount} items", items.Length);
foreach (var item in items)
{
_logger.LogDiagnostic("Processing item: {Item}", item);
}
_logger.LogDiagnostic("Data processing completed");
}
}public class AdvancedService
{
private readonly DiagnosticLogger _logger;
public AdvancedService(IDiagnosticLoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AdvancedService>();
}
public void PerformOperation()
{
if (_logger.DiagnosticsEnabled)
{
// Expensive diagnostic work - only do if diagnostics are enabled
_logger.LogVerbose("Starting detailed diagnostic trace");
// ... detailed logging ...
}
// Always log important operations
_logger.LogInformation("Operation completed successfully");
}
}public class RobustService
{
private readonly DiagnosticLogger _logger;
public RobustService(IDiagnosticLoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<RobustService>();
}
public void ExecuteTask()
{
_logger.LogDiagnostic("Task starting");
try
{
// ... perform work ...
_logger.LogVerbose("Task progressed to checkpoint A");
// ... more work ...
_logger.LogInformation("Task completed successfully");
}
catch (Exception ex)
{
// Errors always logged
_logger.LogError(ex, "Task failed with exception");
}
}
}| Method | Diagnostic-Gated | Log Level | Use Case |
|---|---|---|---|
LogDiagnostic(message, args) |
Yes | Debug | Detailed troubleshooting info only needed with diagnostics enabled |
LogVerbose(message, args) |
Yes | Information | Verbose operational details; suppressed unless debugging |
LogInformation(message, args) |
No | Information | Always-visible operational milestones and status updates |
LogWarning(message, args) |
No | Warning | Potential issues that don't prevent operation |
LogError(message, args) |
No | Error | Failures that need immediate attention |
LogError(exception, message, args) |
No | Error | Exception details always captured |
DiagnosticsEnabled property |
— | — | Check if diagnostics are enabled before expensive operations |
Console logging is automatically enabled when you call EnableDiagnostics():
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableDiagnostics() // Enables console logging
.AddMudBlazor();You can also enable it independently:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableConsoleLogging(true) // Enable console without diagnostics
.AddMudBlazor();Or disable it explicitly:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableConsoleLogging(false) // Disable console output
.AddMudBlazor();When console logging is enabled, the logging framework minimum level is adjusted:
| Setting | Minimum Log Level |
|---|---|
| Console logging disabled | Information |
| Console logging enabled | Debug |
This ensures diagnostic messages (Debug level) are visible in the console when logging is enabled.
Production (Console logging disabled):
- Only Information, Warning, and Error messages appear
- Diagnostic and Verbose messages are suppressed
- Clean, concise output
Development with Diagnostics (Console logging enabled):
- Debug, Information, Warning, and Error messages appear
- Full diagnostic traces visible
- Verbose troubleshooting information available
Three key options control debugging capabilities:
| Option | Default | Description |
|---|---|---|
EnableConsoleLogging |
false |
Show console window for log output |
EnableDevTools |
false |
Enable F12 browser developer tools |
EnableContextMenu |
true |
Enable right-click context menu |
Controls whether a console window is visible for logging output:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableConsoleLogging(true) // Show console window
.AddMudBlazor();Behavior when enabled:
- Console window is displayed for logging output
- If launched from Windows Explorer (no parent console), a new console window is automatically allocated
- Photino WebView logging is set to verbose (level 2)
- All
Console.WriteLineand framework logs are visible
Behavior when disabled (default):
- Console window is hidden for a native desktop app feel
- Standard output/error are redirected to null
- Photino WebView logging is set to critical only (level 0)
Enable browser developer tools for debugging JavaScript, inspecting the DOM, and monitoring network traffic:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableDevTools(true) // Enable developer tools in webview
.AddMudBlazor();How to access DevTools:
- Press F12 to open/close DevTools
- Right-click → "Inspect" (requires
EnableContextMenu = true)
What you can do with DevTools:
-
Console tab: View JavaScript errors, warnings, and
console.logoutput - Network tab: Monitor SignalR WebSocket connections and HTTP requests
- Elements tab: Inspect and modify the DOM in real-time
- Sources tab: Debug JavaScript with breakpoints
Controls whether right-click shows the browser context menu:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableContextMenu(true) // Enable (default)
.EnableContextMenu(false) // Disable for cleaner native feel
.AddMudBlazor();When enabled (default):
- Right-click shows browser menu (copy, paste, inspect, etc.)
- Required for accessing DevTools via right-click → "Inspect"
When disabled:
- Right-click does nothing
- Creates a cleaner native app experience
- DevTools still accessible via F12 if enabled
When running under Visual Studio with a debugger attached:
- Open Debug > Windows > Output
- Select Debug from the "Show output from" dropdown
- Application logs appear in real-time as the app runs
Symptoms: Application launches but Blazor server never initializes; window remains blank
Debugging Steps:
- Enable diagnostics to see startup sequence:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableDiagnostics()
.AddMudBlazor();-
Check console output for messages like:
- "Blazor host started successfully" → Server is running
- "Failed to start Blazor host" → Check the exception details
- "Waiting for Blazor server to respond" → Port may be in use
-
Verify port configuration:
var builder = new HostBuilder()
.WithTitle("My Application")
.UsePort(5000) // Explicitly set port
.EnableDiagnostics()
.AddMudBlazor();- Check that another application isn't using the port:
# Windows - find what's using port 5000
netstat -ano | findstr :5000- If startup is timing out, increase the timeout:
var builder = new HostBuilder()
.WithTitle("My Application")
.ConfigureOptions(options =>
{
options.StartupTimeout = TimeSpan.FromSeconds(30); // Increase from default 15 seconds
options.MaxStartupRetries = 5; // Increase retry attempts
})
.EnableDiagnostics()
.AddMudBlazor();Symptoms: File dialogs open but don't respond to user interaction
Debugging Steps:
- Enable diagnostics to track bridge initialization:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableDiagnostics()
.AddMudBlazor();-
Check for "JavaScript bridge ready" message in console—if missing, the bridge failed to initialize
-
Verify custom JavaScript doesn't interfere:
var builder = new HostBuilder()
.WithTitle("My Application")
.ConfigureOptions(options =>
{
// Temporarily comment out custom JS
// options.CustomJavaScript = "...";
})
.EnableDiagnostics()
.AddMudBlazor();- Check browser permissions are enabled:
var builder = new HostBuilder()
.WithTitle("My Application")
.ConfigureOptions(options =>
{
options.GrantBrowserPermissions = true;
})
.EnableDiagnostics()
.AddMudBlazor();- Monitor the Network tab in Developer Tools—look for failed requests related to file operations
Symptoms: JS interop calls timeout or return undefined
Debugging Steps:
- Enable diagnostics and dev tools:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableDiagnostics()
.EnableDevTools(true)
.AddMudBlazor();-
Check the browser console (Developer Tools) for JavaScript errors
-
Verify the JavaScript function is defined:
// In browser console, check if your function exists
typeof window.myFunction // Should return "function"- Increase the JSInterop timeout:
var builder = new HostBuilder()
.WithTitle("My Application")
.ConfigureOptions(options =>
{
options.CustomStaticFileOptions = new StaticFileOptions
{
// Configure as needed
};
})
.AddMudBlazor();
// Or through Blazor Host Configuration
builder.Services.Configure<CircuitOptions>(options =>
{
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(60); // Default is 1 minute
});- Check SignalR circuit connection in Network tab—verify WebSocket is established and messages flow
Symptoms: Application is sluggish or takes a long time to load
Debugging Steps:
- Enable diagnostics to see timing information:
var builder = new HostBuilder()
.WithTitle("My Application")
.EnableDiagnostics()
.AddMudBlazor();-
Check startup logs for which phase is slow:
- "Blazor host started successfully" → Server initialization
- "JavaScript bridge ready" → Bridge establishment
- Render events in Developer Tools Network tab → Component rendering
-
Profile rendering in browser Developer Tools:
- Open Performance tab
- Record during app interaction
- Look for long tasks or rendering bottlenecks
- Check for excessive network requests
-
Monitor Blazor circuit health:
var builder = new HostBuilder()
.WithTitle("My Application")
.ConfigureOptions(options =>
{
// Increase circuit retention to prevent disconnections
options.MaximumReceiveMessageSize = 100 * 1024 * 1024; // 100MB for large transfers
})
.EnableDiagnostics()
.AddMudBlazor();-
Check for excessive logging—verbose diagnostics can impact performance:
- Only enable
LogDiagnosticandLogVerboseduring active debugging - Use conditional checks with
DiagnosticsEnabledproperty
- Only enable
-
Review component rendering—check for unnecessary re-renders in Developer Tools
info: CheapAvaloniaBlazor.Hosting.HostBuilder[0]
Created BlazorHostWindow with title 'My Application' at 1024x768
dbug: CheapAvaloniaBlazor.Services.EmbeddedBlazorHostService[0]
Configuring Blazor server on port 5000 (HTTP)
dbug: CheapAvaloniaBlazor.Services.EmbeddedBlazorHostService[0]
Service registration summary:
- IBlazorHostService registered
- IDiagnosticLoggerFactory registered
- Mudblazor services configured
info: CheapAvaloniaBlazor.Hosting.HostBuilder[0]
Blazor host started successfully
dbug: CheapAvaloniaBlazor.Services.DiagnosticLogger[0]
User action: Opening file dialog
dbug: CheapAvaloniaBlazor.Services.DiagnosticLogger[0]
File dialog showing: Filter for *.txt files
dbug: CheapAvaloniaBlazor.Services.DiagnosticLogger[0]
Selected file: C:\Users\User\Documents\sample.txt
info: CheapAvaloniaBlazor.Services.DiagnosticLogger[0]
File operation completed successfully
dbug: CheapAvaloniaBlazor.Services.EmbeddedBlazorHostService[0]
Attempting to start Blazor server (attempt 1 of 3)
dbug: CheapAvaloniaBlazor.Services.EmbeddedBlazorHostService[0]
Server startup in progress, waiting for readiness...
fail: CheapAvaloniaBlazor.Hosting.HostBuilder[0]
Failed to start Blazor host
System.Net.HttpRequestException: Port 5000 is already in use
-
Enable Diagnostics During Development
var builder = new HostBuilder() .WithTitle("My Application") .EnableDiagnostics() // Always enable during development .AddMudBlazor();
-
Use Conditional Diagnostics for Expensive Operations
if (_logger.DiagnosticsEnabled) { // Only perform expensive diagnostic work when enabled var diagnosticData = GatherDetailedDiagnostics(); _logger.LogVerbose("Diagnostic data: {Data}", diagnosticData); }
-
Preserve Error Context
try { // risky operation } catch (Exception ex) { // Always log with exception details _logger.LogError(ex, "Operation failed: {Operation}", operationName); }
-
Use Appropriate Log Levels
-
LogDiagnostic(): Step-by-step traces for debugging -
LogVerbose(): Operational progress only visible with diagnostics -
LogInformation(): Normal operational events always visible -
LogWarning(): Potential issues that don't prevent operation -
LogError(): Failures requiring attention
-
-
Disable Diagnostics in Production
var builder = new HostBuilder() .WithTitle("My Application") // Do NOT call .EnableDiagnostics() .EnableConsoleLogging(false) // Disable console output .AddMudBlazor();
-
Keep Console Logging Disabled
- Reduces memory overhead
- Cleaner log output in monitoring systems
- Slightly improved performance
-
Enable Developer Tools Selectively
var builder = new HostBuilder() .WithTitle("My Application") #if DEBUG .EnableDevTools(true) #else .EnableDevTools(false) #endif .AddMudBlazor();
-
Monitor Real Issues
- Configure external logging (Application Insights, Serilog, etc.)
- Only log warnings and errors in production
- Use Information level for important milestones only
-
Testing Before Deployment
// Test with diagnostics disabled var builder = new HostBuilder() .WithTitle("My Application") .EnableConsoleLogging(false) .ConfigureOptions(options => { options.EnableDiagnostics = false; options.EnableDevTools = false; }) .AddMudBlazor();
-
Reproduce the Issue
- Document exact steps to reproduce
- Note error messages and timestamps
-
Enable Maximum Diagnostics
var builder = new HostBuilder() .WithTitle("My Application") .EnableDiagnostics() .EnableDevTools(true) .AddMudBlazor();
-
Gather Logs
- Console output with timestamps
- Browser DevTools console
- Network tab (especially SignalR)
- Application Insights or external monitoring
-
Isolate the Component
- Disable features one by one
- Narrow down which service/component fails
- Reproduce in minimal test case
-
Check Common Causes
- Port conflicts (netstat -ano | findstr :PORT)
- Network connectivity (especially for HTTPS)
- File/folder permissions
- Missing dependencies or services
- JavaScript interop mismatches
-
Clean and Rebuild
- Clear bin/obj folders
- Rebuild entire solution
- Restart Visual Studio/IDE
- Clear browser cache (Ctrl+Shift+Delete)
Build Errors
# Ensure correct .NET version
dotnet --version # Should be 10.0+
# Clear and restore packages
dotnet clean
dotnet restore
dotnet buildWindow Doesn't Appear
- Check if port 5000/5001 is available
- Verify no firewall blocking local connections
- Look for exceptions in console output
- Try different port:
builder.UsePort(8080)
MudBlazor Styles Missing
- Verify CSS reference in
App.razor:<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
- Check browser dev tools for 404 errors
- Ensure
AddMudBlazor()is called in HostBuilder
Black Screen / blazor.web.js 404
- Run
dotnet restoreto ensureMicrosoft.AspNetCore.App.Internal.Assetsis in the NuGet cache - If using
Sdk.Razor: the library extractsblazor.web.jsat runtime from the NuGet cache — check startup logs for extraction messages - If using
Sdk.Web: verifyMapStaticAssets()is in the pipeline (called byUseCheapBlazorDesktop()) - Clear the WebView2 cache if you see stale behavior: delete
%LocalAppData%\Photino\EBWebView\Default\Cache\
Platform Compatibility Issues
- Linux/macOS: Currently untested - if you encounter issues, please report them!
- Windows: Fully tested and supported
- Dependencies (Avalonia, Photino) should work cross-platform, but integration not verified
File Dialog Not Working
- File dialogs use Avalonia StorageProvider (working since v1.0.67)
- Ensure you're using latest version:
dotnet add package CheapAvaloniaBlazor - Check
IDesktopInteropServiceinjection
Taskbar Icon Stays Visible When Minimized to Tray
- This happens when
EnableDevToolsis true - the DevTools window keeps the taskbar icon alive - This is a Photino/WebView2 limitation, not a CheapAvaloniaBlazor bug
- Disable
EnableDevToolsfor production or when testing tray behavior
var builder = new HostBuilder()
.WithTitle("My App")
.EnableConsoleLogging() // Show console window for logging
.EnableDevTools() // Enable F12 developer tools
.EnableContextMenu() // Enable right-click menu (default: true)
.EnableDiagnostics() // Comprehensive diagnostic logging
.AddMudBlazor();