diff --git a/EonaCat.Logger/Enums.cs b/EonaCat.Logger/Enums.cs
index b176f3a..6b99454 100644
--- a/EonaCat.Logger/Enums.cs
+++ b/EonaCat.Logger/Enums.cs
@@ -1,4 +1,6 @@
-namespace EonaCat.Logger
+using Microsoft.Extensions.Logging;
+
+namespace EonaCat.Logger
{
// 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.
@@ -14,4 +16,84 @@
CRITICAL = 6,
TRACE = 7
}
+
+ public static class LogTypeConverter
+ {
+ public static ELogType FromLogLevel(this LogLevel logLevel)
+ {
+ switch (logLevel)
+ {
+ case LogLevel.None:
+ return ELogType.NONE;
+ case LogLevel.Error:
+ return ELogType.ERROR;
+ case LogLevel.Debug:
+ return ELogType.DEBUG;
+ case LogLevel.Critical:
+ return ELogType.CRITICAL;
+ case LogLevel.Warning:
+ return ELogType.WARNING;
+ case LogLevel.Trace:
+ return ELogType.TRACE;
+ default:
+ return ELogType.INFO;
+ }
+ }
+
+ public static ELogType FromSeverity(this ESeverity logLevel)
+ {
+ switch (logLevel)
+ {
+ case ESeverity.Debug:
+ return ELogType.DEBUG;
+ case ESeverity.Warn:
+ return ELogType.WARNING;
+ case ESeverity.Emergency:
+ return ELogType.CRITICAL;
+ case ESeverity.Critical:
+ return ELogType.CRITICAL;
+ case ESeverity.Alert:
+ return ELogType.ERROR;
+ case ESeverity.Error:
+ return ELogType.ERROR;
+ default:
+ return ELogType.INFO;
+ }
+ }
+ }
+
+ ///
+ /// Message severity.
+ ///
+ public enum ESeverity
+ {
+ ///
+ /// Debug messages.
+ ///
+ Debug = 0,
+ ///
+ /// Informational messages.
+ ///
+ Info = 1,
+ ///
+ /// Warning messages.
+ ///
+ Warn = 2,
+ ///
+ /// Error messages.
+ ///
+ Error = 3,
+ ///
+ /// Alert messages.
+ ///
+ Alert = 4,
+ ///
+ /// Critical messages.
+ ///
+ Critical = 5,
+ ///
+ /// Emergency messages.
+ ///
+ Emergency = 6
+ }
}
\ No newline at end of file
diff --git a/EonaCat.Logger/EonaCat.Logger.csproj b/EonaCat.Logger/EonaCat.Logger.csproj
index 7c9e819..3c8a49e 100644
--- a/EonaCat.Logger/EonaCat.Logger.csproj
+++ b/EonaCat.Logger/EonaCat.Logger.csproj
@@ -8,7 +8,7 @@
net6.0;
icon.ico
- 1.0.0
+ 1.0.1
EonaCat (Jeroen Saey)
true
EonaCat (Jeroen Saey)
@@ -23,6 +23,8 @@
True
LICENSE
True
+
EonaCat.Logger
+ git
diff --git a/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs b/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs
index b6ca567..077e0ea 100644
--- a/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs
+++ b/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs
@@ -16,20 +16,20 @@ namespace EonaCat.Logger.Extensions
/// Adds a file logger named 'File' to the factory.
///
/// The to use.
- public static ILoggingBuilder AddFile(this ILoggingBuilder builder)
+ public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
{
builder.Services.AddSingleton();
return builder;
}
///
- /// Adds a file logger named 'File' to the factory.
+ /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
///
/// The to use.
/// Sets the filename prefix to use for log files
- public static ILoggingBuilder AddFile(this ILoggingBuilder builder, string filenamePrefix)
+ public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix)
{
- builder.AddFile(options => options.FileNamePrefix = filenamePrefix);
+ builder.AddEonaCatFileLogger(options => options.FileNamePrefix = filenamePrefix);
return builder;
}
@@ -38,13 +38,13 @@ namespace EonaCat.Logger.Extensions
///
/// The to use.
/// Configure an instance of the to set logging options
- public static ILoggingBuilder AddFile(this ILoggingBuilder builder, Action configure)
+ public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, Action configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
- builder.AddFile();
+ builder.AddEonaCatFileLogger();
builder.Services.Configure(configure);
return builder;
diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs
index 1321b56..550bb60 100644
--- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs
+++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs
@@ -15,6 +15,7 @@ namespace EonaCat.Logger
private int _fileSizeLimit = 200 * 1024 * 1024;
private int _retainedFileCountLimit = 50;
private int _maxRolloverFiles = 10;
+ private int _maxTries = 3;
public static string DefaultPath => AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
@@ -55,6 +56,20 @@ namespace EonaCat.Logger
}
}
+ ///
+ /// Gets or sets the max times to try to write to the file.
+ /// Defaults 3.
+ ///
+ public int MaxWriteTries
+ {
+ get => _maxTries;
+
+ set
+ {
+ _maxTries = value;
+ }
+ }
+
///
/// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit.
/// Defaults to 10.
diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs
index 43a54b1..cc575c9 100644
--- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs
+++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs
@@ -1,9 +1,11 @@
using EonaCat.Logger.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime;
using System.Threading;
using System.Threading.Tasks;
@@ -23,9 +25,37 @@ namespace EonaCat.Logger
private readonly int _maxFileSize;
private readonly int _maxRetainedFiles;
private readonly int _maxRolloverFiles;
+ private readonly int _maxTries;
private int _rollOverCount = 0;
+ private static readonly object _writeLock = new object();
+ private string _logFile;
- public string LogFile { get; private set; }
+ ///
+ /// The file to which log messages should be appended.
+ ///
+ public string LogFile
+ {
+ get
+ {
+ return _logFile;
+ }
+ set
+ {
+ _logFile = value;
+
+ if (!string.IsNullOrEmpty(_logFile))
+ {
+ string dir = Path.GetDirectoryName(_logFile);
+ if (!string.IsNullOrEmpty(dir))
+ {
+ if (!Directory.Exists(dir))
+ {
+ Directory.CreateDirectory(dir);
+ }
+ }
+ }
+ }
+ }
///
/// Creates an instance of the
@@ -39,6 +69,7 @@ namespace EonaCat.Logger
_maxFileSize = loggerOptions.FileSizeLimit;
_maxRetainedFiles = loggerOptions.RetainedFileCountLimit;
_maxRolloverFiles = loggerOptions.MaxRolloverFiles;
+ _maxTries = loggerOptions.MaxWriteTries;
}
///
@@ -77,11 +108,31 @@ namespace EonaCat.Logger
}
}
- using (StreamWriter streamWriter = File.AppendText(LogFile))
+ lock (_writeLock)
{
- foreach (LogMessage item in group)
+ int tries = 0;
+ bool completed = false;
+
+ while (!completed)
{
- await streamWriter.WriteAsync(item.Message);
+ try
+ {
+ System.IO.StreamWriter file = new System.IO.StreamWriter(LogFile, true);
+ foreach (LogMessage item in group)
+ {
+ file.WriteAsync(item.Message);
+ }
+
+ file.Close();
+ completed = true;
+ }
+ catch (Exception exc)
+ {
+ tries++;
+ Task.Delay(100);
+ if (tries >= _maxTries)
+ throw;
+ }
}
}
}
diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs
index 72f8238..ac15f3f 100644
--- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs
+++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Logging;
+using EonaCat.logger.Managers;
+using Microsoft.Extensions.Logging;
using System;
using System.Text;
@@ -25,35 +26,21 @@ namespace EonaCat.Logger.Internal
public bool IsEnabled(LogLevel logLevel)
{
- if (logLevel == LogLevel.None)
- {
- return false;
- }
- return true;
+ return logLevel != LogLevel.None;
}
public void Log(DateTimeOffset timestamp, LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
{
- if (!IsEnabled(logLevel))
- {
- return;
- }
+ if (!IsEnabled(logLevel)) return;
- StringBuilder builder = new StringBuilder();
- builder.Append(timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff zzz"));
- builder.Append(" [");
- builder.Append(logLevel.ToString());
- builder.Append("] ");
- builder.Append(_category);
- builder.Append(": ");
- builder.AppendLine(formatter(state, exception));
+ var message = LogHelper.FormatMessageWithHeader(new Managers.LoggerSettings(), logLevel.FromLogLevel(), formatter(state, exception)) + Environment.NewLine;
if (exception != null)
- {
- builder.AppendLine(exception.ToString());
+ {
+ message = exception.FormatExceptionToMessage() + Environment.NewLine;
}
- _provider.AddMessage(timestamp, builder.ToString());
+ _provider.AddMessage(timestamp, message);
}
public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs
index 6eb1561..25ebe75 100644
--- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs
+++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs
@@ -90,7 +90,7 @@ namespace EonaCat.Logger.Internal
}
catch
{
- //cancellation token canceled or CompleteAdding called
+ // cancellation token canceled or CompleteAdding called
}
}
}
@@ -128,6 +128,12 @@ namespace EonaCat.Logger.Internal
public void Dispose()
{
Stop();
+ GC.SuppressFinalize(this);
+ }
+
+ ~BatchingLoggerProvider()
+ {
+ Dispose();
}
public ILogger CreateLogger(string categoryName)
diff --git a/EonaCat.Logger/EonaCatCoreLogger/Models/EonaCatLogMessage.cs b/EonaCat.Logger/EonaCatCoreLogger/Models/EonaCatLogMessage.cs
index 216c21a..80df403 100644
--- a/EonaCat.Logger/EonaCatCoreLogger/Models/EonaCatLogMessage.cs
+++ b/EonaCat.Logger/EonaCatCoreLogger/Models/EonaCatLogMessage.cs
@@ -9,13 +9,13 @@ namespace EonaCatLogger.EonaCatCoreLogger.Models
public class EonaCatLogMessage
{
- public DateTime DateTime { get; internal set; }
- public string Message { get; internal set; }
- public ELogType LogType { get; internal set; }
+ public DateTime DateTime { get; set; }
+ public string Message { get; set; }
+ public ELogType LogType { get; set; }
public override string ToString()
{
- return $"[{EnumHelper.ToString(LogType)}] [{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] {Message}";
+ return $"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper.ToString(LogType)}] {Message}";
}
}
}
\ No newline at end of file
diff --git a/EonaCat.Logger/Exceptions/EonaCatLoggerAssertionException.cs b/EonaCat.Logger/Exceptions/EonaCatLoggerAssertionException.cs
new file mode 100644
index 0000000..810ec68
--- /dev/null
+++ b/EonaCat.Logger/Exceptions/EonaCatLoggerAssertionException.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace EonaCat.Logger.Exceptions
+{
+ public class EonaCatLoggerAssertionException : Exception
+ {
+ public EonaCatLoggerAssertionException(string message) : base(message) { }
+ }
+}
diff --git a/EonaCat.Logger/Managers/ColorSchema.cs b/EonaCat.Logger/Managers/ColorSchema.cs
new file mode 100644
index 0000000..0c65962
--- /dev/null
+++ b/EonaCat.Logger/Managers/ColorSchema.cs
@@ -0,0 +1,74 @@
+using System;
+
+namespace EonaCat.Logger.Managers
+{
+ ///
+ /// Colors to use when writing to the console.
+ ///
+ public class ColorSchema
+ {
+ private ConsoleColor currentForeground = Console.ForegroundColor;
+
+ ///
+ /// The color to use for debug messages. Default is dark gray on black.
+ ///
+ public ColorScheme Debug = new ColorScheme(ConsoleColor.DarkGray, ConsoleColor.Black);
+
+ ///
+ /// The color to use for informational messages. Default is gray on black.
+ ///
+ public ColorScheme Info = new ColorScheme(ConsoleColor.Gray, ConsoleColor.Black);
+
+ ///
+ /// The color to use for warning messages. Default is dark red on black.
+ ///
+ public ColorScheme Warning = new ColorScheme(ConsoleColor.DarkRed, ConsoleColor.Black);
+
+ ///
+ /// The color to use for error messages. Default is red on black.
+ ///
+ public ColorScheme Error = new ColorScheme(ConsoleColor.Red, ConsoleColor.Black);
+
+ ///
+ /// The color to use for alert messages. Default is dark yellow on black.
+ ///
+ public ColorScheme Traffic = new ColorScheme(ConsoleColor.DarkYellow, ConsoleColor.Black);
+
+ ///
+ /// The color to use for critical messages. Default is yellow on black.
+ ///
+ public ColorScheme Critical = new ColorScheme(ConsoleColor.Yellow, ConsoleColor.Black);
+
+ ///
+ /// The color to use for emergency messages. Default is white on red.
+ ///
+ public ColorScheme Trace = new ColorScheme(ConsoleColor.White, ConsoleColor.Red);
+ }
+
+ ///
+ /// Color scheme for logging messages.
+ ///
+ public class ColorScheme
+ {
+ ///
+ /// Foreground color.
+ ///
+ public ConsoleColor Foreground = Console.ForegroundColor;
+
+ ///
+ /// Background color.
+ ///
+ public ConsoleColor Background = Console.BackgroundColor;
+
+ ///
+ /// Instantiates a new color scheme.
+ ///
+ /// Foreground color.
+ /// Background color.
+ public ColorScheme(ConsoleColor foreground, ConsoleColor background)
+ {
+ Foreground = foreground;
+ Background = background;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.Logger/Managers/EFileLoggingMode.cs b/EonaCat.Logger/Managers/EFileLoggingMode.cs
new file mode 100644
index 0000000..aa887b3
--- /dev/null
+++ b/EonaCat.Logger/Managers/EFileLoggingMode.cs
@@ -0,0 +1,8 @@
+namespace EonaCat.Logger.Managers
+{
+ public enum EFileLoggingMode
+ {
+ Enabled = 0,
+ Disabled = 1,
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.Logger/Managers/ILogManager.cs b/EonaCat.Logger/Managers/ILogManager.cs
new file mode 100644
index 0000000..755b223
--- /dev/null
+++ b/EonaCat.Logger/Managers/ILogManager.cs
@@ -0,0 +1,7 @@
+namespace EonaCat.Logger.Managers
+{
+ public interface ILogManager
+ {
+ void Write(string message, ELogType logType = ELogType.INFO, ELogType? logLevel = null);
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.Logger/Managers/LogHelper.cs b/EonaCat.Logger/Managers/LogHelper.cs
new file mode 100644
index 0000000..3de259a
--- /dev/null
+++ b/EonaCat.Logger/Managers/LogHelper.cs
@@ -0,0 +1,292 @@
+using EonaCat.Logger;
+using EonaCat.Logger.Managers;
+using EonaCat.Logger.Syslog;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EonaCat.logger.Managers
+{
+ public static class LogHelper
+ {
+ private static readonly object _fileLock = new object();
+
+ ///
+ /// Format a message with the specified header
+ ///
+ /// Logger settings
+ /// logtype for the formatted message
+ /// The actual message to format with the header
+ ///
+ public static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage)
+ {
+ if (string.IsNullOrWhiteSpace(currentMessage))
+ {
+ return currentMessage;
+ }
+
+ if (settings == null)
+ {
+ return "[EonaCatLogger]" + " " + currentMessage;
+ }
+
+ string header = settings.HeaderFormat;
+ if (header.Contains("{ts}"))
+ {
+ header = header.Replace("{ts}", DateTime.Now.ToUniversalTime().ToString(settings.TimestampFormat));
+ }
+
+ if (header.Contains("{host}"))
+ {
+ header = header.Replace("{host}", Dns.GetHostName());
+ }
+
+ if (header.Contains("{thread}"))
+ {
+ header = header.Replace("{thread}", Thread.CurrentThread.ManagedThreadId.ToString());
+ }
+
+ if (header.Contains("{sev}"))
+ {
+ header = header.Replace("{sev}", logType.ToString());
+ }
+
+ string fullMessage = "[EonaCatLogger]" + " " + header + " " + currentMessage;
+ return fullMessage;
+ }
+
+ ///
+ /// Formats a given exception as a string
+ ///
+ /// exception
+ /// The name of the module which called the code (optional)
+ /// The name of the method which waws called in code (optional)
+ ///
+ 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 = frame.GetFileLineNumber();
+ string filename = frame.GetFileName();
+
+ string message =
+ Environment.NewLine +
+ "--- [EonaCatLogger] Exception details ---" + Environment.NewLine +
+ (!string.IsNullOrEmpty(module) ? " Module : " + module + Environment.NewLine : "") +
+ (!string.IsNullOrEmpty(method) ? " Method : " + method + Environment.NewLine : "") +
+ " Type : " + exception.GetType().ToString() + Environment.NewLine;
+
+ if (exception.Data != null && exception.Data.Count > 0)
+ {
+ message += " Data : " + Environment.NewLine;
+ foreach (DictionaryEntry curr in exception.Data)
+ {
+ message += " | " + curr.Key + ": " + curr.Value + Environment.NewLine;
+ }
+ }
+ else
+ {
+ message += " Data : (none)" + Environment.NewLine;
+ }
+
+ message +=
+ " Inner : ";
+
+ if (exception.InnerException == null) message += "(null)" + Environment.NewLine;
+ else
+ {
+ message += exception.InnerException.GetType().ToString() + Environment.NewLine;
+ message +=
+ " Message : " + exception.InnerException.Message + Environment.NewLine +
+ " Source : " + exception.InnerException.Source + Environment.NewLine +
+ " StackTrace : " + exception.InnerException.StackTrace + Environment.NewLine +
+ " ToString : " + exception.InnerException.ToString() + Environment.NewLine;
+
+ if (exception.InnerException.Data != null && exception.InnerException.Data.Count > 0)
+ {
+ message += " Data : " + Environment.NewLine;
+ foreach (DictionaryEntry curr in exception.Data)
+ {
+ message += " | " + curr.Key + ": " + curr.Value + Environment.NewLine;
+ }
+ }
+ else
+ {
+ message += " Data : (none)" + Environment.NewLine;
+ }
+ }
+
+ message +=
+ " Message : " + exception.Message + Environment.NewLine +
+ " Source : " + exception.Source + Environment.NewLine +
+ " StackTrace : " + exception.StackTrace + Environment.NewLine +
+ " Line : " + fileLine + Environment.NewLine +
+ " File : " + filename + Environment.NewLine +
+ " ToString : " + exception.ToString() + Environment.NewLine +
+ "---";
+ return message;
+ }
+
+ internal static void SendConsole(LoggerSettings settings, ELogType logType, string message)
+ {
+ if (settings == null) return;
+ if (!settings.EnableConsole) return;
+ if (string.IsNullOrWhiteSpace(message)) return;
+
+ if (settings.EnableColors)
+ {
+ ConsoleColor prevForeground = Console.ForegroundColor;
+ ConsoleColor prevBackground = Console.BackgroundColor;
+
+ if (settings.Colors != null)
+ {
+ switch (logType)
+ {
+ case ELogType.DEBUG:
+ Console.ForegroundColor = settings.Colors.Debug.Foreground;
+ Console.BackgroundColor = settings.Colors.Debug.Background;
+ break;
+ case ELogType.INFO:
+ Console.ForegroundColor = settings.Colors.Info.Foreground;
+ Console.BackgroundColor = settings.Colors.Info.Background;
+ break;
+ case ELogType.WARNING:
+ Console.ForegroundColor = settings.Colors.Warning.Foreground;
+ Console.BackgroundColor = settings.Colors.Warning.Background;
+ break;
+ case ELogType.ERROR:
+ Console.ForegroundColor = settings.Colors.Error.Foreground;
+ Console.BackgroundColor = settings.Colors.Error.Background;
+ break;
+ case ELogType.TRAFFIC:
+ Console.ForegroundColor = settings.Colors.Traffic.Foreground;
+ Console.BackgroundColor = settings.Colors.Traffic.Background;
+ break;
+ case ELogType.CRITICAL:
+ Console.ForegroundColor = settings.Colors.Critical.Foreground;
+ Console.BackgroundColor = settings.Colors.Critical.Background;
+ break;
+ case ELogType.TRACE:
+ Console.ForegroundColor = settings.Colors.Trace.Foreground;
+ Console.BackgroundColor = settings.Colors.Trace.Background;
+ break;
+ }
+ }
+
+ Console.WriteLine(message);
+ Console.ForegroundColor = prevForeground;
+ Console.BackgroundColor = prevBackground;
+ }
+ else
+ {
+ Console.WriteLine(message);
+ }
+ }
+
+ internal static void SendFile(ILogger logger, LoggerSettings settings, ELogType logType, string message, int maxTries = 3)
+ {
+ lock (_fileLock)
+ {
+ if (logger == null) return;
+ if (settings == null) return;
+ if (!settings.EnableFileLogging) return;
+ if (string.IsNullOrWhiteSpace(message)) return;
+ //logger.LogInformation(message);
+
+ int tries = 0;
+ bool completed = false;
+ while (!completed)
+ {
+ try
+ {
+ System.IO.StreamWriter file = new System.IO.StreamWriter($"{settings.FileLoggerOptions.LogDirectory}{Path.DirectorySeparatorChar}{settings.FileLoggerOptions.FileNamePrefix}_{DateTime.Now.ToString("yyyyMMdd")}.log", true);
+ file.Write(message + Environment.NewLine);
+ file.Close();
+ completed = true;
+ }
+ catch (Exception exc)
+ {
+ tries++;
+ Task.Delay(100);
+ if (tries >= maxTries)
+ throw;
+ }
+ }
+ return;
+
+ if (logType == ELogType.CRITICAL)
+ {
+ logger.LogCritical(message);
+ }
+ else if (logType == ELogType.DEBUG)
+ {
+ logger.LogDebug(message);
+ }
+ else if (logType == ELogType.ERROR)
+ {
+ logger.LogError(message);
+ }
+ else if (logType == ELogType.INFO)
+ {
+ logger.LogInformation(message);
+ }
+ else if (logType == ELogType.TRACE)
+ {
+ logger.LogTrace(message);
+ }
+ else if (logType == ELogType.TRAFFIC)
+ {
+ logger.LogTrace($"[TRAFFIC] {message}");
+ }
+ else if (logType == ELogType.WARNING)
+ {
+ logger.LogWarning(message);
+ }
+ }
+ }
+
+ internal static void SendToSysLogServers(LoggerSettings settings, string message)
+ {
+ if (settings == null) return;
+ if (!settings.SendToSyslogServers) return;
+ if (settings.SysLogServers == null) return;
+ if (!settings.SysLogServers.Any()) return;
+ if (string.IsNullOrWhiteSpace(message)) return;
+
+ byte[] data = Encoding.UTF8.GetBytes(message);
+
+ var sysLogServers = settings.SysLogServers.ToList();
+ foreach (SyslogServer server in sysLogServers)
+ {
+ lock (server.SendLock)
+ {
+ if (string.IsNullOrWhiteSpace(server.Hostname))
+ {
+ Console.WriteLine("Server hostname not specified, skipping syslog server");
+ }
+ if (server.Port < 0)
+ {
+ Console.WriteLine("Server port must be zero or greater, skipping syslog server");
+ }
+
+ try
+ {
+ server.Udp.Send(data, data.Length);
+ }
+ catch (Exception)
+ {
+
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.Logger/Managers/LogManager.cs b/EonaCat.Logger/Managers/LogManager.cs
index caf95af..d1b616f 100644
--- a/EonaCat.Logger/Managers/LogManager.cs
+++ b/EonaCat.Logger/Managers/LogManager.cs
@@ -1,13 +1,17 @@
-using EonaCat.Extensions;
+using EonaCat.logger.Managers;
+using EonaCat.Logger.Exceptions;
using EonaCat.Logger.Extensions;
using EonaCat.Logger.Helpers;
+using EonaCat.Logger.Syslog;
using EonaCatLogger.EonaCatCoreLogger.Models;
-using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
-using System.Text;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
@@ -16,11 +20,15 @@ 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.
- public class LogManager : IDisposable
+ public partial class LogManager : ILogManager, IDisposable
{
private readonly object _batton = new object();
private DateTime _logDate;
+ public event LogDelegate OnLog;
+ public delegate void LogDelegate(LogManager logger, ELogType logLevel, EonaCatLogMessage message);
+
+ public ELogType LogType;
private ILoggerProvider LoggerProvider { get; set; }
private ILoggerFactory LoggerFactory { get; set; }
private ILogger Logger { get; set; }
@@ -29,13 +37,50 @@ namespace EonaCat.Logger.Managers
public bool IsRunning { get; private set; }
public StreamWriter Output { get; private set; }
- public string LogFolderPath { get; set; } = "logs";
- public FileLoggerOptions FileLoggerOptions { get; private set; }
- public int FileSizeLimit { get; set; } = 200 * 1024 * 1024;
+
public string CategoryName { get; set; }
- private LogManager Instance { get; set; }
+
+ public readonly string Id;
private bool _disposed;
+ private static LogManager _instance;
+ private LoggerSettings _settings;
+ private CancellationTokenSource _TokenSource = new CancellationTokenSource();
+ private CancellationToken _Token;
+
+ ///
+ /// Default Logger Instance with it's default configuration
+ ///
+ public static LogManager Instance => InstanceInit();
+
+ ///
+ /// Logging settings.
+ ///
+ public LoggerSettings Settings
+ {
+ get
+ {
+ if (_settings == null)
+ {
+ _settings = new LoggerSettings();
+ }
+ return _settings;
+ }
+
+ set
+ {
+ _settings = value;
+ }
+ }
+
+ private static LogManager InstanceInit()
+ {
+ if (_instance == null)
+ {
+ _instance = new LogManager(null, id: "EonaCat");
+ }
+ return _instance;
+ }
protected virtual void Dispose(bool disposing)
{
@@ -49,47 +94,20 @@ namespace EonaCat.Logger.Managers
if (disposing)
{
StopLogging();
+ _TokenSource.Cancel();
}
_disposed = true;
}
}
- public void Dispose()
- {
- Dispose(true);
- }
-
- public static async Task DownloadLogAsync(HttpResponse response, string logFile, long limit)
- {
- using (FileStream fileStream = new FileStream(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
- {
- response.ContentType = "text/plain";
- response.Headers.Add("Content-Disposition", "attachment;filename=" + Path.GetFileName(logFile));
-
- if (limit > fileStream.Length)
- {
- limit = fileStream.Length;
- }
-
- using (OffsetStream offsetStream = new OffsetStream(fileStream, 0, limit))
- {
- using (Stream stream = response.Body)
- {
- offsetStream.CopyTo(stream);
-
- if (fileStream.Length > limit)
- {
- byte[] buffer = Encoding.UTF8.GetBytes("####___EonaCatLogger_TRUNCATED___####");
- await stream.WriteAsync(buffer, 0, buffer.Length);
- }
- }
- }
- }
- }
-
private void StartNewLog()
{
+ if (_TokenSource.IsCancellationRequested)
+ {
+ return;
+ }
+
DateTime now = DateTime.Now;
if (IsRunning && now.Date > _logDate.Date)
@@ -99,143 +117,157 @@ namespace EonaCat.Logger.Managers
IsRunning = true;
IServiceCollection serviceCollection = new ServiceCollection();
- CreateDefaultFileLoggerOptions();
-
- serviceCollection.AddLogging(builder => builder.AddFile(configuration =>
+ serviceCollection.AddLogging(builder => builder.AddEonaCatFileLogger(configuration =>
{
- configuration.BackgroundQueueSize = FileLoggerOptions.BackgroundQueueSize;
- configuration.BatchSize = FileLoggerOptions.BatchSize;
- configuration.FileNamePrefix = FileLoggerOptions.FileNamePrefix;
- configuration.FileSizeLimit = FileLoggerOptions.FileSizeLimit;
- configuration.FlushPeriod = FileLoggerOptions.FlushPeriod;
- configuration.IsEnabled = FileLoggerOptions.IsEnabled;
- configuration.LogDirectory = FileLoggerOptions.LogDirectory;
- configuration.RetainedFileCountLimit = FileLoggerOptions.RetainedFileCountLimit;
+ configuration = Settings.FileLoggerOptions;
}));
var serviceProvider = serviceCollection.BuildServiceProvider();
LoggerProvider = serviceProvider.GetService();
LoggerFactory = serviceProvider.GetService();
+
CategoryName = CategoryName ?? string.Empty;
Logger = LoggerFactory.CreateLogger(CategoryName);
- if (!Directory.Exists(FileLoggerOptions.LogDirectory))
+ if (!Directory.Exists(Settings.FileLoggerOptions.LogDirectory))
{
- Directory.CreateDirectory(FileLoggerOptions.LogDirectory);
+ Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
}
_logDate = now;
- Write(now, "EonaCatLogger started.");
+ Write(now, $"{DllInfo.ApplicationName} started.");
}
- private void CreateDefaultFileLoggerOptions()
+ public void Assert(bool condition, string message)
{
- if (FileLoggerOptions == null)
+ if (!condition)
{
- FileLoggerOptions = new FileLoggerOptions
- {
- LogDirectory = LogFolderPath,
- FileSizeLimit = FileSizeLimit
- };
- }
- }
-
- private void WriteToFile(EonaCatLogMessage EonaCatLogMessage)
- {
- if (EonaCatLogMessage.LogType == ELogType.CRITICAL)
- {
- Instance.Logger.LogCritical(EonaCatLogMessage.Message);
- }
- else if (EonaCatLogMessage.LogType == ELogType.DEBUG)
- {
- Instance.Logger.LogDebug(EonaCatLogMessage.Message);
- }
- else if (EonaCatLogMessage.LogType == ELogType.ERROR)
- {
- Instance.Logger.LogError(EonaCatLogMessage.Message);
- }
- else if (EonaCatLogMessage.LogType == ELogType.INFO)
- {
- Instance.Logger.LogInformation(EonaCatLogMessage.Message);
- }
- else if (EonaCatLogMessage.LogType == ELogType.TRACE)
- {
- Instance.Logger.LogTrace(EonaCatLogMessage.Message);
- }
- else if (EonaCatLogMessage.LogType == ELogType.TRAFFIC)
- {
- Instance.Logger.LogTrace($"[TRAFFIC] {EonaCatLogMessage.Message}");
- }
- else if (EonaCatLogMessage.LogType == ELogType.WARNING)
- {
- Instance.Logger.LogWarning(EonaCatLogMessage.Message);
+ throw new EonaCatLoggerAssertionException(message);
}
}
private void Write(DateTime dateTime, string message, ELogType logType = ELogType.INFO)
{
- var EonaCatMessage = new EonaCatLogMessage { DateTime = dateTime, Message = message, LogType = logType };
- WriteToFile(EonaCatMessage);
+ if (string.IsNullOrWhiteSpace(message)) return;
+ if (logType < ELogType.INFO) return;
+
+ string remainder = "";
+ string currentMessage;
+
+ if (message.Length > _settings.MaxMessageLength)
+ {
+ currentMessage = message.Substring(0, _settings.MaxMessageLength);
+ remainder = message.Substring(_settings.MaxMessageLength, (message.Length - _settings.MaxMessageLength));
+ }
+ else
+ {
+ currentMessage = message;
+ }
+
+ var fullMessage = LogHelper.FormatMessageWithHeader(_settings, logType, currentMessage);
+
+ LogHelper.SendConsole(_settings, logType, fullMessage);
+
+ LogHelper.SendFile(Logger, _settings, logType, fullMessage);
+
+ LogHelper.SendToSysLogServers(_settings, fullMessage);
+
+ if (!string.IsNullOrEmpty(remainder))
+ {
+ Write(dateTime, remainder, logType);
+ }
+
+ var EonaCatMessage = new EonaCatLogMessage { DateTime = dateTime, Message = currentMessage, LogType = logType };
+ OnLog?.Invoke(this, logType, EonaCatMessage);
}
- private LogManager()
- {
- // Do nothing
- }
+ public void Reset() => OnLog = null;
- public LogManager(FileLoggerOptions fileLoggerOptions)
+ public LogManager(LoggerSettings settings, string serverIp, int serverPort, string id = "EonaCatLogger")
{
- FileLoggerOptions = fileLoggerOptions;
+ if (string.IsNullOrEmpty(serverIp)) throw new ArgumentNullException(nameof(serverIp));
+ if (serverPort < 0) throw new ArgumentException("Server port must be zero or greater.");
+
+ settings.SysLogServers = new List();
+ settings.SysLogServers.Add(new SyslogServer(serverIp, serverPort));
+
+ Id = id;
+ Settings = settings;
SetupLogManager();
}
- private void SetupLogManager()
+ public LogManager(LoggerSettings settings, string id = "EonaCatLogger")
{
- AppDomain.CurrentDomain.ProcessExit += ProcessExit;
-
- Instance = this;
-
- _logDate = DateTime.Now;
-
- StartNewLog();
+ Id = id;
+ Settings = settings;
+ SetupFileLogger(settings, null, true);
+ SetupLogManager();
}
- private void ProcessExit(object sender, EventArgs e)
+ private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true)
{
- Instance?.StopLogging();
- Thread.Sleep(1000);
- }
+ if (settings == null)
+ {
+ // Create default loggingSettings
+ Settings = settings;
+ settings = Settings;
+ }
- private void StopLogging()
- {
- Write(DateTime.Now, "EonaCatLogger stopped.");
- IsRunning = false;
- }
-
- public LogManager(string logFolder = null, bool defaultPrefix = true)
- {
- CreateDefaultFileLoggerOptions();
+ if (!settings.EnableFileLogging) return;
+
if (logFolder != null)
{
- FileLoggerOptions.LogDirectory = logFolder;
+ Settings.FileLoggerOptions.LogDirectory = logFolder;
}
if (defaultPrefix)
{
- FileLoggerOptions.FileNamePrefix = "EonaCat";
+ Settings.FileLoggerOptions.FileNamePrefix = "EonaCat";
}
else
{
- FileLoggerOptions.FileNamePrefix = string.Empty;
+ Settings.FileLoggerOptions.FileNamePrefix = string.Empty;
}
+ }
+ private void SetupLogManager()
+ {
+ _Token = _TokenSource.Token;
+ AppDomain.CurrentDomain.ProcessExit += ProcessExit;
+
+ Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);
+
+ _logDate = DateTime.Now;
+ }
+
+ void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
+ {
+ Dispose(true);
+ }
+
+ private void ProcessExit(object sender, EventArgs e)
+ {
+ Dispose(true);
+ }
+
+ private void StopLogging()
+ {
+ IsRunning = false;
+ Write(DateTime.Now, $"{DllInfo.ApplicationName} stopped.");
+ Task.Delay(500);
+ }
+
+ public LogManager(string logFolder = null, bool defaultPrefix = true)
+ {
+ SetupFileLogger(null, logFolder, defaultPrefix);
SetupLogManager();
}
- public void Write(Exception exception)
+ public void Write(Exception exception, string module = null, string method = null, bool criticalException = false)
{
- Write(exception.ToString());
+ if (exception != null) return;
+ Write(exception.FormatExceptionToMessage(module, method), criticalException ? ELogType.CRITICAL : ELogType.ERROR);
}
public void Write(string message, ELogType logType = ELogType.INFO, ELogType? logLevel = null)
@@ -256,7 +288,7 @@ namespace EonaCat.Logger.Managers
StartNewLog();
}
- Write(now, $"{DllInfo.ApplicationName}: {message}", logType);
+ Write(now, message, logType);
}
}
}
@@ -269,8 +301,12 @@ namespace EonaCat.Logger.Managers
{
File.Delete(CurrentLogFile);
}
- StartNewLog();
}
}
+
+ void IDisposable.Dispose()
+ {
+ Dispose(true);
+ }
}
}
\ No newline at end of file
diff --git a/EonaCat.Logger/Managers/LoggerSettings.cs b/EonaCat.Logger/Managers/LoggerSettings.cs
new file mode 100644
index 0000000..bd5c571
--- /dev/null
+++ b/EonaCat.Logger/Managers/LoggerSettings.cs
@@ -0,0 +1,173 @@
+using System.IO;
+using System;
+using System.Collections.Generic;
+using EonaCat.Logger.Syslog;
+using System.Runtime;
+
+namespace EonaCat.Logger.Managers
+{
+ ///
+ /// Logger settings.
+ ///
+ public class LoggerSettings
+ {
+ ///
+ /// 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
+ /// {thread}: Thread ID
+ /// {sev}: Severity
+ /// Default: {ts} {host} {thread} {sev}
+ /// A space will be inserted between the header and the message.
+ ///
+ public string HeaderFormat
+ {
+ get
+ {
+ return _HeaderFormat;
+ }
+ set
+ {
+ if (string.IsNullOrEmpty(value)) _HeaderFormat = "";
+ else _HeaderFormat = value;
+ }
+ }
+
+ ///
+ /// Timestamp format.
+ ///
+ public string TimestampFormat
+ {
+ get
+ {
+ return _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
+ {
+ return _EnableConsole;
+ }
+ set
+ {
+ if (value) _EnableConsole = ConsoleExists();
+ else _EnableConsole = false;
+ }
+ }
+
+ ///
+ /// Minimum severity required to send a message.
+ ///
+ public ESeverity MinimumSeverity { get; set; } = ESeverity.Debug;
+
+ ///
+ /// 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
+ {
+ return _colors;
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(Colors));
+ }
+ _colors = value;
+ }
+ }
+
+ public bool SendToSyslogServers { get; set; }
+
+ public List SysLogServers { get; set; } = new List { new SyslogServer("127.0.0.1", 514) };
+
+ ///
+ /// Determines if the fileLogging is enabled
+ ///
+ public bool EnableFileLogging { get; set; } = true;
+
+ private FileLoggerOptions _fileLoggerOptions;
+
+ ///
+ /// FileLogger settings.
+ ///
+ public FileLoggerOptions FileLoggerOptions
+ {
+ get
+ {
+ if (_fileLoggerOptions == null)
+ {
+ _fileLoggerOptions = CreateDefaultFileLoggerOptions();
+ }
+ return _fileLoggerOptions;
+ }
+
+ set
+ {
+ _fileLoggerOptions = value;
+ }
+ }
+
+ private FileLoggerOptions CreateDefaultFileLoggerOptions()
+ {
+ return new FileLoggerOptions();
+ }
+
+ ///
+ /// Maximum message length. Must be greater than or equal to 32. Default is 1024.
+ ///
+ public int MaxMessageLength
+ {
+ get
+ {
+ return _MaxMessageLength;
+ }
+ set
+ {
+ if (value < 32) throw new ArgumentException("Maximum message length must be at least 32.");
+ _MaxMessageLength = value;
+ }
+ }
+
+ private string _HeaderFormat = "{ts} {host} {thread} {sev}";
+ private string _TimestampFormat = "yyyy-MM-dd HH:mm:ss";
+ private bool _EnableConsole = true;
+ private int _MaxMessageLength = 1024;
+ private ColorSchema _colors = new ColorSchema();
+
+
+ private bool ConsoleExists()
+ {
+ try
+ {
+ bool test1 = Environment.UserInteractive;
+ bool test2 = Console.WindowHeight > 0;
+ return test1 && test2;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.Logger/Syslog/SyslogServer.cs b/EonaCat.Logger/Syslog/SyslogServer.cs
new file mode 100644
index 0000000..f044b14
--- /dev/null
+++ b/EonaCat.Logger/Syslog/SyslogServer.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Text;
+
+namespace EonaCat.Logger.Syslog
+{
+ ///
+ /// Syslog server.
+ ///
+ public class SyslogServer
+ {
+ ///
+ /// Hostname.
+ ///
+ public string Hostname
+ {
+ get
+ {
+ return _Hostname;
+ }
+ set
+ {
+ if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
+ _Hostname = value;
+
+ SetUdp();
+ }
+ }
+
+ ///
+ /// UDP port.
+ ///
+ public int Port
+ {
+ get
+ {
+ return _Port;
+ }
+ set
+ {
+ if (value < 0) throw new ArgumentException("Port must be zero or greater.");
+ _Port = value;
+
+ SetUdp();
+ }
+ }
+
+ ///
+ /// IP:port of the server.
+ ///
+ public string IpPort
+ {
+ get
+ {
+ return _Hostname + ":" + _Port;
+ }
+ }
+
+ internal readonly object SendLock = new object();
+ internal UdpClient Udp = null;
+ private string _Hostname = "127.0.0.1";
+ private int _Port = 514;
+
+ ///
+ /// Instantiate the object.
+ ///
+ public SyslogServer()
+ {
+ }
+
+ ///
+ /// Instantiate the object.
+ ///
+ /// Hostname.
+ /// Port.
+ public SyslogServer(string hostname = "127.0.0.1", int port = 514)
+ {
+ Hostname = hostname;
+ Port = port;
+ }
+
+ private void SetUdp()
+ {
+ Udp = null;
+ Udp = new UdpClient(_Hostname, _Port);
+ }
+ }
+}
diff --git a/README.md b/README.md
index 4422e84..a5da14c 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,12 @@
EonaCat Logger
-Log application information to log files
\ No newline at end of file
+Log application information to log files
+
+
+Be sure the following dependencies are added:
+
+Microsoft.Extensions.Logging.Abstractions
+Microsoft.Extensions.DependencyInjection.Abstractions
+Microsoft.Extensions.DependencyInjection
+Microsoft.Extensions.Logging
\ No newline at end of file