Logging with NLog in Xamarin Forms

This post is a quick demo of how to add logging to a Xamarin Forms application using the NLog library. The complete source code is available here.

NLog is a popular logging library for .net and it’s compatible for Xamarin so it’s a natural choice if you want to add logging to your Xamarin app. It isn’t PCL compatible though, so you do need an implementation for each platform. This isn’t a major problem, and you might want to set the logger up differently for each platform anyway.

Let’s dive into the code…

using System;
namespace LoggingDemo
{
    public interface ILogger
    {
        void Trace(string text, params object[] args);
        void Debug(string text, params object[] args);
        void Info(string text, params object[] args);
        void Warn(string text, params object[] args);
        void Error(string text, params object[] args);
        void Fatal(string text, params object[] args);
    }
}
using System;
using System.Diagnostics.Contracts;
namespace LoggingDemo
{
    public interface ILogManager
    {
        ILogger GetLog([System.Runtime.CompilerServices.CallerFilePath]string callerFilePath = "");
    }
}

In the cross platform project we define a couple of interfaces, ILogManager and ILogger. ILogManager is responsible for creating instances of the ILogger class. ILogger contains the logging message.

In this demo the code for the IOS and Android implementations of the interfaces is pretty much the same. Let’s look at the IOS version…

using NLog;
using NLog.Config;
using NLog.Targets;
using Foundation;
using LoggingDemo.iOS;
using Xamarin.Forms;

[assembly: Dependency(typeof(NLogManager))]
namespace LoggingDemo.iOS
{
    public class NLogManager : ILogManager
    {
        public NLogManager()
        {
            var config = new LoggingConfiguration();

            var consoleTarget = new ConsoleTarget();
            config.AddTarget("console", consoleTarget);

            var consoleRule = new LoggingRule("*", LogLevel.Trace, consoleTarget);
            config.LoggingRules.Add(consoleRule);

            var fileTarget = new FileTarget();
            string folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            fileTarget.FileName = Path.Combine(folder, "Log.txt");
            config.AddTarget("file", fileTarget);

            var fileRule = new LoggingRule("*", LogLevel.Warn, fileTarget);
            config.LoggingRules.Add(fileRule);

            LogManager.Configuration = config;
        }

        public ILogger GetLog([System.Runtime.CompilerServices.CallerFilePath] string callerFilePath = "")
        {
            string fileName = callerFilePath;

            if (fileName.Contains("/"))
            {
                fileName = fileName.Substring(fileName.LastIndexOf("/", StringComparison.CurrentCultureIgnoreCase) + 1);
            }

            var logger = LogManager.GetLogger(fileName);
            return new NLogLogger(logger);  
        }
    }
}

The NLogManager configures the logging in its constructor. Here we are logging all log entries to the console and logs of warning or above into a file called log.txt. The GetLog function returns an instance of a logger. It uses the CallerFilePath attribute to set the name of the log entries.

using System;
using System.Diagnostics.Contracts;
namespace LoggingDemo
{
    public interface ILogManager
    {
        ILogger GetLog([System.Runtime.CompilerServices.CallerFilePath]string callerFilePath = "");
    }
}

The implementation of ILogger is really simple. It just gives us a bunch of functions to log messages of various levels.

In both of these classes we use the Dependency attribute so that the shared project knows about it.

Now let’s look at using the loggers. The demo app has one screen with buttons which create log entries.

screen-shot-2016-10-16-at-21-40-45

using System;
using System.Collections.Generic;

using Xamarin.Forms;

namespace LoggingDemo
{
    public partial class MainPage : ContentPage
    {
        private static ILogger logger = DependencyService.Get<ILogManager>().GetLog();

        public MainPage()
        {
            InitializeComponent();
        }

        private void Trace_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Trace Clicked");
        }

        private void Debug_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Debug Clicked");
        }

        private void Info_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Info Clicked");
        }

        private void Warn_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Warn Clicked");
        }

        private void Error_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Error Clicked");
        }

        private void Fatal_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Fatal Clicked");
        }
    }
}

We set the logger up as a static variable of the class. Also, the dependency service will create one global static interface of the log manager (this is default behaviour and is easy to forget).

And just to prove it works, here are some log messages appearing in the console…

screen-shot-2016-10-16-at-21-41-15

 

Logging with NLog in Xamarin Forms

7 thoughts on “Logging with NLog in Xamarin Forms

  1. Chris says:

    Hi I am playing with your example and the logs do not seem to be writing to the file system! I am focusing on the android target at the mo, any idea???

    Device – Samsung SM-T535 (Android 5 – API 21)

    Thanks,

    Chris.

    Like

    1. martynnw says:

      This is probably due to a bug in MainPage.xaml.cs – it was only logging traces regardless of button. I’ve fixed it so it should work now.

      Like

  2. Chris says:

    Hi Marty,

    Sorry for the delayed response. I have just tried your latest commit, but there is still nothing logged to the file system. Does the file get created successfully when you try?

    Thanks,

    Chris.

    Like

    1. martynnw says:

      Yes it’s working now. The file is at /data/user/0/com.martynnw.loggingdemo/files.

      You could check if it’s created with the following commands at an adb command prompt:
      adb shell
      cd /data/user/0/com.martynnw.loggingdemo/files
      ls
      And to view the contents of the file…
      cat Log.txt

      Like

  3. Chris says:

    I finally worked out what it was, the problem was that the directory did exist on the device, doh.

    I tweaked your code along the lines of –

    if (Directory.Exists(dir))
    fileTarget.FileName = Path.Combine(dir, filename);
    else // Create the directory if it doesn’t already exist…
    fileTarget.FileName = Path.Combine(Directory.CreateDirectory(dir).FullName, filename);

    …and tada, I have a log file.

    Cheers,

    Chris.

    Liked by 1 person

Leave a comment