1

I am trying to figure out how to get Serilog to log the class name and method/line number, like I would see with log4cxx in C++.

I tried my best to grab all the relevant bits out of the real code I am working on and come up with a minimal example.

I've also been Googling Serilog left and right, but I am not finding good documentation. I suppose it is because there are so many libraries on top of the base serilog and each needs there own docs to tell me how to do things.

I can see the basics on configuration at https://github.com/serilog/serilog/wiki/Configuration-Basics , but this seems to use a TextWriter sink from a seperate Serilog library and a custom formatter, both of which I don't really understand.

I can also find examples on stack overflow that use the simple configuatation and the enrich call to log the class and method names.

C# ASP.NET Core Serilog add class name and method to log

I am not able to get it to log them. How can I get this to log the class and method name or line number while still using the custom formatter and TextWriter?

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Serilog;
using Serilog.Events;
using Serilog.Formatting;

namespace SerilogDemo {

    // Someone else made this, I just changed the name to protect the innocent
    public class SomeonesLogTextFormatter : ITextFormatter
    {
        public void Format(LogEvent logEvent, TextWriter output)
        {
            output.Write(logEvent.Level);
            output.Write(": ");
            logEvent.MessageTemplate.Render(logEvent.Properties, output);
            output.WriteLine();

            if (logEvent.Exception != null)
            {
                output.WriteLine(logEvent.Exception);
            }
        }
    }

    public class SomeClass
    {
        private Serilog.ILogger _log = Serilog.Log.ForContext<SomeClass>();

        public SomeClass()
        {
            _log.Debug("SomeClass has been instantiated");
        }

        public void Foo()
        {
            _log.Debug("Foo has been called");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {

            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .Enrich.FromLogContext()
                .WriteTo.TextWriter(textWriter: Console.Out, formatter: new SomeonesLogTextFormatter())
                .CreateLogger();

            var poop = new SomeClass();
            poop.Foo();
        }
    }
}
Christopher Pisz
  • 3,757
  • 4
  • 29
  • 65

1 Answers1

1

The Serilog way of adding more information to a log message is by adding properties to the LogContext either manually or by using an Enricher that does that for you. Read more about Serilog Enrichment.

Serilog by default does not capture that information, and that can be quite expensive if you do for every single message, but the way to do it is by using C#'s Caller Information feature such as CallerMemberName, CallerFilePath, CallerLineNumber.

Here is an example, also copied below:

public static class SerilogWithCallerAttributes
{
    public static void Main()
    {
        Serilog.Log.Logger = new LoggerConfiguration()
            .WriteTo.ColoredConsole()
            .CreateLogger();

        GoDoSomething();
    }

    public static void GoDoSomething()
    {
        int score = 12;

        Log.Information("Player scored: {Score}", CallerInfo.Create(), score);
    }
}

public static class Log
{
    public static void Information(string messageTemplate, CallerInfo callerInfo, params object[] propertyValues)
    {
        Serilog.Log.Logger
            .ForHere(callerInfo.CallerFilePath, callerInfo.CallerMemberName, callerInfo.CallerLineNumber)
            .Information(messageTemplate, propertyValues);
    }
}

public static class LoggerExtensions
{
    public static ILogger ForHere(
        this ILogger logger,
        [CallerFilePath] string callerFilePath = null,
        [CallerMemberName] string callerMemberName = null,
        [CallerLineNumber] int callerLineNumber = 0)
    {
        return logger
            .ForContext("SourceFile", callerFilePath)
            .ForContext("SourceMember", callerMemberName)
            .ForContext("SourceLine", callerLineNumber);
    }
}

public class CallerInfo
{
    public string CallerFilePath { get; private set; }

    public string CallerMemberName { get; private set; }

    public int CallerLineNumber { get; private set; }

    private CallerInfo(string callerFilePath, string callerMemberName, int callerLineNumber)
    {
        this.CallerFilePath = callerFilePath;
        this.CallerMemberName = callerMemberName;
        this.CallerLineNumber = callerLineNumber;
    }

    public static CallerInfo Create(
        [CallerFilePath] string callerFilePath = null,
        [CallerMemberName] string callerMemberName = null,
        [CallerLineNumber] int callerLineNumber = 0)
    {
        return new CallerInfo(callerFilePath, callerMemberName, callerLineNumber);
    }
}
C. Augusto Proiete
  • 24,684
  • 2
  • 63
  • 91