Skip to content

Instantly share code, notes, and snippets.

@litetex
Last active March 2, 2024 03:12
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save litetex/b88fe0531e5acea82df1189643fb1f79 to your computer and use it in GitHub Desktop.
Save litetex/b88fe0531e5acea82df1189643fb1f79 to your computer and use it in GitHub Desktop.
Serilog (C#): How to get current MethodName, FileName/Path and LineNumber without reflection

Serilog (C#): How to get the current MethodName, FileName/Path and LineNumber without reflection

This is a simple setup for reflectionless logging with serilog using caller information (and a single static class).

See also https://stackoverflow.com/a/46905798

Log.cs

Create your own Log.cs in your Root-Namespace (you can use the class below).

This class is required to detect where the call is coming from; it uses Caller-Information to speed up the program execution, because the attributes are resolved at compile-time.

You can now also remove all using Serilog;-imports - if you have any - in the classes where you want to log, because the Log.cs is everywhere in the namespace visble and easy accessible.

Serilog-Output Template

Now you can use the added properties (here: MemberName, FilePath, FileName, LineNumber) in your outputTemplate:

⚠️ Note: For more performance you can remove/uncomment unused properties, here e.g. FilePath and LineNumber in SetContext(...)

Final Example

OutputTemplate: {Timestamp:HH:mm:ss,fff} {Level:u3} {FileName} [{MemberName}] {Message:lj}{NewLine}{Exception}

namespace Demo
{
  public class TestClass
  {
    public void TestMethod()
    {
      Log.Info("Some test");
    }
  }
}

Produces the following output: 18:16:40,183 INFO TestClass [TestMethod] Some test

Changelog

  • 2020-03-21: Tuned docs a bit; updated and reformatted the Log.cs class
  • 2020-05-29: Tuned docs a bit; Reformatted the Log.cs; References to CoreFramework.Logging
// MIT License
// Copyright (c) 2020 litetex
// See also https://github.com/litetex/CoreFramework/blob/develop/CoreFramework.Logging/Log.cs
using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace CoreFramework
{
public static class Log
{
private static string FormatForException(this string message, Exception ex)
{
return $"{message}: {(ex != null ? ex.ToString() : "")}";
}
private static string FormatForContext(
this string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
var fileName = Path.GetFileNameWithoutExtension(sourceFilePath);
var methodName = memberName;
return $"{fileName} [{methodName}] {message}";
}
public static void Verbose(
string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Verbose(
message
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Verbose(
string message,
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Verbose(
message
.FormatForException(ex)
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Verbose(
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Verbose(
(ex != null ? ex.ToString() : "")
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Debug(
string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Debug(
message
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Debug(
string message,
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Debug(
message
.FormatForException(ex)
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Debug(
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Debug(
(ex != null ? ex.ToString() : "")
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Info(
string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Information(
message
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Info(
string message,
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Information(
message
.FormatForException(ex)
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Info(
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Information(
(ex != null ? ex.ToString() : "")
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Warn(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Warning(
message
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Warn(
string message,
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Warning(
message
.FormatForException(ex)
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Warn(
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Warning(
(ex != null ? ex.ToString() : "")
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Error(
string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Error(
message
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Error(
string message,
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Error(
message
.FormatForException(ex)
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Error(
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Serilog.Log.Error(
(ex != null ? ex.ToString() : "")
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Fatal(
string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
FatalAction();
Serilog.Log.Error(
message
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Fatal(
string message,
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
FatalAction();
Serilog.Log.Error(
message
.FormatForException(ex)
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
public static void Fatal(
Exception ex,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
FatalAction();
Serilog.Log.Error(
(ex != null ? ex.ToString() : "")
.FormatForContext(memberName, sourceFilePath, sourceLineNumber)
);
}
private static void FatalAction()
{
Environment.ExitCode = -1;
}
}
}
@danworley
Copy link

Nice job @morteng85 ! That works with the message templates! I just needed to implement the Constants, added .Enrich.With<InvocationContextEnricher>() to the LoggerConfiguration instatiation, and the appropriate outputTemplate in my Sink(s), and it all worked! 🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment