initial
This commit is contained in:
31
code/portaloggy.sln
Normal file
31
code/portaloggy.sln
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.10.35027.167
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "portaloggy", "portaloggy\portaloggy.csproj", "{67A83ECB-9B90-47E1-B56A-BAFE8DB64B1D}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "portaloggy.Tester", "portaloggy.Tester\portaloggy.Tester.csproj", "{4BE8310F-EC55-443C-A5AF-3C2D065E314C}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{67A83ECB-9B90-47E1-B56A-BAFE8DB64B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{67A83ECB-9B90-47E1-B56A-BAFE8DB64B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{67A83ECB-9B90-47E1-B56A-BAFE8DB64B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{67A83ECB-9B90-47E1-B56A-BAFE8DB64B1D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4BE8310F-EC55-443C-A5AF-3C2D065E314C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4BE8310F-EC55-443C-A5AF-3C2D065E314C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4BE8310F-EC55-443C-A5AF-3C2D065E314C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4BE8310F-EC55-443C-A5AF-3C2D065E314C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {EF3D422B-47DF-4283-A055-D1A59E923175}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
15
code/portaloggy/Constants.cs
Normal file
15
code/portaloggy/Constants.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
namespace portaloggy
|
||||||
|
{
|
||||||
|
public class Constants
|
||||||
|
{
|
||||||
|
public const string LOG_SEVERITY_NONE = "NONE";
|
||||||
|
public const string LOG_SEVERITY_TRACE = "TRACE";
|
||||||
|
public const string LOG_SEVERITY_DEBUG = "DEBUG";
|
||||||
|
public const string LOG_SEVERITY_INFO = "INFO";
|
||||||
|
public const string LOG_SEVERITY_WARN = "WARN";
|
||||||
|
public const string LOG_SEVERITY_ERROR = "ERROR";
|
||||||
|
public const string LOG_SEVERITY_FATAL = "FATAL";
|
||||||
|
public const string LOG_SEVERITY_SUCCESS = "SUCCESS";
|
||||||
|
public const string LOG_SEVERITY_ANCHOR = "ANCHOR";
|
||||||
|
}
|
||||||
|
}
|
||||||
52
code/portaloggy/GenericPrettifier.cs
Normal file
52
code/portaloggy/GenericPrettifier.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
namespace portaloggy
|
||||||
|
{
|
||||||
|
public static class GenericPrettifier
|
||||||
|
{
|
||||||
|
public static string GetPrettifiedSeverity(string severity = null)
|
||||||
|
{
|
||||||
|
var calculatedSeverity = severity ?? Constants.LOG_SEVERITY_NONE;
|
||||||
|
|
||||||
|
if(calculatedSeverity.Length > 5)
|
||||||
|
{
|
||||||
|
calculatedSeverity = calculatedSeverity.Substring(0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return calculatedSeverity.PadLeft(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetPrettifiedSource(string knownCallerMemberName, string knownCallerFilePath, int knownCallerLineNumber, bool isFileNameToClassTypeTranslationEnabled = true)
|
||||||
|
{
|
||||||
|
var computedSource = string.Empty;
|
||||||
|
var sourceFile = new FileInfo(knownCallerFilePath);
|
||||||
|
|
||||||
|
if(isFileNameToClassTypeTranslationEnabled)
|
||||||
|
{
|
||||||
|
computedSource = $"{sourceFile.Name.Substring(0, sourceFile.Name.Length - sourceFile.Extension.Length)}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
computedSource = $"{sourceFile.Name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(computedSource.Length>20)
|
||||||
|
{
|
||||||
|
computedSource = computedSource.Substring(computedSource.Length - 17);
|
||||||
|
computedSource = $"...{computedSource}";
|
||||||
|
}
|
||||||
|
|
||||||
|
computedSource = computedSource.PadRight(20);
|
||||||
|
|
||||||
|
if(knownCallerLineNumber > 0)
|
||||||
|
{
|
||||||
|
var formattedLineNumber = knownCallerLineNumber.ToString().PadRight(4);
|
||||||
|
computedSource = $"{computedSource}:{formattedLineNumber}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
computedSource = computedSource.PadRight(25);
|
||||||
|
}
|
||||||
|
|
||||||
|
return computedSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
code/portaloggy/ILogger.cs
Normal file
24
code/portaloggy/ILogger.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace portaloggy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Universal logger. Use everywhere.
|
||||||
|
/// </summary>
|
||||||
|
public interface ILogger
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Logs a message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">Your message.</param>
|
||||||
|
/// <param name="severity">Optionally specify severity.</param>
|
||||||
|
/// <param name="exception">Optionally give exception.</param>
|
||||||
|
void Log(
|
||||||
|
string message,
|
||||||
|
string severity = null,
|
||||||
|
Exception exception = null,
|
||||||
|
[CallerMemberName] string memberName = "",
|
||||||
|
[CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerLineNumber] int sourceLineNumber = 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
112
code/portaloggy/LoggerExtensions.cs
Normal file
112
code/portaloggy/LoggerExtensions.cs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace portaloggy
|
||||||
|
{
|
||||||
|
public static class LoggerExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Trace messages are debug messages which are too many to even store.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// For example, you're checking a directory for new files every 5 seconds, and you generate 5 log message for
|
||||||
|
/// starting, running, ending and result of the operation. You run this over 5 directories. That's 25 messages
|
||||||
|
/// per 5 seconds. You don't need to log this unless you're really after some serious bug that you're hunting.
|
||||||
|
/// Designating this kind of log message as trace allows us to ignore it, as it would baloon our log files.
|
||||||
|
/// </remarks>
|
||||||
|
public static void Trace(
|
||||||
|
this ILogger logger,
|
||||||
|
string message,
|
||||||
|
[CallerMemberName] string callerMemberName = "",
|
||||||
|
[CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerLineNumber] int callerLineNumber = 0)
|
||||||
|
{
|
||||||
|
logger.Log(message, Constants.LOG_SEVERITY_TRACE, null, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Debug messages that are likely not going to be enabled/collected from a production build, or will be only
|
||||||
|
/// enabled temporarily to hunt down a bug. Use <see cref="Trace(ILogger, string, string, string, int)"/>, if
|
||||||
|
/// you're expecting to have so many messages, that it would make the log output basically unreadable.
|
||||||
|
/// </summary>
|
||||||
|
public static void Debug(
|
||||||
|
this ILogger logger,
|
||||||
|
string message,
|
||||||
|
[CallerMemberName] string callerMemberName = "",
|
||||||
|
[CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerLineNumber] int callerLineNumber = 0)
|
||||||
|
{
|
||||||
|
logger.Log(message, Constants.LOG_SEVERITY_DEBUG, null, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Info(
|
||||||
|
this ILogger logger,
|
||||||
|
string message,
|
||||||
|
[CallerMemberName] string callerMemberName = "",
|
||||||
|
[CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerLineNumber] int callerLineNumber = 0)
|
||||||
|
{
|
||||||
|
logger.Log(message, Constants.LOG_SEVERITY_INFO, null, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Warning(
|
||||||
|
this ILogger logger,
|
||||||
|
string message,
|
||||||
|
[CallerMemberName] string callerMemberName = "",
|
||||||
|
[CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerLineNumber] int callerLineNumber = 0)
|
||||||
|
{
|
||||||
|
logger.Log(message, Constants.LOG_SEVERITY_WARN, null, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Success(
|
||||||
|
this ILogger logger,
|
||||||
|
string message,
|
||||||
|
[CallerMemberName] string callerMemberName = "",
|
||||||
|
[CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerLineNumber] int callerLineNumber = 0)
|
||||||
|
{
|
||||||
|
logger.Log(message, Constants.LOG_SEVERITY_SUCCESS, null, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Error(
|
||||||
|
this ILogger logger,
|
||||||
|
string message,
|
||||||
|
Exception ex = null,
|
||||||
|
[CallerMemberName] string callerMemberName = "",
|
||||||
|
[CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerLineNumber] int callerLineNumber = 0)
|
||||||
|
{
|
||||||
|
logger.Log(message, Constants.LOG_SEVERITY_ERROR, ex, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Irrecoverable error after which the process or application will die.
|
||||||
|
/// </summary>
|
||||||
|
public static void Fatal(
|
||||||
|
this ILogger logger,
|
||||||
|
string message,
|
||||||
|
Exception ex = null,
|
||||||
|
[CallerMemberName] string callerMemberName = "",
|
||||||
|
[CallerFilePath] string callerFilePath = "",
|
||||||
|
[CallerLineNumber] int callerLineNumber = 0)
|
||||||
|
{
|
||||||
|
logger.Log(message, Constants.LOG_SEVERITY_FATAL, ex, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a log line that carries keywords, such as #specificScenario, to allow machine-readable identification
|
||||||
|
/// of log line in log output. Your message value is prefixed with a ":::".
|
||||||
|
/// </summary>
|
||||||
|
public static void Anchor(
|
||||||
|
this ILogger logger,
|
||||||
|
string message,
|
||||||
|
params string[] keywords)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logger.Log($"Anchored log line {string.Join(", ", keywords)}:::{message}", Constants.LOG_SEVERITY_ANCHOR);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
code/portaloggy/Loggers/AggregatedLogger.cs
Normal file
37
code/portaloggy/Loggers/AggregatedLogger.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace portaloggy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Allows logging into multiple loggers simultaneously.
|
||||||
|
/// </summary>
|
||||||
|
public class AggregatedLogger : ILogger
|
||||||
|
{
|
||||||
|
private readonly ICollection<ILogger> loggers;
|
||||||
|
|
||||||
|
public AggregatedLogger(params ILogger[] loggers)
|
||||||
|
{
|
||||||
|
if (loggers == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(loggers));
|
||||||
|
}
|
||||||
|
else if (loggers.Length == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(loggers));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loggers = loggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log(string message, string severity = null, Exception exception = null, [CallerMemberName] string memberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
|
||||||
|
{
|
||||||
|
lock(this.loggers)
|
||||||
|
{
|
||||||
|
foreach(ILogger logger in this.loggers)
|
||||||
|
{
|
||||||
|
logger.Log(message, severity, exception, memberName, callerFilePath, sourceLineNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
code/portaloggy/Loggers/ConsoleLogger.cs
Normal file
111
code/portaloggy/Loggers/ConsoleLogger.cs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace portaloggy
|
||||||
|
{
|
||||||
|
public class ConsoleLogger : ILogger
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If enabled, logger will print [TRACE] logs. Warning: There are supposed to be A LOT of those.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsTraceOutputEnabled = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If enabled, logger will print [DEBUG] logs.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDebugOutputEnabled = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If nabled, source file names will be trated as class types.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFileNameToClassTypeTranslationEnabled = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If enabled, messages will use different coloring based on their severity.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsMessageSeverityColoringEnabled = true;
|
||||||
|
|
||||||
|
private object printLocker = new object();
|
||||||
|
|
||||||
|
public void Log(string message, string severity = null, Exception exception = null, [CallerMemberName] string memberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (severity == Constants.LOG_SEVERITY_TRACE && this.IsTraceOutputEnabled == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (severity == Constants.LOG_SEVERITY_DEBUG && this.IsDebugOutputEnabled == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var processedMessage = exception == null
|
||||||
|
? message
|
||||||
|
: $"{exception.GetType().Name}: {exception.Message}";
|
||||||
|
|
||||||
|
if (exception != null && !string.IsNullOrWhiteSpace(message))
|
||||||
|
{
|
||||||
|
processedMessage = $"{message} Error: {processedMessage}";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Print(severity, $"[{DateTime.Now:s}] - {GenericPrettifier.GetPrettifiedSource(memberName, callerFilePath, sourceLineNumber, this.IsFileNameToClassTypeTranslationEnabled)} - {GenericPrettifier.GetPrettifiedSeverity(severity)} - {processedMessage ?? "No message."}");
|
||||||
|
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
var stacktrace = this.FormatStacktrace(exception);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(stacktrace) == false)
|
||||||
|
{
|
||||||
|
Console.WriteLine("--- STACKTRACE ---");
|
||||||
|
Console.WriteLine(stacktrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { } // Ignore. We do not crash because of a failed log.
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Print(string severity, string message)
|
||||||
|
{
|
||||||
|
lock (this.printLocker)
|
||||||
|
{
|
||||||
|
if (this.IsMessageSeverityColoringEnabled == false)
|
||||||
|
{
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
else if (severity == Constants.LOG_SEVERITY_TRACE)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||||
|
}
|
||||||
|
else if(severity == Constants.LOG_SEVERITY_DEBUG)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||||
|
}
|
||||||
|
else if(severity == Constants.LOG_SEVERITY_WARN)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
}
|
||||||
|
else if(severity == Constants.LOG_SEVERITY_ERROR)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
}
|
||||||
|
else if(severity == Constants.LOG_SEVERITY_FATAL)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
}
|
||||||
|
else if(severity == Constants.LOG_SEVERITY_SUCCESS)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine(message);
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatStacktrace(Exception ex)
|
||||||
|
{
|
||||||
|
var trace = $"\n{ex.StackTrace}";
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
code/portaloggy/portaloggy.csproj
Normal file
14
code/portaloggy/portaloggy.csproj
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<Title>portaloggy</Title>
|
||||||
|
<Authors>mc</Authors>
|
||||||
|
<AssemblyVersion>1.0.1</AssemblyVersion>
|
||||||
|
<Version>$(AssemblyVersion)</Version>
|
||||||
|
<Description>A highly-portable, multi-platform, system-agnostic logging abstraction. Use portaloggy.ILogger everywhere. Make use of portaloggy.LoggerExtensions. Already contains ConsoleLogger for dead-simple console logging and AggregatedLogger for simultaneous logging to your own implementation of ILogger.</Description>
|
||||||
|
<Copyright>mc @ 2024</Copyright>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user