Logging on .net MAUI
Asked Answered
L

4

19

.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?

Lurleen answered 24/3, 2022 at 9:31 Comment(0)
B
19

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

    }
);
Belle answered 2/4, 2022 at 7:11 Comment(0)
S
3

There is a package taylor made for logging in MAUI: https://github.com/roubachof/MetroLog

Including:

  • Share logs as a zip file
  • Displaying logs in a page
  • Shake the device to display logs

And you can find a tutorial by Gerald here: https://www.youtube.com/watch?v=WicmnH72kf0

Schilt answered 12/12, 2022 at 9:14 Comment(2)
It’s a .net6 library so it’s compatible with .net7 (it’s has been tested)Schilt
@Schilt I can confirm it works fine with .Net 7 bindings if anybody is checking this answer out today and I would highly recommend this Plugin.Centripetal
L
0

NLog.Targets.MauiLog enables debug logging on the various platforms:

  • Apple iOS / MacOS - Unified Logging
  • Android - Android.Util.Log / LogCat
  • NetStandard - System.Diagnostics.Debugger.Log
Leora answered 18/9, 2022 at 9:30 Comment(2)
Doesn't seem to work (atm) for net7 bindings.Atrophy
@Atrophy You are wellcome to create an issue for the NLog.Targets.MauiLog-repository. And if you know what is missing, then pull-requests are great.Leora
G
-1

I used MetroLog.Maui here is a video I recorded https://youtu.be/8CPXDHQlBa0

In a Utility class

private static ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddStreamingFileLogger(options =>
    {
        options.RetainDays = 2;               
        options.FolderPath = Path.Combine(FileSystem.CacheDirectory, "InfoBoardLogs");
    }));

    public static ILogger Logger(string categoryName) {
        return loggerFactory.CreateLogger(categoryName);
    }
//Usege
using Microsoft.Extensions.Logging;
private readonly ILogger _logger;

_logger = Utilities.Logger(nameof(ImageDisplay));
_logger.LogInformation("\n++++++++++++++ ImageDisplay Constructor");
Gautier answered 2/7, 2023 at 21:10 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.