diff --git a/CLI/Program.cs b/CLI/Program.cs index 4f90baf..7d08a20 100755 --- a/CLI/Program.cs +++ b/CLI/Program.cs @@ -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(); -logger.Information("Whoah... Hi!"); +logger.Information("Whoah... Hi!, {Param}", "Test!"); +var cult = Culture.GetSystemCultures(); Console.ReadKey(); // Hold console app open. return; diff --git a/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs b/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs index b2b39c4..c2f7aea 100644 --- a/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs +++ b/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs @@ -18,21 +18,23 @@ public class BasedServerAuthenticationStateProvider : ServerAuthenticationStateP _config = configuration; _localStorage = localStorage; _securityService = securityService; - _logger = LogService.RegisterLogger(typeof(BasedServerAuthenticationStateProvider)); + _logger = LogService.RegisterLogger(); } 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 GetAuthenticationStateAsync() { + _logger.Debug("Getting authentication state..."); var sessionIdResult = await _localStorage.GetAsync(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; } diff --git a/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs b/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs index c64cbca..4366532 100644 --- a/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs +++ b/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs @@ -18,6 +18,7 @@ public static class DotBasedAuthDependencyInjection var Configuration = new BasedAuthConfiguration(); configurationAction?.Invoke(Configuration); + services.AddSingleton(Configuration); if (Configuration.AuthDataRepositoryType == null) throw new ArgumentNullException(nameof(Configuration.AuthDataRepositoryType), $"No '{nameof(IAuthDataRepository)}' configured!"); diff --git a/DotBased.ASP.Auth/MemoryAuthDataRepository.cs b/DotBased.ASP.Auth/MemoryAuthDataRepository.cs index 01edad6..708db7a 100644 --- a/DotBased.ASP.Auth/MemoryAuthDataRepository.cs +++ b/DotBased.ASP.Auth/MemoryAuthDataRepository.cs @@ -34,11 +34,11 @@ public class MemoryAuthDataRepository : IAuthDataRepository public async Task> 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.Ok(userModel) : Result.Failed("No user found!"); } diff --git a/DotBased.ASP.Auth/Services/SecurityService.cs b/DotBased.ASP.Auth/Services/SecurityService.cs index c17da21..851c0a5 100644 --- a/DotBased.ASP.Auth/Services/SecurityService.cs +++ b/DotBased.ASP.Auth/Services/SecurityService.cs @@ -16,7 +16,7 @@ public class SecurityService _authDataRepository = authDataRepository; _dataCache = dataCache; _localStorage = localStorage; - _logger = LogService.RegisterLogger(typeof(SecurityService)); + _logger = LogService.RegisterLogger(); } private readonly IAuthDataRepository _authDataRepository; @@ -26,7 +26,7 @@ public class SecurityService public async Task> GetAuthenticationStateFromSessionAsync(string id) { - if (id.IsNullOrWhiteSpace()) + if (id.IsNullOrEmpty()) return Result.Failed("No valid id!"); AuthenticationStateModel? authStateModel = null; var stateCache = _dataCache.RequestSessionState(id); @@ -75,13 +75,13 @@ public class SecurityService { UserModel? user = null; Result 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); diff --git a/DotBased.Logging.MEL/BasedLogger.cs b/DotBased.Logging.MEL/BasedLogger.cs new file mode 100644 index 0000000..8ea49a4 --- /dev/null +++ b/DotBased.Logging.MEL/BasedLogger.cs @@ -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(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (!IsEnabled(logLevel)) + return; + var severity = ConvertLogLevelToSeverity(logLevel); + var capsule = ConstructCapsule(severity, eventId, state, exception, formatter); + basedLogger.Log(capsule); + } + + private LogCapsule ConstructCapsule(LogSeverity severity, EventId eventId, TState state, Exception? exception, Func formatter) + { + //TODO: Extract parameters & format + if (state is IEnumerable> 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 state) where TState : notnull => default; +} \ No newline at end of file diff --git a/DotBased.Logging.MEL/BasedLoggerProvider.cs b/DotBased.Logging.MEL/BasedLoggerProvider.cs new file mode 100644 index 0000000..3908fa9 --- /dev/null +++ b/DotBased.Logging.MEL/BasedLoggerProvider.cs @@ -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() + { + + } +} \ No newline at end of file diff --git a/DotBased.Logging.MEL/DotBased.Logging.MEL.csproj b/DotBased.Logging.MEL/DotBased.Logging.MEL.csproj new file mode 100644 index 0000000..9b5d862 --- /dev/null +++ b/DotBased.Logging.MEL/DotBased.Logging.MEL.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.1 + enable + enable + 12 + + + + + + + + + + + diff --git a/DotBased.Logging.MEL/LoggerBuilderExtensions.cs b/DotBased.Logging.MEL/LoggerBuilderExtensions.cs new file mode 100644 index 0000000..ddde1d6 --- /dev/null +++ b/DotBased.Logging.MEL/LoggerBuilderExtensions.cs @@ -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; + } +} \ No newline at end of file diff --git a/DotBased.Logging.Serilog/BasedSerilog.cs b/DotBased.Logging.Serilog/BasedSerilog.cs index 7586298..9f7106f 100644 --- a/DotBased.Logging.Serilog/BasedSerilog.cs +++ b/DotBased.Logging.Serilog/BasedSerilog.cs @@ -7,7 +7,7 @@ public static class BasedSerilog /// /// Default output template with the extra properties that can be used for serilog sinks. /// - 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 /// 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"; } } \ No newline at end of file diff --git a/DotBased.Logging.Serilog/BasedSerilogAdapter.cs b/DotBased.Logging.Serilog/BasedSerilogAdapter.cs index 7d2e3e1..9372d4c 100755 --- a/DotBased.Logging.Serilog/BasedSerilogAdapter.cs +++ b/DotBased.Logging.Serilog/BasedSerilogAdapter.cs @@ -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? properties = null; diff --git a/DotBased.Logging.Serilog/BasedSerilogEnricher.cs b/DotBased.Logging.Serilog/BasedSerilogEnricher.cs index c6dcd15..940fc9f 100644 --- a/DotBased.Logging.Serilog/BasedSerilogEnricher.cs +++ b/DotBased.Logging.Serilog/BasedSerilogEnricher.cs @@ -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); diff --git a/DotBased.Logging.Serilog/DotBased.Logging.Serilog.csproj b/DotBased.Logging.Serilog/DotBased.Logging.Serilog.csproj index 2e065aa..e43e411 100755 --- a/DotBased.Logging.Serilog/DotBased.Logging.Serilog.csproj +++ b/DotBased.Logging.Serilog/DotBased.Logging.Serilog.csproj @@ -1,7 +1,7 @@ - net8.0 + netstandard2.1 enable default diff --git a/DotBased.sln b/DotBased.sln index 7a653af..9e42709 100755 --- a/DotBased.sln +++ b/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 diff --git a/DotBased/Collections/InstanceContainer.cs b/DotBased/Collections/InstanceContainer.cs index beb2265..4f25ece 100755 --- a/DotBased/Collections/InstanceContainer.cs +++ b/DotBased/Collections/InstanceContainer.cs @@ -8,7 +8,7 @@ namespace DotBased.Collections; /// public class InstanceContainer : IDisposable { - private readonly ILogger _log = LogService.RegisterLogger(typeof(InstanceContainer)); + private readonly ILogger _log = LogService.RegisterLogger(); private readonly Dictionary _tCollection = new Dictionary(); /// @@ -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; } diff --git a/DotBased/DotBased.csproj b/DotBased/DotBased.csproj index 291f62d..abda654 100755 --- a/DotBased/DotBased.csproj +++ b/DotBased/DotBased.csproj @@ -1,10 +1,10 @@ - net8.0 enable enable default + netstandard2.1 diff --git a/DotBased/Extensions/StringExtensions.cs b/DotBased/Extensions/StringExtensions.cs index 7d9737d..bbe0160 100755 --- a/DotBased/Extensions/StringExtensions.cs +++ b/DotBased/Extensions/StringExtensions.cs @@ -5,5 +5,5 @@ namespace DotBased.Extensions; /// public static class StringExtensions { - public static bool IsNullOrWhiteSpace(this string s) => string.IsNullOrWhiteSpace(s); + public static bool IsNullOrEmpty(this string s) => string.IsNullOrWhiteSpace(s); } \ No newline at end of file diff --git a/DotBased/Logging/ILogger.cs b/DotBased/Logging/ILogger.cs index 2b809f6..734f91f 100755 --- a/DotBased/Logging/ILogger.cs +++ b/DotBased/Logging/ILogger.cs @@ -5,6 +5,7 @@ namespace DotBased.Logging; /// 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; } } \ No newline at end of file diff --git a/DotBased/Logging/LogAdapterBase.cs b/DotBased/Logging/LogAdapterBase.cs index 863620c..6dcbb01 100755 --- a/DotBased/Logging/LogAdapterBase.cs +++ b/DotBased/Logging/LogAdapterBase.cs @@ -1,7 +1,7 @@ namespace DotBased.Logging; /// -/// The base for creating log adpaters. +/// The base for creating log adapters. /// public abstract class LogAdapterBase { @@ -19,7 +19,7 @@ public abstract class LogAdapterBase public string AdapterName { get; } /// - /// Handle the incomming that the sends. + /// Handle the incoming that the sends. /// /// The log processor that has processed this log /// The log capsule, which contains the log information diff --git a/DotBased/Logging/LogOptions.cs b/DotBased/Logging/LogOptions.cs index b2dd560..26009bd 100755 --- a/DotBased/Logging/LogOptions.cs +++ b/DotBased/Logging/LogOptions.cs @@ -1,3 +1,6 @@ +using System.Collections.ObjectModel; +using DotBased.Extensions; + namespace DotBased.Logging; /// @@ -5,14 +8,35 @@ namespace DotBased.Logging; /// public class LogOptions { + public readonly SeverityFilterCollection SeverityFilters = []; + /// - /// The severty the logger will log + /// The severity the logger will log /// public LogSeverity Severity { get; set; } = LogSeverity.Trace; /// - /// The function that will build and return the when calling , so a custom logger based on the can be used. + /// The function that will build and return the . /// - public Func, LoggerBase> LoggerBuilder { get; set; } = - (identifier, sendEvent) => new Logger(identifier, ref sendEvent); + public Func 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 +{ + protected override string GetKeyForItem(SeverityFilter item) => item.Filter; } \ No newline at end of file diff --git a/DotBased/Logging/LogProcessor.cs b/DotBased/Logging/LogProcessor.cs index e0ce76a..3096afd 100755 --- a/DotBased/Logging/LogProcessor.cs +++ b/DotBased/Logging/LogProcessor.cs @@ -8,7 +8,7 @@ public class LogProcessor : IDisposable public LogProcessor() { _processorQueue = new Queue(); - IncommingLogHandlerEvent = IncommingLogHandler; + IncomingLogHandlerEvent = IncomingLogHandler; _processorThread = new Thread(ProcessLog) { IsBackground = true, @@ -16,7 +16,7 @@ public class LogProcessor : IDisposable }; _processorThread.Start(); } - public readonly Action IncommingLogHandlerEvent; + public readonly Action IncomingLogHandlerEvent; public event EventHandler? LogProcessed; private readonly Queue _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 diff --git a/DotBased/Logging/LogService.cs b/DotBased/Logging/LogService.cs index 8584f30..701db32 100755 --- a/DotBased/Logging/LogService.cs +++ b/DotBased/Logging/LogService.cs @@ -1,5 +1,6 @@ using System.Collections.ObjectModel; using System.Reflection; +using DotBased.Extensions; namespace DotBased.Logging; @@ -8,52 +9,43 @@ namespace DotBased.Logging; /// 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 Adapters { get; } = new HashSet(); - private static HashSet Loggers { get; } = new HashSet(); + private static HashSet Adapters { get; } = []; + private static HashSet Loggers { get; } = []; /// /// Action for internal communication between loggers and processor /// - private static readonly Action LoggerSendEvent; + internal static readonly Action LoggerSendEvent; - /// - /// 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. - /// - /// - /// - /// public class Program - /// { - /// public Program - /// { - /// logger = LogService.RegisterLogger(nameof(Program)); - /// } - /// private ILogger logger; - /// } - /// - /// - /// The type that called the function - /// The configured implementation that will be configuered in the at the class - public static ILogger RegisterLogger(Type callerType) + public static void Initialize(Action? 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); + /// + /// Register a logger. + /// + /// The configured implementation that will be configured in the at the class + public static ILogger RegisterLogger() => RegisterLogger(typeof(T), string.Empty); - public static ReadOnlyCollection GetLoggers => new ReadOnlyCollection(Loggers.ToList()); + public static ReadOnlyCollection GetLoggers => new ReadOnlyCollection(Loggers.ToList()); /// /// Add a log adapter to the service. @@ -69,7 +61,7 @@ public static class LogService /// Removes the log adapter from the service. /// /// The adapter to remove - /// True if the adapter is succesfully removed otherwise false. + /// True if the adapter is successfully removed otherwise false. 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 GetAdapters => - new ReadOnlyCollection(Adapters.ToList()); + public static ReadOnlyCollection GetAdapters => new ReadOnlyCollection(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; } \ No newline at end of file diff --git a/DotBased/Logging/LogSeverity.cs b/DotBased/Logging/LogSeverity.cs index edb2da3..09713ae 100755 --- a/DotBased/Logging/LogSeverity.cs +++ b/DotBased/Logging/LogSeverity.cs @@ -8,5 +8,6 @@ public enum LogSeverity Info = 3, Warning = 4, Error = 5, - Fatal = 6 + Fatal = 6, + Ignore = 99 } \ No newline at end of file diff --git a/DotBased/Logging/Logger.cs b/DotBased/Logging/Logger.cs index b5a673a..79db121 100755 --- a/DotBased/Logging/Logger.cs +++ b/DotBased/Logging/Logger.cs @@ -3,13 +3,8 @@ namespace DotBased.Logging; /// /// Main logger, this class is the default logger that the function will return. /// -public class Logger(CallerInformation caller, ref Action 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 logProcesso }); } - public override int GetHashCode() => HashCode.Combine(Caller.Source, Caller.AssemblyFullname); + public override int GetHashCode() => HashCode.Combine(LoggerInformation.TypeFullName, LoggerInformation.AssemblyFullname); } \ No newline at end of file diff --git a/DotBased/Logging/LoggerBase.cs b/DotBased/Logging/LoggerBase.cs index 2c73653..e7a1f00 100755 --- a/DotBased/Logging/LoggerBase.cs +++ b/DotBased/Logging/LoggerBase.cs @@ -1,15 +1,23 @@ +using DotBased.Extensions; + namespace DotBased.Logging; /// /// Base for creating loggers /// -/// The caller information -/// The handler where the logs can be send to -public abstract class LoggerBase(CallerInformation caller, ref Action logProcessorHandler) : ILogger +/// The caller information +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 ProcessLog = logProcessorHandler; + private readonly Action 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); diff --git a/DotBased/Objects/DbObjectAttribute.cs b/DotBased/Objects/DbObjectAttribute.cs index 5a6eb78..2ce8931 100644 --- a/DotBased/Objects/DbObjectAttribute.cs +++ b/DotBased/Objects/DbObjectAttribute.cs @@ -2,11 +2,11 @@ using DotBased.Extensions; namespace DotBased.Objects; -public class DbObjectAttribute : ObjectAttribute +public class DbObjectAttribute : ObjectAttribute 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; } diff --git a/DotBased/Objects/ObjectAttribute.cs b/DotBased/Objects/ObjectAttribute.cs index a56298d..791b1ce 100644 --- a/DotBased/Objects/ObjectAttribute.cs +++ b/DotBased/Objects/ObjectAttribute.cs @@ -6,7 +6,7 @@ public class ObjectAttribute : IObjectAttribute { 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; diff --git a/DotBased/Utilities/Culture.cs b/DotBased/Utilities/Culture.cs index cc2c725..7411011 100755 --- a/DotBased/Utilities/Culture.cs +++ b/DotBased/Utilities/Culture.cs @@ -1,4 +1,5 @@ using System.Globalization; +using DotBased.Logging; namespace DotBased.Utilities; @@ -6,6 +7,7 @@ public static class Culture { private static List _sysCultures = new List(); private static Dictionary _regions = new Dictionary(); + private static readonly ILogger _logger = LogService.RegisterLogger(typeof(Culture)); /// /// Get all system known cultures. @@ -14,6 +16,7 @@ public static class Culture /// The list with 's the system knows public static IEnumerable GetSystemCultures() { + _logger.Debug("Getting system cultures..."); if (_sysCultures.Count == 0) _sysCultures = CultureInfo.GetCultures(CultureTypes.AllCultures).ToList(); return _sysCultures; diff --git a/TestWebApi/Program.cs b/TestWebApi/Program.cs new file mode 100644 index 0000000..25a52d2 --- /dev/null +++ b/TestWebApi/Program.cs @@ -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); +} \ No newline at end of file diff --git a/TestWebApi/TestWebApi.csproj b/TestWebApi/TestWebApi.csproj new file mode 100644 index 0000000..c3c9828 --- /dev/null +++ b/TestWebApi/TestWebApi.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/TestWebApi/TestWebApi.http b/TestWebApi/TestWebApi.http new file mode 100644 index 0000000..d2addab --- /dev/null +++ b/TestWebApi/TestWebApi.http @@ -0,0 +1,6 @@ +@TestWebApi_HostAddress = http://localhost:5044 + +GET {{TestWebApi_HostAddress}}/weatherforecast/ +Accept: application/json + +###