Skip to content

diagnostics

github-actions[bot] edited this page Jul 1, 2026 · 1 revision

Diagnostics & Debugging Guide

Overview

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.


Enabling Diagnostics

Via HostBuilder (Recommended)

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 = true in options
  • Automatically enables console logging
  • Logging minimum level adjusted to Debug
  • All diagnostic log methods will output messages

Alias Method

An alternative alias method is available:

var builder = new HostBuilder()
    .WithTitle("My Application")
    .WithDiagnostics()  // Alias for EnableDiagnostics()
    .AddMudBlazor();

builder.RunApp(args);

Independent Console Logging

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);

Via Configuration Options

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);

Diagnostic Features

What Gets Logged

When diagnostics are enabled, the system automatically tracks:

Service Registration

  • Summary of registered services
  • Dependency injection container status
  • Custom service configuration details

Blazor Server Startup

  • Server initialization timestamp
  • Port and protocol configuration (HTTP/HTTPS)
  • Content root and web root paths
  • Static web assets loading
  • Circuit initialization and configuration

Window Initialization

  • Window creation with type name
  • Title, width, and height settings
  • Window startup location (centered vs. manual positioning)
  • Icon loading success/failure
  • Avalonia platform configuration

JavaScript Bridge Status

  • Bridge initialization status
  • Ready state confirmation
  • Interop timeout configuration
  • Message channel establishment

File Dialog Operations

  • Dialog open/save operations initiated
  • File selection results
  • Dialog cancellation events
  • Path information and file counts

Performance Timing

  • Service initialization duration
  • Blazor server startup time
  • Window creation latency
  • Bridge readiness time

DiagnosticLogger Service

Overview

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.

Dependency Injection

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.

Usage Examples

Basic Diagnostic Logging

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");
    }
}

Conditional Logging Based on Diagnostics Flag

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");
    }
}

Mixed Logging Levels

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");
        }
    }
}

Log Levels & Behavior

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

Enabling Console Output

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();

Log Level Configuration

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.

Output Behavior

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

Development Tools

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

Console Logging

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.WriteLine and 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)

Browser Developer Tools

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.log output
  • 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

Context Menu

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

Debug Output Window (Visual Studio)

When running under Visual Studio with a debugger attached:

  1. Open Debug > Windows > Output
  2. Select Debug from the "Show output from" dropdown
  3. Application logs appear in real-time as the app runs

Common Debugging Scenarios

Scenario 1: Blazor Won't Start

Symptoms: Application launches but Blazor server never initializes; window remains blank

Debugging Steps:

  1. Enable diagnostics to see startup sequence:
var builder = new HostBuilder()
    .WithTitle("My Application")
    .EnableDiagnostics()
    .AddMudBlazor();
  1. 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
  2. Verify port configuration:

var builder = new HostBuilder()
    .WithTitle("My Application")
    .UsePort(5000)  // Explicitly set port
    .EnableDiagnostics()
    .AddMudBlazor();
  1. Check that another application isn't using the port:
# Windows - find what's using port 5000
netstat -ano | findstr :5000
  1. 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();

Scenario 2: File Dialogs Not Working

Symptoms: File dialogs open but don't respond to user interaction

Debugging Steps:

  1. Enable diagnostics to track bridge initialization:
var builder = new HostBuilder()
    .WithTitle("My Application")
    .EnableDiagnostics()
    .AddMudBlazor();
  1. Check for "JavaScript bridge ready" message in console—if missing, the bridge failed to initialize

  2. Verify custom JavaScript doesn't interfere:

var builder = new HostBuilder()
    .WithTitle("My Application")
    .ConfigureOptions(options =>
    {
        // Temporarily comment out custom JS
        // options.CustomJavaScript = "...";
    })
    .EnableDiagnostics()
    .AddMudBlazor();
  1. Check browser permissions are enabled:
var builder = new HostBuilder()
    .WithTitle("My Application")
    .ConfigureOptions(options =>
    {
        options.GrantBrowserPermissions = true;
    })
    .EnableDiagnostics()
    .AddMudBlazor();
  1. Monitor the Network tab in Developer Tools—look for failed requests related to file operations

Scenario 3: JavaScript Interop Failures

Symptoms: JS interop calls timeout or return undefined

Debugging Steps:

  1. Enable diagnostics and dev tools:
var builder = new HostBuilder()
    .WithTitle("My Application")
    .EnableDiagnostics()
    .EnableDevTools(true)
    .AddMudBlazor();
  1. Check the browser console (Developer Tools) for JavaScript errors

  2. Verify the JavaScript function is defined:

// In browser console, check if your function exists
typeof window.myFunction  // Should return "function"
  1. 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
});
  1. Check SignalR circuit connection in Network tab—verify WebSocket is established and messages flow

Scenario 4: Performance Problems

Symptoms: Application is sluggish or takes a long time to load

Debugging Steps:

  1. Enable diagnostics to see timing information:
var builder = new HostBuilder()
    .WithTitle("My Application")
    .EnableDiagnostics()
    .AddMudBlazor();
  1. 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
  2. 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
  3. 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();
  1. Check for excessive logging—verbose diagnostics can impact performance:

    • Only enable LogDiagnostic and LogVerbose during active debugging
    • Use conditional checks with DiagnosticsEnabled property
  2. Review component rendering—check for unnecessary re-renders in Developer Tools


Log Output Examples

Successful Startup Sequence

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

Diagnostic Trace During Operation

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

Error with Diagnostics

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

Best Practices

Development Guidelines

  1. Enable Diagnostics During Development

    var builder = new HostBuilder()
        .WithTitle("My Application")
        .EnableDiagnostics()  // Always enable during development
        .AddMudBlazor();
  2. 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);
    }
  3. Preserve Error Context

    try
    {
        // risky operation
    }
    catch (Exception ex)
    {
        // Always log with exception details
        _logger.LogError(ex, "Operation failed: {Operation}", operationName);
    }
  4. 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

Production Guidelines

  1. Disable Diagnostics in Production

    var builder = new HostBuilder()
        .WithTitle("My Application")
        // Do NOT call .EnableDiagnostics()
        .EnableConsoleLogging(false)  // Disable console output
        .AddMudBlazor();
  2. Keep Console Logging Disabled

    • Reduces memory overhead
    • Cleaner log output in monitoring systems
    • Slightly improved performance
  3. Enable Developer Tools Selectively

    var builder = new HostBuilder()
        .WithTitle("My Application")
        #if DEBUG
            .EnableDevTools(true)
        #else
            .EnableDevTools(false)
        #endif
        .AddMudBlazor();
  4. Monitor Real Issues

    • Configure external logging (Application Insights, Serilog, etc.)
    • Only log warnings and errors in production
    • Use Information level for important milestones only
  5. 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();

Troubleshooting Workflow

  1. Reproduce the Issue

    • Document exact steps to reproduce
    • Note error messages and timestamps
  2. Enable Maximum Diagnostics

    var builder = new HostBuilder()
        .WithTitle("My Application")
        .EnableDiagnostics()
        .EnableDevTools(true)
        .AddMudBlazor();
  3. Gather Logs

    • Console output with timestamps
    • Browser DevTools console
    • Network tab (especially SignalR)
    • Application Insights or external monitoring
  4. Isolate the Component

    • Disable features one by one
    • Narrow down which service/component fails
    • Reproduce in minimal test case
  5. Check Common Causes

    • Port conflicts (netstat -ano | findstr :PORT)
    • Network connectivity (especially for HTTPS)
    • File/folder permissions
    • Missing dependencies or services
    • JavaScript interop mismatches
  6. Clean and Rebuild

    • Clear bin/obj folders
    • Rebuild entire solution
    • Restart Visual Studio/IDE
    • Clear browser cache (Ctrl+Shift+Delete)

Common Issues & Solutions

Build Errors

# Ensure correct .NET version
dotnet --version  # Should be 10.0+

# Clear and restore packages
dotnet clean
dotnet restore
dotnet build

Window 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 restore to ensure Microsoft.AspNetCore.App.Internal.Assets is in the NuGet cache
  • If using Sdk.Razor: the library extracts blazor.web.js at runtime from the NuGet cache — check startup logs for extraction messages
  • If using Sdk.Web: verify MapStaticAssets() is in the pipeline (called by UseCheapBlazorDesktop())
  • 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 IDesktopInteropService injection

Taskbar Icon Stays Visible When Minimized to Tray

  • This happens when EnableDevTools is true - the DevTools window keeps the taskbar icon alive
  • This is a Photino/WebView2 limitation, not a CheapAvaloniaBlazor bug
  • Disable EnableDevTools for production or when testing tray behavior

Debug Mode

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();

Related Resources

Clone this wiki locally