logginglog4netnlogmaui

Logging on .net MAUI


.net MAUI recently removed logging in one of it's newest release. What is the alternative now and how should it be implemented? Have been going all over online, but couldn't find a single example of any logging architecture implemented. Tried log4net, NLog, but did not manage to set any of them up at the end. There is 0 examples online for setting it up any logging on MAUI.

Also, saw the builder.Services.AddLogging() and builder.Logging.Services in MauiProgram which should work with dependency injection, but can't find any Maui example for that implementation too.

How one should set up a basic logging in MAUI now?


Solution

  • Start by adding a reference to Microsoft.Extensions.Logging.Debug. You could do it like this if you only want it in debug mode:

    <ItemGroup Condition="'$(Configuration)' == 'Debug'">
        <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
    </ItemGroup>
    

    Then when the application starts:

    var builder = MauiApp.CreateBuilder();
    // ...
    #if DEBUG
    builder.Services.AddLogging(
        configure =>
        {
            configure.AddDebug();
        }
    );
    #endif
    

    You could also add filters:

    #if DEBUG
    builder.Services.AddLogging(
        configure =>
        {
            configure
                .AddDebug()
                .AddFilter("MyCompany.MyApp.Namespace", LogLevel.Trace)
                .AddFilter("Microsoft", LogLevel.Warning);
        }
    );
    #endif
    

    Android logging (Logcat)

    If you want to use the native logging support on Android, an easy fix for this is to add a reference to Microsoft.Extensions.Logging.Console and then configure it:

    builder.Services.AddLogging(
        configure =>
        {
            configure.AddDebug();
            configure.AddConsole();
        }
    );
    

    While this works, the logs are a bit hard to read. A better way is to use a custom logging provider that wraps the native logging support. In the Android folder, add this code:

        using Microsoft.Extensions.Logging;
    
        namespace MyMauiApp;
    
        public class AndroidLoggerProvider : ILoggerProvider
        {
            public AndroidLoggerProvider()
            {
            }
    
            public ILogger CreateLogger(string categoryName)
            {
                // Category name is often the full class name, like
                // MyApp.ViewModel.MyViewModel
                // This removes the namespace:
                int lastDotPos = categoryName.LastIndexOf('.');
                if (lastDotPos > 0)
                {
                    categoryName = categoryName.Substring(lastDotPos + 1);
                }
            
                return new AndroidLogger(categoryName);
            }
    
            public void Dispose() { }
        }
    
        public class AndroidLogger : ILogger
        {
            private readonly string Category;
    
            public IDisposable BeginScope<TState>(TState state) => null!;
    
            public bool IsEnabled(LogLevel logLevel) => true;
    
            public AndroidLogger(string category)
            {
                Category = category;
            }
    
            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
            {
                string message = formatter(state, exception);
    
                Java.Lang.Throwable? throwable = null;
    
                if (exception is not null)
                {
                    throwable = Java.Lang.Throwable.FromException(exception);
                }
    
                switch (logLevel)
                {
                    case LogLevel.Trace:
                        Android.Util.Log.Verbose(Category, throwable, message);
                        break;
    
                    case LogLevel.Debug:
                        Android.Util.Log.Debug(Category, throwable, message);
                        break;
    
                    case LogLevel.Information:
                        Android.Util.Log.Info(Category, throwable, message);
                        break;
    
                    case LogLevel.Warning:
                        Android.Util.Log.Warn(Category, throwable, message);
                        break;
    
                    case LogLevel.Error:
                        Android.Util.Log.Error(Category, throwable, message);
                        break;
    
                    case LogLevel.Critical:
                        Android.Util.Log.Wtf(Category, throwable, message);
                        break;
                }
            }
        }
    

    And then you configure it like this:

    builder.Services.AddLogging(
        configure =>
        {
            // You don't need the debug logger on Android if you use AndroidLoggerProvider.
            // configure.AddDebug();
    
    #if ANDROID
    #if DEBUG
            LogLevel androidLogLevel = LogLevel.Debug;
    #else
            LogLevel androidLogLevel = LogLevel.Information;
    #endif
    
            configure
                .AddProvider(new AndroidLoggerProvider())
                .AddFilter("MyMauiApp", androidLogLevel);
    #endif
    
        }
    );