36

Is there anyway to enrich all Serilog output with the Method Name.

For Instance consider If I have the following;

Public Class MyClassName

  Private Function MyFunctionName() As Boolean
      Logger.Information("Hello World")
      Return True
  End Function

End Class

The desired output would be as follows;

2015-04-06 18:41:35.361 +10:00 [Information] [MyFunctionName] Hello World!

Actually the Fully Qualified Name would be good.

2015-04-06 18:41:35.361 +10:00 [Information] [MyClassName.MyFunctionName] Hello World!

It seems that "Enrichers" are only good for Static Information and won't work each time.

Nasreddine
  • 36,610
  • 17
  • 75
  • 94
Aaron Glover
  • 1,199
  • 2
  • 13
  • 30

5 Answers5

40

in case you need a version in C#:

public static class LoggerExtensions
{
    public static ILogger Here(this ILogger logger,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0) {
        return logger
            .ForContext("MemberName", memberName)
            .ForContext("FilePath", sourceFilePath)
            .ForContext("LineNumber", sourceLineNumber);
    }
}

use like this:

// at the beginning of the class
private static Serilog.ILogger Log => Serilog.Log.ForContext<MyClass>();

// in the method
Log.Here().Information("Hello, world!");

Remember to add those properties in the message template. You can use something like this:

var outputTemplate = "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message}{NewLine}in method {MemberName} at {FilePath}:{LineNumber}{NewLine}{Exception}{NewLine}";

Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Warning()
            .Enrich.FromLogContext()
            .WriteTo.RollingFile("log/{Date}.log", outputTemplate, LogEventLevel.Warning)
            .WriteTo.Console(LogEventLevel.Warning, outputTemplate, theme: AnsiConsoleTheme.Literate)
            .CreateLogger();
MovGP0
  • 7,267
  • 3
  • 49
  • 42
  • 2
    Thanks very much for this. I posted a complete example based on your answer [here](https://stackoverflow.com/questions/48268854/how-to-get-formatted-output-from-logevent/48272467#48272467). –  Jan 16 '18 at 00:18
  • FYI... My use required I preface with `Log.`, like `Log.Logger.Here().Information("Hello, world!");` and don't forget to add `using` to extension namespace. – Adam Cox Mar 18 '20 at 19:33
  • I prefer to have something like this at the beginning of my class: `private static Serilog.ILogger Log => Serilog.Log.ForContext();` – MovGP0 Mar 18 '20 at 21:03
  • 1
    just for curiosity, can't be `Here()` method replaced by new *source generators* in .NET 5? – Jan Zahradník Nov 05 '20 at 20:55
  • 1
    I'm wondering if there is a way to make this work .NET Core's ILogger as opposed to Serilog's. You have access to BeginScope, but I can't really figure out an elegant way to implement it. – e36M3 Apr 08 '21 at 20:16
  • Any idea how to write this log context to Seq? – Thilina Koggalage Nov 12 '21 at 06:54
  • Serilog.Sinks.Seq https://github.com/datalust/serilog-sinks-seq – MovGP0 Nov 13 '21 at 15:01
  • Is there a way to do this without having to specify the `ForContext` in each class you need to use the logger? Can it be moved to an extension method or something similar? – Sach Nov 04 '22 at 22:33
  • @sach the `ForContext` annotates the logging data with the name of the class. There is missing a `[CallerClassName]` attribute in the .NET Framework that could be used in the logging methods. – MovGP0 Nov 07 '22 at 08:59
18

It's possible to do this with an enricher by reflecting over the call stack, but very expensive to do so, so Serilog doesn't offer it.

Instead you can use something like:

Logger.Here().Information("Hello, world!");

and implement the Here() method as an extension method on ILogger:

<Extension>
Public Sub Here(ByVal logger as ILogger,
    <CallerMemberName> Optional memberName As String = Nothing)

    Return logger.ForContext("MemberName", memberName)
End Sub 
Nicholas Blumhardt
  • 30,271
  • 4
  • 90
  • 101
  • 1
    OK this will work. But i do wonder just how much overhead this adds. .. it seems to be a fundamental feature of most logging libraries. It would be nice if this was just a builtin enricher or configuration option... turned off by default but if people are prepared to take the hit then they can turn it on. It would remove the requirement to write code constantly to call the extension method – Aaron Glover Apr 08 '15 at 11:02
  • There's now a sample of the enricher-based approach described at: https://github.com/serilog/serilog/issues/1084#issuecomment-358117004 – Nicholas Blumhardt Jan 17 '18 at 22:13
  • @NicholasBlumhardt Can you please provide your answer in C# with more details, like how to implement Here in Ilogger etc – Mohd Waseem Jan 23 '19 at 13:44
7

Based on MovGP0's answer (for C#),

I created a solution that doesn't require the Here()-Method in every line where you want to log, by simply adding a custom Log.cs-Class into the "root namespace" of a project.

For more info see: https://gist.github.com/litetex/b88fe0531e5acea82df1189643fb1f79

litetex
  • 193
  • 2
  • 8
  • 5
    ... so you basically created a wrapper for Serilog. Valid but most likely not every ones cup of tea. – ViRuSTriNiTy Nov 26 '20 at 11:59
  • 1
    Serilog works with message templates: [message-template-recommendations](https://github.com/serilog/serilog/wiki/Writing-Log-Events#message-template-recommendations) And with the above gist, you don't have that message template anymore as you can only pass a single string, which kinda forces you to do string interpolation, which is not recommended. – Jason Landbridge Feb 23 '23 at 19:39
0

In outputTemplate for the Serilog , configure for logs to write Properties. Method name will be written as part of ActionName column.

ActionName can also be configured individually (instead of all properties) in outputTemplate.

Configuring Properties/ActionName will write the method name in Namespace.ClassName.MethodName format.

tryingToLearn
  • 10,691
  • 12
  • 80
  • 114
Jaya B.
  • 1
  • 1
0

The version on C# could be simplified. Just use AutofacSerilogIntegration:

var path = Server.MapPath("~/");

        var outputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext} {Message} {NewLine}{Exception}";
        Log.Logger = new LoggerConfiguration()
                    .MinimumLevel.Debug()
                    .WriteTo.File($"{path}/log/serilog-.log", LogEventLevel.Debug, outputTemplate, rollingInterval: RollingInterval.Day)
                    .CreateLogger();

        var builder = new ContainerBuilder();
        builder.RegisterLogger();  // from AutofacSerilogIntegration
        builder.RegisterControllers(typeof(MvcApplication).Assembly);
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
Sergii Fasolko
  • 188
  • 1
  • 7