Skip to content

Instantly share code, notes, and snippets.

@AldeRoberge
Last active November 15, 2024 21:26
Show Gist options
  • Save AldeRoberge/b88cc971fbe86d52aaf6b9bfe5ca6597 to your computer and use it in GitHub Desktop.
Save AldeRoberge/b88cc971fbe86d52aaf6b9bfe5ca6597 to your computer and use it in GitHub Desktop.
The Logging config I use for all my apps
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Events;
using Serilog.Exceptions;
using Serilog.Sinks.SystemConsole.Themes;
namespace ADG.Library.Logging
{
public static class LoggingConfig
{
// Configure the Serilog logger
public static void ConfigureLogger(LogEventLevel minimumLevel = LogEventLevel.Information)
{
// Ensure Logs folder exists
Directory.CreateDirectory("Logs");
// Define the log output template
var outputTemplate =
"[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:w3} {FilePath}.{Method}:{LineNumber}] " + // Timestamp with milliseconds and log level
"{Message}" + // Log message
"{NewLine}{Exception}"; // Exception details (if any)
// Configure the logger
var config = new LoggerConfiguration()
.MinimumLevel.Is(minimumLevel)
.MinimumLevel.Override("Microsoft", minimumLevel)
.Enrich.WithExceptionDetails()
.Enrich.FromLogContext()
.WriteTo.Console(theme: AnsiConsoleTheme.Code, outputTemplate: outputTemplate)
.WriteTo.File("Logs/ADG-Log.txt",
LogEventLevel.Warning,
outputTemplate: outputTemplate,
rollingInterval: RollingInterval.Day);
// Set the minimum log level
// Create the logger
Log.Logger = config.CreateLogger();
}
// Enables the use of the Serilog logger in Dependency Injection
public static void ConfigureLogger(this IServiceCollection services)
{
ConfigureLogger();
services.AddSingleton(Log.Logger);
}
}
}
using System;
using System.Diagnostics;
using Serilog;
using Serilog.Context;
using Serilog.Core;
namespace ADG.Shared.Logging
{
public static class LoggerExtensions
{
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogInformation(this ILogger? logger, string messageTemplate, params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Information))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Information(messageTemplate, propertyValues);
}
}
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogWarning(this ILogger? logger, string messageTemplate, params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Warning))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Warning(messageTemplate, propertyValues);
}
}
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogError(this ILogger? logger, Exception exception, string messageTemplate,
params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Error))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Error(exception, messageTemplate, propertyValues);
}
}
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogError(this ILogger? logger, string messageTemplate, params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Error))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Error(messageTemplate, propertyValues);
}
}
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogError(this ILogger? logger, Exception exception, params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Error))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Error(exception, string.Empty, propertyValues);
}
}
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogError(this ILogger? logger, string messageTemplate, Exception exception,
params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Error))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Error(exception, messageTemplate, propertyValues);
}
}
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogVerbose(this ILogger? logger, string messageTemplate, params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Verbose))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Verbose(messageTemplate, propertyValues);
}
}
// Log Fatal
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogFatal(this ILogger? logger, Exception exception, string messageTemplate,
params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Fatal))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Fatal(exception, messageTemplate, propertyValues);
}
}
// Log Fatal without exception
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogFatal(this ILogger? logger, string messageTemplate, params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Fatal))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Fatal(messageTemplate, propertyValues);
}
}
// Log Fatal without message
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogFatal(this ILogger? logger, Exception exception, params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Fatal))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Fatal(exception, string.Empty, propertyValues);
}
}
// Log Fatal with exception and message
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogFatal(this ILogger? logger, string messageTemplate, Exception exception,
params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Fatal))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Fatal(exception, messageTemplate, propertyValues);
}
}
// Log Debug
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogDebug(this ILogger? logger, string messageTemplate, params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Debug))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Debug(messageTemplate, propertyValues);
}
}
// Log Verbose
[MessageTemplateFormatMethod("messageTemplate")]
public static void LogVerbose(this ILogger? logger, Exception exception, string messageTemplate,
params object[] propertyValues)
{
logger ??= Log.Logger;
if (logger is Logger log && !log.IsEnabled(Serilog.Events.LogEventLevel.Verbose))
return;
var callerInfo = GetCallerInfo();
using (LogContext.PushProperty("Method", $"{callerInfo.MethodName}."))
using (LogContext.PushProperty("FilePath", $" {callerInfo.FileName}."))
using (LogContext.PushProperty("LineNumber", callerInfo.LineNumber))
{
logger.Verbose(exception, messageTemplate, propertyValues);
}
}
private static (string MethodName, string FileName, int LineNumber) GetCallerInfo()
{
var stackTrace = new StackTrace(true); // Capture file info as well
var frame = stackTrace.GetFrame(2); // 2 to get the immediate caller of the logging method
var method = frame?.GetMethod();
var methodName = method?.Name ?? "?";
var fileName = frame?.GetFileName() ?? "?";
var lineNumber = frame?.GetFileLineNumber() ?? 0;
return (methodName, GetFilePath(fileName), lineNumber);
}
private static string GetFilePath(string sourceFilePath)
{
if (string.IsNullOrEmpty(sourceFilePath))
return "UnknownFile";
// Keep only the file name
var fileName = sourceFilePath.Substring(sourceFilePath.LastIndexOf("\\", StringComparison.Ordinal) + 1);
// Remove the file extension
// Ensure file name contains "."
if (fileName.IndexOf(".", StringComparison.Ordinal) == -1)
return fileName;
return fileName.Substring(0, fileName.LastIndexOf(".", StringComparison.Ordinal));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment