diff --git a/EonaCat.Logger/EonaCat.Logger.csproj b/EonaCat.Logger/EonaCat.Logger.csproj index 3f2d3b3..b06968c 100644 --- a/EonaCat.Logger/EonaCat.Logger.csproj +++ b/EonaCat.Logger/EonaCat.Logger.csproj @@ -2,9 +2,9 @@ .netstandard2.1; net8.0; net4.8; icon.ico - 1.4.8 + 1.4.9 latest - 1.4.8 + 1.4.9 EonaCat (Jeroen Saey) true EonaCat (Jeroen Saey) @@ -25,7 +25,7 @@ - 1.4.8+{chash:10}.{c:ymd} + 1.4.9+{chash:10}.{c:ymd} true true v[0-9]* diff --git a/EonaCat.Logger/Extensions/DateTimeExtensions.cs b/EonaCat.Logger/Extensions/DateTimeExtensions.cs index 0ffb519..4907149 100644 --- a/EonaCat.Logger/Extensions/DateTimeExtensions.cs +++ b/EonaCat.Logger/Extensions/DateTimeExtensions.cs @@ -2,6 +2,9 @@ namespace EonaCat.Logger.Extensions; +// This file is part of the EonaCat project(s) which is released under the Apache License. +// See the LICENSE file or go to https://EonaCat.com/License for full license details. + public static class DateTimeExtensions { public static long ToUnixTimestamp(this DateTime dateTime) diff --git a/EonaCat.Logger/Extensions/ExceptionExtensions.cs b/EonaCat.Logger/Extensions/ExceptionExtensions.cs index 4169a3c..cc27a79 100644 --- a/EonaCat.Logger/Extensions/ExceptionExtensions.cs +++ b/EonaCat.Logger/Extensions/ExceptionExtensions.cs @@ -1,98 +1,101 @@ -using System; -using System.Collections; -using System.Diagnostics; -using System.Text; - -namespace EonaCat.Logger.Extensions; - -public static class ExceptionExtensions -{ - public static string FormatExceptionToMessage(this Exception exception, string module = null, string method = null) - { - if (exception == null) - { - return string.Empty; - } - - var st = new StackTrace(exception, true); - var frame = st.GetFrame(0); - int fileLine = -1; - string filename = "Unknown"; - - if (frame != null) - { - fileLine = frame.GetFileLineNumber(); - filename = frame.GetFileName(); - } - - var sb = new StringBuilder(); - - sb.AppendLine(); - sb.AppendLine($"--- Exception details provided by {DllInfo.ApplicationName} ---"); - if (!string.IsNullOrEmpty(module)) - { - sb.AppendLine(" Module : " + module); - } - - if (!string.IsNullOrEmpty(method)) - { - sb.AppendLine(" Method : " + method); - } - - sb.Append(" Type : ").AppendLine(exception.GetType().ToString()); - sb.Append(" Data : ").AppendLine(exception.Data != null && exception.Data.Count > 0 - ? FormatExceptionData(exception.Data) - : "(none)"); - sb.Append(" Inner : ").AppendLine(exception.InnerException != null - ? FormatInnerException(exception.InnerException) - : "(null)"); - sb.Append(" Message : ").AppendLine(exception.Message); - sb.Append(" Source : ").AppendLine(exception.Source); - sb.Append(" StackTrace : ").AppendLine(exception.StackTrace); - sb.Append(" Line : ").AppendLine(fileLine.ToString()); - sb.Append(" File : ").AppendLine(filename); - sb.Append(" ToString : ").AppendLine(exception.ToString()); - sb.AppendLine("---"); - - return sb.ToString(); - } - - private static string FormatExceptionData(IDictionary data) - { - var sb = new StringBuilder(); - - foreach (DictionaryEntry entry in data) - { +using System; +using System.Collections; +using System.Diagnostics; +using System.Text; + +namespace EonaCat.Logger.Extensions; + +// This file is part of the EonaCat project(s) which is released under the Apache License. +// See the LICENSE file or go to https://EonaCat.com/License for full license details. + +public static class ExceptionExtensions +{ + public static string FormatExceptionToMessage(this Exception exception, string module = null, string method = null) + { + if (exception == null) + { + return string.Empty; + } + + var st = new StackTrace(exception, true); + var frame = st.GetFrame(0); + int fileLine = -1; + string filename = "Unknown"; + + if (frame != null) + { + fileLine = frame.GetFileLineNumber(); + filename = frame.GetFileName(); + } + + var sb = new StringBuilder(); + + sb.AppendLine(); + sb.AppendLine($"--- Exception details provided by {DllInfo.ApplicationName} ---"); + if (!string.IsNullOrEmpty(module)) + { + sb.AppendLine(" Module : " + module); + } + + if (!string.IsNullOrEmpty(method)) + { + sb.AppendLine(" Method : " + method); + } + + sb.Append(" Type : ").AppendLine(exception.GetType().ToString()); + sb.Append(" Data : ").AppendLine(exception.Data != null && exception.Data.Count > 0 + ? FormatExceptionData(exception.Data) + : "(none)"); + sb.Append(" Inner : ").AppendLine(exception.InnerException != null + ? FormatInnerException(exception.InnerException) + : "(null)"); + sb.Append(" Message : ").AppendLine(exception.Message); + sb.Append(" Source : ").AppendLine(exception.Source); + sb.Append(" StackTrace : ").AppendLine(exception.StackTrace); + sb.Append(" Line : ").AppendLine(fileLine.ToString()); + sb.Append(" File : ").AppendLine(filename); + sb.Append(" ToString : ").AppendLine(exception.ToString()); + sb.AppendLine("---"); + + return sb.ToString(); + } + + private static string FormatExceptionData(IDictionary data) + { + var sb = new StringBuilder(); + + foreach (DictionaryEntry entry in data) + { if (entry.Key != null) { - sb.Append(" | ") + sb.Append(" | ") .Append(entry.Key); - } - + } + if (entry.Value != null) { - sb.Append(": ") + sb.Append(": ") .AppendLine(entry.Value.ToString()); - } - } - - return sb.ToString(); - } - - private static string FormatInnerException(Exception innerException) - { - var sb = new StringBuilder(); - - sb.AppendLine(innerException.GetType().ToString()) - .AppendLine(" Message : " + innerException.Message) - .AppendLine(" Source : " + innerException.Source) - .AppendLine(" StackTrace : " + innerException.StackTrace) - .AppendLine(" ToString : " + innerException) - .Append(" Data : ") - .AppendLine(innerException.Data != null && innerException.Data.Count > 0 - ? FormatExceptionData(innerException.Data) - : "(none)"); - - return sb.ToString(); - } + } + } + + return sb.ToString(); + } + + private static string FormatInnerException(Exception innerException) + { + var sb = new StringBuilder(); + + sb.AppendLine(innerException.GetType().ToString()) + .AppendLine(" Message : " + innerException.Message) + .AppendLine(" Source : " + innerException.Source) + .AppendLine(" StackTrace : " + innerException.StackTrace) + .AppendLine(" ToString : " + innerException) + .Append(" Data : ") + .AppendLine(innerException.Data != null && innerException.Data.Count > 0 + ? FormatExceptionData(innerException.Data) + : "(none)"); + + return sb.ToString(); + } } \ No newline at end of file diff --git a/EonaCat.Logger/Extensions/ObjectExtensions.cs b/EonaCat.Logger/Extensions/ObjectExtensions.cs new file mode 100644 index 0000000..31f5289 --- /dev/null +++ b/EonaCat.Logger/Extensions/ObjectExtensions.cs @@ -0,0 +1,425 @@ +using EonaCat.Json; +using EonaCat.Json.Serialization; +using EonaCat.Logger.Managers; +using Microsoft.Extensions.Logging; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace EonaCat.Logger.Extensions +{ + // This file is part of the EonaCat project(s) which is released under the Apache License. + // See the LICENSE file or go to https://EonaCat.com/License for full license details. + + public enum DumpFormat + { + Json, + Xml, + Tree + } + + public static class ObjectExtensions + { + /// + /// Dumps any object to a string in JSON, XML, or detailed tree format. + /// + /// Object to dump + /// "json" (default), "xml", or "tree" + /// For JSON: include private/internal fields. Ignored for tree format + /// Optional max depth for tree dump. Null = no limit + /// Optional max items to display in collections. Null = show all + /// String representation of the object + public static string Dump(this object currentObject, DumpFormat format = DumpFormat.Json, bool detailed = false, int? maxDepth = null, int? maxCollectionItems = null) + { + if (currentObject == null) + { + return "null"; + } + + try + { + switch (format) + { + case DumpFormat.Xml: + return DumpXml(currentObject); + case DumpFormat.Tree: + return DumpTree(currentObject, maxDepth, maxCollectionItems); + case DumpFormat.Json: + default: + return DumpJson(currentObject, detailed); + } + } + catch (Exception ex) + { + return $"Error dumping object: {ex.Message}"; + } + } + + private static string DumpJson(object currentObject, bool isDetailed) + { + var settings = new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + Formatting = Formatting.Indented + }; + + if (isDetailed) + { + settings.ContractResolver = new DefaultContractResolver + { + IgnoreSerializableAttribute = false, + IgnoreSerializableInterface = false + }; + } + + return JsonHelper.ToJson(currentObject, settings); + } + + private static string DumpXml(object currentObject) + { + try + { + var xmlSerializer = new XmlSerializer(currentObject.GetType()); + using (var stringWriter = new StringWriter()) + { + xmlSerializer.Serialize(stringWriter, currentObject); + return stringWriter.ToString(); + } + } + catch (Exception ex) + { + return $"XML serialization failed: {ex.Message}"; + } + } + + private static string DumpTree(object currentObject, int? maxDepth, int? maxCollectionItems) + { + var stringBuilder = new StringBuilder(); + var visitedHashSet = new HashSet(new ReferenceEqualityComparer()); + DumpTreeInternal(currentObject, stringBuilder, 0, visitedHashSet, maxDepth, maxCollectionItems); + return stringBuilder.ToString(); + } + + private static void DumpTreeInternal(object currentObject, StringBuilder stringBuilder, int indent, HashSet visited, int? maxDepth, int? maxCollectionItems) + { + string indentation = new string(' ', indent * 2); + + if (currentObject == null) + { + stringBuilder.AppendLine($"{indentation}null"); + return; + } + + Type type = currentObject.GetType(); + string typeName = type.FullName; + + if (IsPrimitive(type)) + { + stringBuilder.AppendLine($"{indentation}{currentObject} ({typeName})"); + return; + } + + if (visited.Contains(currentObject)) + { + stringBuilder.AppendLine($"{indentation}<>"); + return; + } + + if (maxDepth.HasValue && indent >= maxDepth.Value) + { + stringBuilder.AppendLine($"{indentation}<>"); + return; + } + + visited.Add(currentObject); + + if (currentObject is IEnumerable enumerable && !(currentObject is string)) + { + int count = 0; + + foreach (var _ in enumerable) + { + count++; + } + + if (maxCollectionItems.HasValue && count > maxCollectionItems.Value) + { + stringBuilder.AppendLine($"{indentation}{typeName} [<<{count} items, collapsed>>]"); + return; + } + + stringBuilder.AppendLine($"{indentation}{typeName} ["); + + foreach (var item in enumerable) + { + DumpTreeInternal(item, stringBuilder, indent + 1, visited, maxDepth, maxCollectionItems); + } + stringBuilder.AppendLine($"{indentation}]"); + } + else + { + stringBuilder.AppendLine($"{indentation}{typeName} {{"); + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var members = type.GetFields(flags); + + foreach (var field in members) + { + object value = null; + + try + { + value = field.GetValue(currentObject); + } + catch + { + value = "<>"; + } + + stringBuilder.Append($"{indentation} {field.Name} = "); + DumpTreeInternal(value, stringBuilder, indent + 1, visited, maxDepth, maxCollectionItems); + } + + var properties = type.GetProperties(flags); + + foreach (var current in properties) + { + if (current.GetIndexParameters().Length > 0) + { + continue; + } + + object value = null; + try { value = current.GetValue(currentObject); } catch { value = "<>"; } + stringBuilder.Append($"{indentation} {current.Name} = "); + DumpTreeInternal(value, stringBuilder, indent + 1, visited, maxDepth, maxCollectionItems); + } + + stringBuilder.AppendLine($"{indentation}}}"); + } + } + + private static bool IsPrimitive(Type type) + { + return type.IsPrimitive + || type.IsEnum + || type == typeof(string) + || type == typeof(decimal) + || type == typeof(DateTime) + || type == typeof(DateTimeOffset) + || type == typeof(Guid) + || type == typeof(TimeSpan); + } + + private class ReferenceEqualityComparer : IEqualityComparer + { + public new bool Equals(object x, object y) => ReferenceEquals(x, y); + public int GetHashCode(object obj) => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); + } + + public static void ForEach(this IEnumerable items, Action action) + { + if (items == null || action == null) + { + return; + } + + foreach (var item in items) + { + action(item); + } + } + + /// Check if collection is null or empty + public static bool IsNullOrEmpty(this IEnumerable items) => items == null || !items.Any(); + + /// Check if collection has items + public static bool HasItems(this IEnumerable items) => !items.IsNullOrEmpty(); + + /// Safe get by index + public static T SafeGet(this IList list, int index, T defaultValue = default) + { + if (list == null || index < 0 || index >= list.Count) + { + return defaultValue; + } + + return list[index]; + } + + /// Convert collection to delimited string + public static string ToDelimitedString(this IEnumerable items, string delimiter = ", ") + { + return items == null ? "" : string.Join(delimiter, items); + } + + public static bool IsNullOrWhiteSpace(this string s) => string.IsNullOrWhiteSpace(s); + + public static string Truncate(this string s, int maxLength) + { + if (string.IsNullOrEmpty(s)) + { + return s; + } + + return s.Length <= maxLength ? s : s.Substring(0, maxLength); + } + + public static bool ContainsIgnoreCase(this string s, string value) => + s?.IndexOf(value ?? "", StringComparison.OrdinalIgnoreCase) >= 0; + + public static string OrDefault(this string s, string defaultValue) => + string.IsNullOrEmpty(s) ? defaultValue : s; + + public static bool IsWeekend(this DateTime date) => + date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday; + + public static DateTime StartOfDay(this DateTime date) => + date.Date; + + public static DateTime EndOfDay(this DateTime date) => + date.Date.AddDays(1).AddTicks(-1); + + /// + /// Log an object only if a condition is true. + /// + public static async Task LogIfAsync(this LogManager logger, object currentObject, Func condition, + ELogType logType = ELogType.INFO, string message = null) + { + if (logger == null || currentObject == null || condition == null) + { + return; + } + + if (condition(currentObject)) + { + string output = message ?? currentObject.Dump(); + await logger.WriteAsync(output, logType); + } + } + + + public static IDisposable BeginLoggingScope(this ILogger logger, object context) + { + if (logger == null || context == null) + { + return null; + } + + return logger.BeginScope(context.ToDictionary()); + } + + public static void LogExecutionTime(this ILogger logger, Action action, string operationName) + { + if (logger == null || action == null) + { + return; + } + + var sw = System.Diagnostics.Stopwatch.StartNew(); + action(); + sw.Stop(); + logger.LogInformation("{Operation} executed in {ElapsedMilliseconds}ms", operationName, sw.ElapsedMilliseconds); + } + + /// + /// Converts a Unix timestamp, expressed as the number of seconds since the Unix epoch, to a local DateTime + /// value. + /// + /// The returned DateTime is expressed in the local time zone. To obtain a UTC DateTime, + /// use DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime instead. + /// The Unix timestamp representing the number of seconds that have elapsed since 00:00:00 UTC on 1 January + /// 1970. + /// A DateTime value that represents the local date and time equivalent of the specified Unix timestamp. + public static DateTime FromUnixTimestamp(this long timestamp) => + DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime; + + /// + /// Executes the specified task without waiting for its completion and handles any exceptions that occur during + /// its execution. + /// + /// Use this method to start a task when you do not need to await its completion but want + /// to ensure that exceptions are observed. This method should be used with caution, as exceptions may be + /// handled asynchronously and may not be propagated to the calling context. Avoid using this method for tasks + /// that must complete before continuing execution. + /// The task to execute in a fire-and-forget manner. Cannot be null. + /// An optional callback that is invoked if the task throws an exception. If not provided, exceptions are + /// written to the console. + public static async void FireAndForget(this Task task, Action onError = null) + { + if (task == null) + { + return; + } + + try { await task; } + catch (Exception ex) + { + if (onError != null) + { + onError(ex); + } + else + { + Console.WriteLine("FireAndForget Exception: " + ex.FormatExceptionToMessage()); + } + } + } + + /// Check if object has property + public static bool HasProperty(this object obj, string name) => + obj != null && obj.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null; + + /// Get property value safely + public static object GetPropertyValue(this object obj, string name) + { + if (obj == null) + { + return null; + } + + var prop = obj.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + return prop?.GetValue(obj); + } + + /// + /// Creates a dictionary containing the public and non-public instance properties and fields of the specified + /// object. + /// + /// Indexed properties are excluded from the resulting dictionary. Both public and + /// non-public instance members are included. If multiple members share the same name, property values will + /// overwrite field values with the same name. + /// The object whose properties and fields are to be included in the dictionary. Can be null. + /// A dictionary with the names and values of the object's properties and fields. If the object is null, returns + /// an empty dictionary. + public static Dictionary ToDictionary(this object obj) + { + if (obj == null) + { + return new Dictionary(); + } + + var dict = new Dictionary(); + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + foreach (var prop in obj.GetType().GetProperties(flags)) + { + if (prop.GetIndexParameters().Length > 0) + { + continue; + } + + dict[prop.Name] = prop.GetValue(obj); + } + foreach (var field in obj.GetType().GetFields(flags)) + { + dict[field.Name] = field.GetValue(obj); + } + return dict; + } + } +} diff --git a/EonaCat.Logger/Managers/LogManager.cs b/EonaCat.Logger/Managers/LogManager.cs index ecce89d..5ccba7f 100644 --- a/EonaCat.Logger/Managers/LogManager.cs +++ b/EonaCat.Logger/Managers/LogManager.cs @@ -76,6 +76,21 @@ namespace EonaCat.Logger.Managers GC.SuppressFinalize(this); } + public async Task WriteAsync(object currentObject, ELogType logType = ELogType.INFO, bool? writeToConsole = null, + string customSplunkSourceType = null, + string grayLogFacility = null, string grayLogSource = null, + string grayLogVersion = "1.1", bool disableSplunkSSL = false, DumpFormat dumpFormat = DumpFormat.Json, bool isDetailedDump = false, int? dumpDepth = null, int? maxCollectionItems = null) + { + if (currentObject == null) + { + return; + } + + await WriteAsync(currentObject.Dump(dumpFormat, isDetailedDump, dumpDepth, maxCollectionItems), logType, writeToConsole, + customSplunkSourceType, grayLogFacility, grayLogSource, + grayLogVersion, disableSplunkSSL); + } + public async Task WriteAsync(Exception exception, string module = null, string method = null, bool criticalException = false, bool? writeToConsole = null, string customSplunkSourceType = null, string grayLogFacility = null, diff --git a/EonaCat.Logger/Managers/LoggerSettings.cs b/EonaCat.Logger/Managers/LoggerSettings.cs index 5549231..a27532d 100644 --- a/EonaCat.Logger/Managers/LoggerSettings.cs +++ b/EonaCat.Logger/Managers/LoggerSettings.cs @@ -1,343 +1,343 @@ -using System; -using System.Collections.Generic; -using System.Net.Sockets; -using System.Text.RegularExpressions; -using EonaCat.Json.Linq; -using EonaCat.Logger.EonaCatCoreLogger; -using EonaCat.Logger.EonaCatCoreLogger.Models; -using EonaCat.Logger.Servers.GrayLog; - -namespace EonaCat.Logger.Managers; -// This file is part of the EonaCat project(s) which is released under the Apache License. -// See the LICENSE file or go to https://EonaCat.com/License for full license details. - -/// -/// Logger settings. -/// -public class LoggerSettings -{ - public delegate void LogDelegate(EonaCatLogMessage message); - - private ColorSchema _colors = new(); - private bool _enableConsole = true; - - private FileLoggerOptions _fileLoggerOptions; - - private string _headerFormat = "{ts} {host} {category} {thread} {sev}"; - private string _timestampFormat = "yyyy-MM-dd HH:mm:ss"; - - /// - /// Determines if we need to use the local time in the logging or UTC (default:false) - /// - public bool UseLocalTime { get; set; } - - public string Id { get; set; } = DllInfo.ApplicationName; - - /// - /// Header format. Provide a string that specifies how the preamble of each message should be structured. You can use - /// variables including: - /// {ts}: UTC timestamp - /// {host}: Hostname - /// {category}: Category - /// {thread}: Thread ID - /// {sev}: Severity - /// Default: {ts} {host} {category} {thread} {sev} - /// A space will be inserted between the header and the message. - /// - public string HeaderFormat - { - get => _headerFormat; - set - { - if (string.IsNullOrEmpty(value)) - { - _headerFormat = ""; - } - else - { - _headerFormat = value; - } - } - } - - /// - /// Timestamp format. - /// - public string TimestampFormat - { - get => _timestampFormat; - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentNullException(nameof(HeaderFormat)); - } - - _timestampFormat = value; - } - } - - /// - /// Enable or disable console logging. - /// Settings this to true will first validate if a console exists. - /// If a console is not available, it will be set to false. - /// - public bool EnableConsole - { - get => _enableConsole; - set - { - if (value) - { - _enableConsole = ConsoleExists(); - } - else - { - _enableConsole = false; - } - } - } - - /// - /// Enable or disable use of color for console messages. - /// - public bool EnableColors { get; set; } = true; - - /// - /// Colors to use for console messages based on message severity. - /// - public ColorSchema Colors - { - get => _colors; - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(Colors)); - } - - _colors = value; - } - } - - private readonly List _defaultLogTypes = new List { ELogType.INFO, ELogType.WARNING, ELogType.ERROR, ELogType.TRAFFIC, ELogType.DEBUG, ELogType.CRITICAL, ELogType.TRACE }; - private List _logTypes = new List { ELogType.INFO, ELogType.WARNING, ELogType.ERROR, ELogType.TRAFFIC, ELogType.DEBUG, ELogType.CRITICAL, ELogType.TRACE }; - public List TypesToLog - { - get - { +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Text.RegularExpressions; +using EonaCat.Json.Linq; +using EonaCat.Logger.EonaCatCoreLogger; +using EonaCat.Logger.EonaCatCoreLogger.Models; +using EonaCat.Logger.Servers.GrayLog; + +namespace EonaCat.Logger.Managers; +// This file is part of the EonaCat project(s) which is released under the Apache License. +// See the LICENSE file or go to https://EonaCat.com/License for full license details. + +/// +/// Logger settings. +/// +public class LoggerSettings +{ + public delegate void LogDelegate(EonaCatLogMessage message); + + private ColorSchema _colors = new(); + private bool _enableConsole = true; + + private FileLoggerOptions _fileLoggerOptions; + + private string _headerFormat = "{ts} {host} {category} {thread} {sev}"; + private string _timestampFormat = "yyyy-MM-dd HH:mm:ss"; + + /// + /// Determines if we need to use the local time in the logging or UTC (default:false) + /// + public bool UseLocalTime { get; set; } + + public string Id { get; set; } = DllInfo.ApplicationName; + + /// + /// Header format. Provide a string that specifies how the preamble of each message should be structured. You can use + /// variables including: + /// {ts}: UTC timestamp + /// {host}: Hostname + /// {category}: Category + /// {thread}: Thread ID + /// {sev}: Severity + /// Default: {ts} {host} {category} {thread} {sev} + /// A space will be inserted between the header and the message. + /// + public string HeaderFormat + { + get => _headerFormat; + set + { + if (string.IsNullOrEmpty(value)) + { + _headerFormat = ""; + } + else + { + _headerFormat = value; + } + } + } + + /// + /// Timestamp format. + /// + public string TimestampFormat + { + get => _timestampFormat; + set + { + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentNullException(nameof(HeaderFormat)); + } + + _timestampFormat = value; + } + } + + /// + /// Enable or disable console logging. + /// Settings this to true will first validate if a console exists. + /// If a console is not available, it will be set to false. + /// + public bool EnableConsole + { + get => _enableConsole; + set + { + if (value) + { + _enableConsole = ConsoleExists(); + } + else + { + _enableConsole = false; + } + } + } + + /// + /// Enable or disable use of color for console messages. + /// + public bool EnableColors { get; set; } = true; + + /// + /// Colors to use for console messages based on message severity. + /// + public ColorSchema Colors + { + get => _colors; + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(Colors)); + } + + _colors = value; + } + } + + private readonly List _defaultLogTypes = new List { ELogType.INFO, ELogType.WARNING, ELogType.ERROR, ELogType.TRAFFIC, ELogType.DEBUG, ELogType.CRITICAL, ELogType.TRACE }; + private List _logTypes = new List { ELogType.INFO, ELogType.WARNING, ELogType.ERROR, ELogType.TRAFFIC, ELogType.DEBUG, ELogType.CRITICAL, ELogType.TRACE }; + public List TypesToLog + { + get + { if (_logTypes == null) { _logTypes = _defaultLogTypes; - } - return _logTypes; - } - - set + } + return _logTypes; + } + + set { - _logTypes = value; - } - } - - public List SysLogServers { get; internal set; } - public List SplunkServers { get; internal set; } - - public List GrayLogServers { get; internal set; } - public List TcpServers { get; internal set; } - - public List UdpServers { get; internal set; } - - /// - /// Determines if the fileLogging is enabled - /// - public bool EnableFileLogging { get; set; } = true; - - /// - /// FileLogger settings. - /// - public FileLoggerOptions FileLoggerOptions - { - get - { - if (_fileLoggerOptions == null) - { - _fileLoggerOptions = CreateDefaultFileLoggerOptions(); - } - - return _fileLoggerOptions; - } - - set => _fileLoggerOptions = value; - } - - /// - /// Set the origin of where the OnLog event was initiated - /// - public string LogOrigin { get; set; } - - /// - /// Determines if we need to mask certain keywords - /// - public bool UseMask { get; set; } - - /// - /// Determines the keywords to mask - /// - public List MaskedKeywords { get; set; } = new List(); - public string Mask { get; set; } = "***MASKED***"; - - /// - /// Determines that if masking is enabled we also need to use the default masking options: - /// IP addresses - /// MAC addresses - /// Emails - /// Passwords - /// Credit card numbers - /// Social security numbers (SSN) and BSN (Dutch Citizen Service Number) - /// API keys/tokens - /// Phone numbers (generic and Dutch specific) - /// Dates of birth (DOB) or other date formats - /// IBAN/Bank account numbers (generic and Dutch specific) - /// JWT tokens - /// URLs with sensitive query strings - /// License keys - /// Public and private keys (e.g., PEM format) - /// Dutch KVK number (8 or 12 digits) - /// Dutch BTW-nummer (VAT number) - /// Dutch driving license number (10-12 characters) - /// Dutch health insurance number (Zorgnummer) - /// Other Dutch Bank Account numbers (9-10 digits) - /// Dutch Passport Numbers (9 alphanumeric characters - /// Dutch Identification Document Numbers (varying formats) - /// Custom keywords specified in LoggerSettings - /// - public bool UseDefaultMasking { get; set; } = true; - - public event LogDelegate OnLog; - - private static FileLoggerOptions CreateDefaultFileLoggerOptions() - { - return new FileLoggerOptions(); - } - - - private static bool ConsoleExists() - { - try - { - var test1 = Environment.UserInteractive; - var test2 = Console.WindowHeight > 0; - return test1 && test2; - } - catch (Exception) - { - return false; - } - } - - internal void OnLogEvent(EonaCatLogMessage eonaCatLogMessage) - { - OnLog?.Invoke(eonaCatLogMessage); - } - - internal void ResetLogEvent() - { - OnLog = null; - } - - public void AllowAllLogTypes() - { - TypesToLog.Clear(); - } - - public void LogInfo() - { - if (TypesToLog == null) - { - TypesToLog = new List(); - } - - if (!TypesToLog.Contains(ELogType.INFO)) - { - TypesToLog.Add(ELogType.INFO); - } - } - - public void LogWarning() - { - if (TypesToLog == null) - { - TypesToLog = new List(); - } - - if (!TypesToLog.Contains(ELogType.WARNING)) - { - TypesToLog.Add(ELogType.WARNING); - } - } - - public void LogError() - { - if (TypesToLog == null) - { - TypesToLog = new List(); - } - - if (!TypesToLog.Contains(ELogType.ERROR)) - { - TypesToLog.Add(ELogType.ERROR); - } - } - - public void LogCritical() - { - if (TypesToLog == null) - { - TypesToLog = new List(); - } - - if (!TypesToLog.Contains(ELogType.CRITICAL)) - { - TypesToLog.Add(ELogType.CRITICAL); - } - } - - public void LogTraffic() - { - if (TypesToLog == null) - { - TypesToLog = new List(); - } - - if (!TypesToLog.Contains(ELogType.TRAFFIC)) - { - TypesToLog.Add(ELogType.TRAFFIC); - } - } - - public void LogTrace() - { - if (TypesToLog == null) - { - TypesToLog = new List(); - } - - if (!TypesToLog.Contains(ELogType.TRACE)) - { - TypesToLog.Add(ELogType.TRACE); - } - } - - public void LogDebug() - { - if (TypesToLog == null) - { - TypesToLog = new List(); - } - - if (!TypesToLog.Contains(ELogType.DEBUG)) - { - TypesToLog.Add(ELogType.DEBUG); - } - } + _logTypes = value; + } + } + + public List SysLogServers { get; internal set; } + public List SplunkServers { get; internal set; } + + public List GrayLogServers { get; internal set; } + public List TcpServers { get; internal set; } + + public List UdpServers { get; internal set; } + + /// + /// Determines if the fileLogging is enabled + /// + public bool EnableFileLogging { get; set; } = true; + + /// + /// FileLogger settings. + /// + public FileLoggerOptions FileLoggerOptions + { + get + { + if (_fileLoggerOptions == null) + { + _fileLoggerOptions = CreateDefaultFileLoggerOptions(); + } + + return _fileLoggerOptions; + } + + set => _fileLoggerOptions = value; + } + + /// + /// Set the origin of where the OnLog event was initiated + /// + public string LogOrigin { get; set; } + + /// + /// Determines if we need to mask certain keywords + /// + public bool UseMask { get; set; } + + /// + /// Determines the keywords to mask + /// + public List MaskedKeywords { get; set; } = new List(); + public string Mask { get; set; } = "***MASKED***"; + + /// + /// Determines that if masking is enabled we also need to use the default masking options: + /// IP addresses + /// MAC addresses + /// Emails + /// Passwords + /// Credit card numbers + /// Social security numbers (SSN) and BSN (Dutch Citizen Service Number) + /// API keys/tokens + /// Phone numbers (generic and Dutch specific) + /// Dates of birth (DOB) or other date formats + /// IBAN/Bank account numbers (generic and Dutch specific) + /// JWT tokens + /// URLs with sensitive query strings + /// License keys + /// Public and private keys (e.g., PEM format) + /// Dutch KVK number (8 or 12 digits) + /// Dutch BTW-nummer (VAT number) + /// Dutch driving license number (10-12 characters) + /// Dutch health insurance number (Zorgnummer) + /// Other Dutch Bank Account numbers (9-10 digits) + /// Dutch Passport Numbers (9 alphanumeric characters + /// Dutch Identification Document Numbers (varying formats) + /// Custom keywords specified in LoggerSettings + /// + public bool UseDefaultMasking { get; set; } = true; + + public event LogDelegate OnLog; + + private static FileLoggerOptions CreateDefaultFileLoggerOptions() + { + return new FileLoggerOptions(); + } + + + private static bool ConsoleExists() + { + try + { + var test1 = Environment.UserInteractive; + var test2 = Console.WindowHeight > 0; + return test1 && test2; + } + catch (Exception) + { + return false; + } + } + + internal void OnLogEvent(EonaCatLogMessage eonaCatLogMessage) + { + OnLog?.Invoke(eonaCatLogMessage); + } + + internal void ResetLogEvent() + { + OnLog = null; + } + + public void AllowAllLogTypes() + { + TypesToLog.Clear(); + } + + public void LogInfo() + { + if (TypesToLog == null) + { + TypesToLog = new List(); + } + + if (!TypesToLog.Contains(ELogType.INFO)) + { + TypesToLog.Add(ELogType.INFO); + } + } + + public void LogWarning() + { + if (TypesToLog == null) + { + TypesToLog = new List(); + } + + if (!TypesToLog.Contains(ELogType.WARNING)) + { + TypesToLog.Add(ELogType.WARNING); + } + } + + public void LogError() + { + if (TypesToLog == null) + { + TypesToLog = new List(); + } + + if (!TypesToLog.Contains(ELogType.ERROR)) + { + TypesToLog.Add(ELogType.ERROR); + } + } + + public void LogCritical() + { + if (TypesToLog == null) + { + TypesToLog = new List(); + } + + if (!TypesToLog.Contains(ELogType.CRITICAL)) + { + TypesToLog.Add(ELogType.CRITICAL); + } + } + + public void LogTraffic() + { + if (TypesToLog == null) + { + TypesToLog = new List(); + } + + if (!TypesToLog.Contains(ELogType.TRAFFIC)) + { + TypesToLog.Add(ELogType.TRAFFIC); + } + } + + public void LogTrace() + { + if (TypesToLog == null) + { + TypesToLog = new List(); + } + + if (!TypesToLog.Contains(ELogType.TRACE)) + { + TypesToLog.Add(ELogType.TRACE); + } + } + + public void LogDebug() + { + if (TypesToLog == null) + { + TypesToLog = new List(); + } + + if (!TypesToLog.Contains(ELogType.DEBUG)) + { + TypesToLog.Add(ELogType.DEBUG); + } + } } \ No newline at end of file