mirror of
https://github.com/hmaxnl/DotBased.git
synced 2025-01-18 10:04:20 +01:00
[REFACTOR] Refactored logging & added support for Microsoft.Extensions.Logging. Added test WebApi project
This commit is contained in:
parent
58739c2aea
commit
737cbcfd11
|
@ -7,14 +7,23 @@
|
|||
|
||||
using DotBased.Logging.Serilog;
|
||||
using DotBased.Logging;
|
||||
using DotBased.Utilities;
|
||||
using Serilog;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
LogService.Initialize(options =>
|
||||
{
|
||||
options
|
||||
.AddSeverityFilter("Program", LogSeverity.Verbose)
|
||||
.AddSeverityFilter("DotBased.dll", LogSeverity.Verbose);
|
||||
});
|
||||
|
||||
var serilogLogger = SetupSerilog();
|
||||
LogService.AddLogAdapter(new BasedSerilogAdapter(serilogLogger));
|
||||
var logger = LogService.RegisterLogger(typeof(Program));
|
||||
var logger = LogService.RegisterLogger<Program>();
|
||||
|
||||
logger.Information("Whoah... Hi!");
|
||||
logger.Information("Whoah... Hi!, {Param}", "Test!");
|
||||
var cult = Culture.GetSystemCultures();
|
||||
|
||||
Console.ReadKey(); // Hold console app open.
|
||||
return;
|
||||
|
|
|
@ -18,21 +18,23 @@ public class BasedServerAuthenticationStateProvider : ServerAuthenticationStateP
|
|||
_config = configuration;
|
||||
_localStorage = localStorage;
|
||||
_securityService = securityService;
|
||||
_logger = LogService.RegisterLogger(typeof(BasedServerAuthenticationStateProvider));
|
||||
_logger = LogService.RegisterLogger<BasedServerAuthenticationStateProvider>();
|
||||
}
|
||||
|
||||
private BasedAuthConfiguration _config;
|
||||
private readonly ProtectedLocalStorage _localStorage;
|
||||
private readonly SecurityService _securityService;
|
||||
private ILogger _logger;
|
||||
private readonly ILogger _logger;
|
||||
private readonly AuthenticationState _anonState = new(new ClaimsPrincipal());
|
||||
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
_logger.Debug("Getting authentication state...");
|
||||
var sessionIdResult = await _localStorage.GetAsync<string>(BasedAuthDefaults.StorageKey);
|
||||
if (!sessionIdResult.Success || sessionIdResult.Value == null)
|
||||
return _anonState;
|
||||
_logger.Debug("Found state [{State}], getting session from {Service}", sessionIdResult.Value, nameof(SecurityService));
|
||||
var stateResult = await _securityService.GetAuthenticationStateFromSessionAsync(sessionIdResult.Value);
|
||||
return stateResult is { Success: true, Value: not null } ? stateResult.Value : _anonState;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ public static class DotBasedAuthDependencyInjection
|
|||
var Configuration = new BasedAuthConfiguration();
|
||||
configurationAction?.Invoke(Configuration);
|
||||
|
||||
|
||||
services.AddSingleton<BasedAuthConfiguration>(Configuration);
|
||||
if (Configuration.AuthDataRepositoryType == null)
|
||||
throw new ArgumentNullException(nameof(Configuration.AuthDataRepositoryType), $"No '{nameof(IAuthDataRepository)}' configured!");
|
||||
|
|
|
@ -34,11 +34,11 @@ public class MemoryAuthDataRepository : IAuthDataRepository
|
|||
public async Task<Result<UserModel>> GetUserAsync(string id, string email, string username)
|
||||
{
|
||||
UserModel? userModel = null;
|
||||
if (!id.IsNullOrWhiteSpace())
|
||||
if (!id.IsNullOrEmpty())
|
||||
userModel = MemoryData.users.FirstOrDefault(u => u.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
|
||||
if (!email.IsNullOrWhiteSpace())
|
||||
if (!email.IsNullOrEmpty())
|
||||
userModel = MemoryData.users.FirstOrDefault(u => u.Email.Equals(email, StringComparison.OrdinalIgnoreCase));
|
||||
if (!username.IsNullOrWhiteSpace())
|
||||
if (!username.IsNullOrEmpty())
|
||||
userModel = MemoryData.users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.OrdinalIgnoreCase));
|
||||
return userModel != null ? Result<UserModel>.Ok(userModel) : Result<UserModel>.Failed("No user found!");
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ public class SecurityService
|
|||
_authDataRepository = authDataRepository;
|
||||
_dataCache = dataCache;
|
||||
_localStorage = localStorage;
|
||||
_logger = LogService.RegisterLogger(typeof(SecurityService));
|
||||
_logger = LogService.RegisterLogger<SecurityService>();
|
||||
}
|
||||
|
||||
private readonly IAuthDataRepository _authDataRepository;
|
||||
|
@ -26,7 +26,7 @@ public class SecurityService
|
|||
|
||||
public async Task<Result<AuthenticationState>> GetAuthenticationStateFromSessionAsync(string id)
|
||||
{
|
||||
if (id.IsNullOrWhiteSpace())
|
||||
if (id.IsNullOrEmpty())
|
||||
return Result<AuthenticationState>.Failed("No valid id!");
|
||||
AuthenticationStateModel? authStateModel = null;
|
||||
var stateCache = _dataCache.RequestSessionState(id);
|
||||
|
@ -75,13 +75,13 @@ public class SecurityService
|
|||
{
|
||||
UserModel? user = null;
|
||||
Result<UserModel> usrResult;
|
||||
if (!login.UserName.IsNullOrWhiteSpace())
|
||||
if (!login.UserName.IsNullOrEmpty())
|
||||
{
|
||||
usrResult = await _authDataRepository.GetUserAsync(string.Empty, string.Empty, login.UserName);
|
||||
if (usrResult is { Success: true, Value: not null })
|
||||
user = usrResult.Value;
|
||||
}
|
||||
else if (!login.Email.IsNullOrWhiteSpace())
|
||||
else if (!login.Email.IsNullOrEmpty())
|
||||
{
|
||||
usrResult = await _authDataRepository.GetUserAsync(string.Empty, login.Email, string.Empty);
|
||||
if (usrResult is { Success: true, Value: not null })
|
||||
|
@ -114,7 +114,7 @@ public class SecurityService
|
|||
{
|
||||
try
|
||||
{
|
||||
if (state.IsNullOrWhiteSpace())
|
||||
if (state.IsNullOrEmpty())
|
||||
return Result.Failed($"Argument {nameof(state)} is empty!");
|
||||
|
||||
var stateResult = await _authDataRepository.GetAuthenticationStateAsync(state);
|
||||
|
|
63
DotBased.Logging.MEL/BasedLogger.cs
Normal file
63
DotBased.Logging.MEL/BasedLogger.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DotBased.Logging.MEL;
|
||||
|
||||
public class BasedLogger : Microsoft.Extensions.Logging.ILogger
|
||||
{
|
||||
public BasedLogger(ILogger logger)
|
||||
{
|
||||
basedLogger = logger;
|
||||
}
|
||||
|
||||
private readonly ILogger basedLogger;
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel))
|
||||
return;
|
||||
var severity = ConvertLogLevelToSeverity(logLevel);
|
||||
var capsule = ConstructCapsule(severity, eventId, state, exception, formatter);
|
||||
basedLogger.Log(capsule);
|
||||
}
|
||||
|
||||
private LogCapsule ConstructCapsule<TState>(LogSeverity severity, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
//TODO: Extract parameters & format
|
||||
if (state is IEnumerable<KeyValuePair<string, object>> stateEnum)
|
||||
{
|
||||
foreach (var prop in stateEnum)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return new LogCapsule()
|
||||
{
|
||||
Exception = exception,
|
||||
Message = formatter.Invoke(state, exception),
|
||||
Parameters = [],
|
||||
Severity = severity,
|
||||
TimeStamp = DateTime.Now,
|
||||
Logger = basedLogger as LoggerBase ?? throw new NullReferenceException(nameof(basedLogger))
|
||||
};
|
||||
}
|
||||
|
||||
private LogSeverity ConvertLogLevelToSeverity(LogLevel level)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
LogLevel.Trace => LogSeverity.Trace,
|
||||
LogLevel.Debug => LogSeverity.Debug,
|
||||
LogLevel.Information => LogSeverity.Info,
|
||||
LogLevel.Warning => LogSeverity.Warning,
|
||||
LogLevel.Error => LogSeverity.Error,
|
||||
LogLevel.Critical => LogSeverity.Fatal,
|
||||
LogLevel.None => LogSeverity.Ignore,
|
||||
_ => LogSeverity.Verbose
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
|
||||
|
||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default;
|
||||
}
|
23
DotBased.Logging.MEL/BasedLoggerProvider.cs
Normal file
23
DotBased.Logging.MEL/BasedLoggerProvider.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DotBased.Logging.MEL;
|
||||
|
||||
public class BasedLoggerProvider : ILoggerProvider
|
||||
{
|
||||
public BasedLoggerProvider(LogOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
private readonly LogOptions Options;
|
||||
|
||||
public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new BasedLogger(Options.LoggerBuilder.Invoke(new LoggerInformation(typeof(BasedLoggerProvider)), categoryName));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
18
DotBased.Logging.MEL/DotBased.Logging.MEL.csproj
Normal file
18
DotBased.Logging.MEL/DotBased.Logging.MEL.csproj
Normal file
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>12</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DotBased\DotBased.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
DotBased.Logging.MEL/LoggerBuilderExtensions.cs
Normal file
14
DotBased.Logging.MEL/LoggerBuilderExtensions.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DotBased.Logging.MEL;
|
||||
|
||||
public static class LoggerBuilderExtensions
|
||||
{
|
||||
public static ILoggingBuilder AddDotBased(this ILoggingBuilder builder, LogOptions options)
|
||||
{
|
||||
if (builder == null)
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
builder.AddProvider(new BasedLoggerProvider(options));
|
||||
return builder;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ public static class BasedSerilog
|
|||
/// <summary>
|
||||
/// Default output template with the extra properties that can be used for serilog sinks.
|
||||
/// </summary>
|
||||
public const string OutputTemplate = "[{Timestamp:HH:mm:ss} - {Caller}->{Assembly}] | {Level:u3}] {Message:lj}{NewLine}{Exception}";
|
||||
public const string OutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3} - {LoggerName}]{NewLine} {Message:lj}{NewLine}{Exception}";
|
||||
|
||||
public static LoggerConfiguration UseBasedExtension(this LoggerConfiguration loggerConfiguration)
|
||||
{
|
||||
|
@ -20,8 +20,10 @@ public static class BasedSerilog
|
|||
/// </summary>
|
||||
public static class ExtraProperties
|
||||
{
|
||||
public const string LoggerName = "LoggerName";
|
||||
public const string AssemblyProp = "Assembly";
|
||||
public const string SourceProp = "Source";
|
||||
public const string FullNameProp = "FullName";
|
||||
public const string NamespaceProp = "Namespace";
|
||||
public const string CallerProp = "Caller";
|
||||
}
|
||||
}
|
|
@ -16,9 +16,11 @@ public class BasedSerilogAdapter(global::Serilog.ILogger serilogLogger) : LogAda
|
|||
if (capsule == null)
|
||||
return;
|
||||
var logger = serilogLogger
|
||||
.ForContext(BasedSerilog.ExtraProperties.AssemblyProp, capsule.Logger.Caller.AssemblyName)
|
||||
.ForContext(BasedSerilog.ExtraProperties.SourceProp, capsule.Logger.Caller.Source)
|
||||
.ForContext(BasedSerilog.ExtraProperties.CallerProp, capsule.Logger.Caller.Name);
|
||||
.ForContext(BasedSerilog.ExtraProperties.LoggerName, capsule.Logger.Name)
|
||||
.ForContext(BasedSerilog.ExtraProperties.AssemblyProp, capsule.Logger.LoggerInformation.AssemblyName)
|
||||
.ForContext(BasedSerilog.ExtraProperties.FullNameProp, capsule.Logger.LoggerInformation.TypeFullName)
|
||||
.ForContext(BasedSerilog.ExtraProperties.NamespaceProp, capsule.Logger.LoggerInformation.TypeNamespace)
|
||||
.ForContext(BasedSerilog.ExtraProperties.CallerProp, capsule.Logger.LoggerInformation.TypeName);
|
||||
|
||||
var template = _messageTemplateParser.Parse(capsule.Message);
|
||||
IEnumerable<LogEventProperty>? properties = null;
|
||||
|
|
|
@ -15,7 +15,7 @@ public class BasedSerilogEnricher : ILogEventEnricher
|
|||
sourcePropValue = appValue.ToString().Replace("\"", "");
|
||||
|
||||
var assemblyProperty = propertyFactory.CreateProperty(BasedSerilog.ExtraProperties.AssemblyProp, asmPropValue);
|
||||
var sourceProperty = propertyFactory.CreateProperty(BasedSerilog.ExtraProperties.SourceProp, sourcePropValue);
|
||||
var sourceProperty = propertyFactory.CreateProperty(BasedSerilog.ExtraProperties.FullNameProp, sourcePropValue);
|
||||
var callerProperty = propertyFactory.CreateProperty(BasedSerilog.ExtraProperties.CallerProp, sourcePropValue);
|
||||
|
||||
logEvent.AddPropertyIfAbsent(assemblyProperty);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>default</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
|
16
DotBased.sln
16
DotBased.sln
|
@ -10,6 +10,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{2156
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.ASP.Auth", "DotBased.ASP.Auth\DotBased.ASP.Auth.csproj", "{CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.Logging.MEL", "DotBased.Logging.MEL\DotBased.Logging.MEL.csproj", "{D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{DBDB4538-85D4-45AC-9E0A-A684467AEABA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWebApi", "TestWebApi\TestWebApi.csproj", "{BADA4BAF-142B-47A8-95FC-B25E1D3D0020}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -32,9 +38,19 @@ Global
|
|||
{CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BADA4BAF-142B-47A8-95FC-B25E1D3D0020}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BADA4BAF-142B-47A8-95FC-B25E1D3D0020}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BADA4BAF-142B-47A8-95FC-B25E1D3D0020}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BADA4BAF-142B-47A8-95FC-B25E1D3D0020}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC} = {2156FB93-C252-4B33-8A0C-73C82FABB163}
|
||||
{CBD4111D-F1CA-466A-AC73-9EAB7F235B3D} = {2156FB93-C252-4B33-8A0C-73C82FABB163}
|
||||
{D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D} = {2156FB93-C252-4B33-8A0C-73C82FABB163}
|
||||
{BADA4BAF-142B-47A8-95FC-B25E1D3D0020} = {DBDB4538-85D4-45AC-9E0A-A684467AEABA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace DotBased.Collections;
|
|||
/// </summary>
|
||||
public class InstanceContainer : IDisposable
|
||||
{
|
||||
private readonly ILogger _log = LogService.RegisterLogger(typeof(InstanceContainer));
|
||||
private readonly ILogger _log = LogService.RegisterLogger<InstanceContainer>();
|
||||
private readonly Dictionary<string, InstanceNode> _tCollection = new Dictionary<string, InstanceNode>();
|
||||
|
||||
/// <summary>
|
||||
|
@ -40,7 +40,7 @@ public class InstanceContainer : IDisposable
|
|||
case null:
|
||||
break;
|
||||
case IDisposable instance when dispose:
|
||||
_log.Debug("Disposing disposable object... (InstanceContainer)");
|
||||
_log.Debug("Disposing disposable object...");
|
||||
instance.Dispose();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>default</LangVersion>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -5,5 +5,5 @@ namespace DotBased.Extensions;
|
|||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static bool IsNullOrWhiteSpace(this string s) => string.IsNullOrWhiteSpace(s);
|
||||
public static bool IsNullOrEmpty(this string s) => string.IsNullOrWhiteSpace(s);
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace DotBased.Logging;
|
|||
/// </summary>
|
||||
public interface ILogger
|
||||
{
|
||||
public void Log(LogCapsule capsule);
|
||||
public void Verbose(string message, params object?[]? parameters);
|
||||
|
||||
public void Trace(string message, params object?[]? parameters);
|
||||
|
@ -18,4 +19,6 @@ public interface ILogger
|
|||
public void Error(Exception exception, string message, params object?[]? parameters);
|
||||
|
||||
public void Fatal(Exception exception, string message, params object?[]? parameters);
|
||||
|
||||
public string Name { get; }
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
namespace DotBased.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// The base for creating log adpaters.
|
||||
/// The base for creating log adapters.
|
||||
/// </summary>
|
||||
public abstract class LogAdapterBase
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ public abstract class LogAdapterBase
|
|||
public string AdapterName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle the incomming <see cref="LogCapsule"/> that the <see cref="LogProcessor"/> sends.
|
||||
/// Handle the incoming <see cref="LogCapsule"/> that the <see cref="LogProcessor"/> sends.
|
||||
/// </summary>
|
||||
/// <param name="processor">The log processor that has processed this log</param>
|
||||
/// <param name="capsule">The log capsule, which contains the log information</param>
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using DotBased.Extensions;
|
||||
|
||||
namespace DotBased.Logging;
|
||||
|
||||
/// <summary>
|
||||
|
@ -5,14 +8,35 @@ namespace DotBased.Logging;
|
|||
/// </summary>
|
||||
public class LogOptions
|
||||
{
|
||||
public readonly SeverityFilterCollection SeverityFilters = [];
|
||||
|
||||
/// <summary>
|
||||
/// The severty the logger will log
|
||||
/// The severity the logger will log
|
||||
/// </summary>
|
||||
public LogSeverity Severity { get; set; } = LogSeverity.Trace;
|
||||
|
||||
/// <summary>
|
||||
/// The function that will build and return the <see cref="LoggerBase"/> when calling <see cref="LogService.RegisterLogger"/>, so a custom logger based on the <see cref="LoggerBase"/> can be used.
|
||||
/// The function that will build and return the <see cref="ILogger"/>.
|
||||
/// </summary>
|
||||
public Func<CallerInformation, Action<LogCapsule>, LoggerBase> LoggerBuilder { get; set; } =
|
||||
(identifier, sendEvent) => new Logger(identifier, ref sendEvent);
|
||||
public Func<LoggerInformation, string, ILogger> LoggerBuilder { get; set; } =
|
||||
(identifier, loggerName) => new Logger(identifier, loggerName);
|
||||
|
||||
public LogOptions AddSeverityFilter(string filter, LogSeverity logSeverity)
|
||||
{
|
||||
if (filter.IsNullOrEmpty())
|
||||
return this;
|
||||
SeverityFilters.Add(new SeverityFilter() { Filter = filter, Severity = logSeverity });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public struct SeverityFilter
|
||||
{
|
||||
public string Filter { get; set; }
|
||||
public LogSeverity Severity { get; set; }
|
||||
}
|
||||
|
||||
public class SeverityFilterCollection : KeyedCollection<string, SeverityFilter>
|
||||
{
|
||||
protected override string GetKeyForItem(SeverityFilter item) => item.Filter;
|
||||
}
|
|
@ -8,7 +8,7 @@ public class LogProcessor : IDisposable
|
|||
public LogProcessor()
|
||||
{
|
||||
_processorQueue = new Queue<LogCapsule>();
|
||||
IncommingLogHandlerEvent = IncommingLogHandler;
|
||||
IncomingLogHandlerEvent = IncomingLogHandler;
|
||||
_processorThread = new Thread(ProcessLog)
|
||||
{
|
||||
IsBackground = true,
|
||||
|
@ -16,7 +16,7 @@ public class LogProcessor : IDisposable
|
|||
};
|
||||
_processorThread.Start();
|
||||
}
|
||||
public readonly Action<LogCapsule> IncommingLogHandlerEvent;
|
||||
public readonly Action<LogCapsule> IncomingLogHandlerEvent;
|
||||
public event EventHandler<LogCapsule>? LogProcessed;
|
||||
private readonly Queue<LogCapsule> _processorQueue;
|
||||
private readonly Thread _processorThread;
|
||||
|
@ -41,14 +41,14 @@ public class LogProcessor : IDisposable
|
|||
Stop();
|
||||
}
|
||||
|
||||
private void IncommingLogHandler(LogCapsule e)
|
||||
private void IncomingLogHandler(LogCapsule e)
|
||||
{
|
||||
_processorQueue.Enqueue(e);
|
||||
// Check is the thread is running, if not wake up the thread.
|
||||
// Check if the thread is running, if not wake up the thread.
|
||||
if (!_threadSuspendEvent.WaitOne(0))
|
||||
_threadSuspendEvent.Set();
|
||||
}
|
||||
|
||||
|
||||
private void ProcessLog()
|
||||
{
|
||||
try
|
||||
|
@ -63,7 +63,9 @@ public class LogProcessor : IDisposable
|
|||
if (_processorQueue.Count != 0)
|
||||
{
|
||||
var capsule = _processorQueue.Dequeue();
|
||||
if (LogService.ShouldLog(LogService.Options.Severity, capsule.Severity))
|
||||
if (!LogService.CanLog(LogService.Options.Severity, capsule.Severity))
|
||||
continue;
|
||||
if (LogService.FilterSeverityLog(capsule))
|
||||
LogProcessed?.Invoke(this, capsule);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using DotBased.Extensions;
|
||||
|
||||
namespace DotBased.Logging;
|
||||
|
||||
|
@ -8,52 +9,43 @@ namespace DotBased.Logging;
|
|||
/// </summary>
|
||||
public static class LogService
|
||||
{
|
||||
// TODO: Future: add middlewares and changeable log processor
|
||||
static LogService()
|
||||
{
|
||||
Options = new LogOptions();
|
||||
LoggerSendEvent = LogProcessor.IncommingLogHandlerEvent;
|
||||
LoggerSendEvent = LogProcessor.IncomingLogHandlerEvent;
|
||||
}
|
||||
public static bool ShouldLog(LogSeverity maxSeverity, LogSeverity severity) => maxSeverity <= severity;
|
||||
public static bool CanLog(LogSeverity maxSeverity, LogSeverity severity) => maxSeverity <= severity;
|
||||
public static LogOptions Options { get; private set; }
|
||||
public static LogProcessor LogProcessor { get; private set; } = new LogProcessor();
|
||||
|
||||
private static HashSet<LogAdapterBase> Adapters { get; } = new HashSet<LogAdapterBase>();
|
||||
private static HashSet<LoggerBase> Loggers { get; } = new HashSet<LoggerBase>();
|
||||
private static HashSet<LogAdapterBase> Adapters { get; } = [];
|
||||
private static HashSet<ILogger> Loggers { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Action for internal communication between loggers and processor
|
||||
/// </summary>
|
||||
private static readonly Action<LogCapsule> LoggerSendEvent;
|
||||
internal static readonly Action<LogCapsule> LoggerSendEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Register a logger that will be used in a class and will live as long as the class.
|
||||
/// This will get the calling assembly and will pass that through ther log adapters.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public class Program
|
||||
/// {
|
||||
/// public Program
|
||||
/// {
|
||||
/// logger = LogService.RegisterLogger(nameof(Program));
|
||||
/// }
|
||||
/// private ILogger logger;
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="callerType">The type that called the function</param>
|
||||
/// <returns>The configured <see cref="ILogger"/> implementation that will be configuered in the <see cref="LogOptions.LoggerBuilder"/> at the <see cref="LogService"/> class</returns>
|
||||
public static ILogger RegisterLogger(Type callerType)
|
||||
public static void Initialize(Action<LogOptions>? options = null)
|
||||
{
|
||||
var logger = Options.LoggerBuilder.Invoke(new CallerInformation(callerType), LoggerSendEvent);
|
||||
Options = new LogOptions();
|
||||
options?.Invoke(Options);
|
||||
}
|
||||
|
||||
public static ILogger RegisterLogger(Type? callerType, string name = "")
|
||||
{
|
||||
var logger = Options.LoggerBuilder.Invoke(new LoggerInformation(callerType), name);
|
||||
Loggers.Add(logger);
|
||||
return logger;
|
||||
}
|
||||
|
||||
public static bool UnregisterLogger(LoggerBase logger) => Loggers.Remove(logger);
|
||||
/// <summary>
|
||||
/// Register a logger.
|
||||
/// </summary>
|
||||
/// <returns>The configured <see cref="ILogger"/> implementation that will be configured in the <see cref="LogOptions.LoggerBuilder"/> at the <see cref="LogService"/> class</returns>
|
||||
public static ILogger RegisterLogger<T>() => RegisterLogger(typeof(T), string.Empty);
|
||||
|
||||
public static ReadOnlyCollection<LoggerBase> GetLoggers => new ReadOnlyCollection<LoggerBase>(Loggers.ToList());
|
||||
public static ReadOnlyCollection<ILogger> GetLoggers => new ReadOnlyCollection<ILogger>(Loggers.ToList());
|
||||
|
||||
/// <summary>
|
||||
/// Add a log adapter to the service.
|
||||
|
@ -69,7 +61,7 @@ public static class LogService
|
|||
/// Removes the log adapter from the service.
|
||||
/// </summary>
|
||||
/// <param name="adapter">The adapter to remove</param>
|
||||
/// <returns>True if the adapter is succesfully removed otherwise false.</returns>
|
||||
/// <returns>True if the adapter is successfully removed otherwise false.</returns>
|
||||
public static bool RemoveLogAdapter(LogAdapterBase adapter)
|
||||
{
|
||||
if (!Adapters.Contains(adapter)) return false;
|
||||
|
@ -77,28 +69,52 @@ public static class LogService
|
|||
return Adapters.Remove(adapter);
|
||||
}
|
||||
|
||||
public static ReadOnlyCollection<LogAdapterBase> GetAdapters =>
|
||||
new ReadOnlyCollection<LogAdapterBase>(Adapters.ToList());
|
||||
public static ReadOnlyCollection<LogAdapterBase> GetAdapters => new ReadOnlyCollection<LogAdapterBase>(Adapters.ToList());
|
||||
|
||||
internal static bool FilterSeverityLog(LogCapsule capsule)
|
||||
{
|
||||
if (Options.SeverityFilters.TryGetValue(capsule.Logger.Name, out var namespaceFilter))
|
||||
return CanLog(namespaceFilter.Severity, capsule.Severity);
|
||||
var filterCapsuleNamespace = Options.SeverityFilters.Where(kvp => capsule.Logger.Name.Contains(kvp.Filter)).Select(v => v).ToList();
|
||||
if (filterCapsuleNamespace.Count == 0) return true;
|
||||
var filter = filterCapsuleNamespace.FirstOrDefault();
|
||||
return CanLog(filter.Severity, capsule.Severity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public readonly struct CallerInformation
|
||||
public readonly struct LoggerInformation
|
||||
{
|
||||
public CallerInformation(Type type)
|
||||
public LoggerInformation(Type? type)
|
||||
{
|
||||
Name = type.Name;
|
||||
Source = type.FullName ?? type.GUID.ToString();
|
||||
Namespace = type.Namespace ?? string.Empty;
|
||||
SourceAssembly = type.Assembly;
|
||||
if (type == null)
|
||||
return;
|
||||
|
||||
var asmName = SourceAssembly.GetName();
|
||||
AssemblyName = asmName.Name ?? "Unknown";
|
||||
AssemblyFullname = asmName.FullName;
|
||||
TypeName = type.Name;
|
||||
TypeFullName = type.FullName ?? string.Empty;
|
||||
TypeNamespace = type.Namespace ?? string.Empty;
|
||||
|
||||
var module = type.Module;
|
||||
ModuleName = module.Name;
|
||||
ModuleScopeName = module.ScopeName;
|
||||
ModuleFullyQualifiedName = module.FullyQualifiedName;
|
||||
|
||||
var assemblyName = type.Assembly.GetName();
|
||||
AssemblyName = assemblyName.Name ?? (type.Assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute)).FirstOrDefault() as AssemblyTitleAttribute)?.Title ?? string.Empty;
|
||||
AssemblyFullname = assemblyName.FullName;
|
||||
|
||||
if (TypeFullName.IsNullOrEmpty())
|
||||
TypeFullName = !TypeNamespace.IsNullOrEmpty() ? $"{TypeNamespace}.{TypeName}" : TypeName;
|
||||
if (TypeNamespace.IsNullOrEmpty())
|
||||
TypeNamespace = TypeName;
|
||||
}
|
||||
public string Name { get; }
|
||||
public string Source { get; }
|
||||
public string Namespace { get; }
|
||||
public Assembly SourceAssembly { get; }
|
||||
public string AssemblyName { get; }
|
||||
public string AssemblyFullname { get; }
|
||||
|
||||
public string TypeName { get; } = string.Empty;
|
||||
public string TypeFullName { get; } = string.Empty;
|
||||
public string TypeNamespace { get; } = string.Empty;
|
||||
public string AssemblyName { get; } = string.Empty;
|
||||
public string AssemblyFullname { get; } = string.Empty;
|
||||
public string ModuleName { get; } = string.Empty;
|
||||
public string ModuleScopeName { get; } = string.Empty;
|
||||
public string ModuleFullyQualifiedName { get; } = string.Empty;
|
||||
}
|
|
@ -8,5 +8,6 @@ public enum LogSeverity
|
|||
Info = 3,
|
||||
Warning = 4,
|
||||
Error = 5,
|
||||
Fatal = 6
|
||||
Fatal = 6,
|
||||
Ignore = 99
|
||||
}
|
|
@ -3,13 +3,8 @@ namespace DotBased.Logging;
|
|||
/// <summary>
|
||||
/// Main logger, this class is the default logger that the <see cref="LogService.RegisterLogger"/> function will return.
|
||||
/// </summary>
|
||||
public class Logger(CallerInformation caller, ref Action<LogCapsule> logProcessorHandler) : LoggerBase(caller, ref logProcessorHandler)
|
||||
public class Logger(LoggerInformation loggerInformation, string name) : LoggerBase(loggerInformation, name)
|
||||
{
|
||||
public void Log(LogCapsule capsule)
|
||||
{
|
||||
ProcessLog(capsule);
|
||||
}
|
||||
|
||||
public override void Verbose(string message, params object?[]? parameters)
|
||||
{
|
||||
Log(new LogCapsule()
|
||||
|
@ -96,5 +91,5 @@ public class Logger(CallerInformation caller, ref Action<LogCapsule> logProcesso
|
|||
});
|
||||
}
|
||||
|
||||
public override int GetHashCode() => HashCode.Combine(Caller.Source, Caller.AssemblyFullname);
|
||||
public override int GetHashCode() => HashCode.Combine(LoggerInformation.TypeFullName, LoggerInformation.AssemblyFullname);
|
||||
}
|
|
@ -1,15 +1,23 @@
|
|||
using DotBased.Extensions;
|
||||
|
||||
namespace DotBased.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Base for creating loggers
|
||||
/// </summary>
|
||||
/// <param name="caller">The caller information</param>
|
||||
/// <param name="logProcessorHandler">The handler where the logs can be send to</param>
|
||||
public abstract class LoggerBase(CallerInformation caller, ref Action<LogCapsule> logProcessorHandler) : ILogger
|
||||
/// <param name="loggerInformation">The caller information</param>
|
||||
public abstract class LoggerBase(LoggerInformation loggerInformation, string name) : ILogger
|
||||
{
|
||||
public CallerInformation Caller { get; } = caller;
|
||||
public LoggerInformation LoggerInformation { get; } = loggerInformation;
|
||||
public string Name { get; } = name.IsNullOrEmpty() ? loggerInformation.TypeNamespace : name;
|
||||
|
||||
internal readonly Action<LogCapsule> ProcessLog = logProcessorHandler;
|
||||
private readonly Action<LogCapsule> ProcessLog = LogService.LoggerSendEvent;
|
||||
|
||||
public void Log(LogCapsule capsule)
|
||||
{
|
||||
ProcessLog(capsule);
|
||||
}
|
||||
|
||||
|
||||
public abstract void Verbose(string message, params object?[]? parameters);
|
||||
public abstract void Trace(string message, params object?[]? parameters);
|
||||
|
|
|
@ -2,11 +2,11 @@ using DotBased.Extensions;
|
|||
|
||||
namespace DotBased.Objects;
|
||||
|
||||
public class DbObjectAttribute<TValueType> : ObjectAttribute<TValueType>
|
||||
public class DbObjectAttribute<TValueType> : ObjectAttribute<TValueType> where TValueType : IConvertible
|
||||
{
|
||||
public DbObjectAttribute(string key, TValueType value, string owner) : base(key, value)
|
||||
{
|
||||
if (owner.IsNullOrWhiteSpace())
|
||||
if (owner.IsNullOrEmpty())
|
||||
throw new ArgumentNullException(nameof(owner), $"The parameter {nameof(owner)} is null or empty! This parameter is required!");
|
||||
Owner = owner;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ public class ObjectAttribute<TValueType> : IObjectAttribute<TValueType>
|
|||
{
|
||||
protected ObjectAttribute(string key, TValueType value)
|
||||
{
|
||||
if (key.IsNullOrWhiteSpace())
|
||||
if (key.IsNullOrEmpty())
|
||||
throw new ArgumentNullException(nameof(key), $"The parameter {nameof(key)} is null or empty!");
|
||||
Key = key;
|
||||
Value = value;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Globalization;
|
||||
using DotBased.Logging;
|
||||
|
||||
namespace DotBased.Utilities;
|
||||
|
||||
|
@ -6,6 +7,7 @@ public static class Culture
|
|||
{
|
||||
private static List<CultureInfo> _sysCultures = new List<CultureInfo>();
|
||||
private static Dictionary<string, RegionInfo> _regions = new Dictionary<string, RegionInfo>();
|
||||
private static readonly ILogger _logger = LogService.RegisterLogger(typeof(Culture));
|
||||
|
||||
/// <summary>
|
||||
/// Get all system known cultures.
|
||||
|
@ -14,6 +16,7 @@ public static class Culture
|
|||
/// <returns>The list with <see cref="CultureInfo"/>'s the system knows</returns>
|
||||
public static IEnumerable<CultureInfo> GetSystemCultures()
|
||||
{
|
||||
_logger.Debug("Getting system cultures...");
|
||||
if (_sysCultures.Count == 0)
|
||||
_sysCultures = CultureInfo.GetCultures(CultureTypes.AllCultures).ToList();
|
||||
return _sysCultures;
|
||||
|
|
72
TestWebApi/Program.cs
Normal file
72
TestWebApi/Program.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using DotBased.Logging;
|
||||
using DotBased.Logging.MEL;
|
||||
using DotBased.Logging.Serilog;
|
||||
using Serilog;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
LogService.Initialize(options =>
|
||||
{
|
||||
options
|
||||
.AddSeverityFilter("Program", LogSeverity.Verbose);
|
||||
});
|
||||
|
||||
var serilogLogger = SetupSerilog();
|
||||
LogService.AddLogAdapter(new BasedSerilogAdapter(serilogLogger));
|
||||
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Logging.AddDotBased(LogService.Options);
|
||||
|
||||
// Add services to the container.
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
app.MapGet("/weatherforecast", () =>
|
||||
{
|
||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
return forecast;
|
||||
})
|
||||
.WithName("GetWeatherForecast")
|
||||
.WithOpenApi();
|
||||
|
||||
app.Run();
|
||||
|
||||
ILogger SetupSerilog()
|
||||
{
|
||||
var logConfig = new LoggerConfiguration()
|
||||
.MinimumLevel.Verbose()
|
||||
.WriteTo.Console(outputTemplate: BasedSerilog.OutputTemplate);
|
||||
return logConfig.CreateLogger();
|
||||
}
|
||||
|
||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
21
TestWebApi/TestWebApi.csproj
Normal file
21
TestWebApi/TestWebApi.csproj
Normal file
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DotBased.Logging.MEL\DotBased.Logging.MEL.csproj" />
|
||||
<ProjectReference Include="..\DotBased.Logging.Serilog\DotBased.Logging.Serilog.csproj" />
|
||||
<ProjectReference Include="..\DotBased\DotBased.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
6
TestWebApi/TestWebApi.http
Normal file
6
TestWebApi/TestWebApi.http
Normal file
|
@ -0,0 +1,6 @@
|
|||
@TestWebApi_HostAddress = http://localhost:5044
|
||||
|
||||
GET {{TestWebApi_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
Loading…
Reference in New Issue
Block a user