Added sysLogServer functionality

This commit is contained in:
2022-12-15 15:02:10 +01:00
parent 7783dc07e1
commit f8659ad197
17 changed files with 1008 additions and 169 deletions

View File

@@ -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;
}
}
}
/// <summary>
/// Message severity.
/// </summary>
public enum ESeverity
{
/// <summary>
/// Debug messages.
/// </summary>
Debug = 0,
/// <summary>
/// Informational messages.
/// </summary>
Info = 1,
/// <summary>
/// Warning messages.
/// </summary>
Warn = 2,
/// <summary>
/// Error messages.
/// </summary>
Error = 3,
/// <summary>
/// Alert messages.
/// </summary>
Alert = 4,
/// <summary>
/// Critical messages.
/// </summary>
Critical = 5,
/// <summary>
/// Emergency messages.
/// </summary>
Emergency = 6
}
}

View File

@@ -8,7 +8,7 @@
net6.0;
</TargetFrameworks>
<ApplicationIcon>icon.ico</ApplicationIcon>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<Authors>EonaCat (Jeroen Saey)</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Company>EonaCat (Jeroen Saey)</Company>
@@ -23,6 +23,8 @@
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<Title>EonaCat.Logger</Title>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>

View File

@@ -16,20 +16,20 @@ namespace EonaCat.Logger.Extensions
/// Adds a file logger named 'File' to the factory.
/// </summary>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
public static ILoggingBuilder AddFile(this ILoggingBuilder builder)
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
{
builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
return builder;
}
/// <summary>
/// Adds a file logger named 'File' to the factory.
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
/// </summary>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
/// <param name="filenamePrefix">Sets the filename prefix to use for log files</param>
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
/// </summary>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
/// <param name="configure">Configure an instance of the <see cref="FileLoggerOptions" /> to set logging options</param>
public static ILoggingBuilder AddFile(this ILoggingBuilder builder, Action<FileLoggerOptions> configure)
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, Action<FileLoggerOptions> configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
builder.AddFile();
builder.AddEonaCatFileLogger();
builder.Services.Configure(configure);
return builder;

View File

@@ -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
}
}
/// <summary>
/// Gets or sets the max times to try to write to the file.
/// Defaults 3.
/// </summary>
public int MaxWriteTries
{
get => _maxTries;
set
{
_maxTries = value;
}
}
/// <summary>
/// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit.
/// Defaults to <c>10</c>.

View File

@@ -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; }
/// <summary>
/// The file to which log messages should be appended.
/// </summary>
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);
}
}
}
}
}
/// <summary>
/// Creates an instance of the <see cref="FileLoggerProvider" />
@@ -39,6 +69,7 @@ namespace EonaCat.Logger
_maxFileSize = loggerOptions.FileSizeLimit;
_maxRetainedFiles = loggerOptions.RetainedFileCountLimit;
_maxRolloverFiles = loggerOptions.MaxRolloverFiles;
_maxTries = loggerOptions.MaxWriteTries;
}
/// <inheritdoc />
@@ -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;
}
}
}
}

View File

@@ -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<TState>(DateTimeOffset timestamp, LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> 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<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)

View File

@@ -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)

View File

@@ -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<ELogType>.ToString(LogType)}] [{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] {Message}";
return $"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper<ELogType>.ToString(LogType)}] {Message}";
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace EonaCat.Logger.Exceptions
{
public class EonaCatLoggerAssertionException : Exception
{
public EonaCatLoggerAssertionException(string message) : base(message) { }
}
}

View File

@@ -0,0 +1,74 @@
using System;
namespace EonaCat.Logger.Managers
{
/// <summary>
/// Colors to use when writing to the console.
/// </summary>
public class ColorSchema
{
private ConsoleColor currentForeground = Console.ForegroundColor;
/// <summary>
/// The color to use for debug messages. Default is dark gray on black.
/// </summary>
public ColorScheme Debug = new ColorScheme(ConsoleColor.DarkGray, ConsoleColor.Black);
/// <summary>
/// The color to use for informational messages. Default is gray on black.
/// </summary>
public ColorScheme Info = new ColorScheme(ConsoleColor.Gray, ConsoleColor.Black);
/// <summary>
/// The color to use for warning messages. Default is dark red on black.
/// </summary>
public ColorScheme Warning = new ColorScheme(ConsoleColor.DarkRed, ConsoleColor.Black);
/// <summary>
/// The color to use for error messages. Default is red on black.
/// </summary>
public ColorScheme Error = new ColorScheme(ConsoleColor.Red, ConsoleColor.Black);
/// <summary>
/// The color to use for alert messages. Default is dark yellow on black.
/// </summary>
public ColorScheme Traffic = new ColorScheme(ConsoleColor.DarkYellow, ConsoleColor.Black);
/// <summary>
/// The color to use for critical messages. Default is yellow on black.
/// </summary>
public ColorScheme Critical = new ColorScheme(ConsoleColor.Yellow, ConsoleColor.Black);
/// <summary>
/// The color to use for emergency messages. Default is white on red.
/// </summary>
public ColorScheme Trace = new ColorScheme(ConsoleColor.White, ConsoleColor.Red);
}
/// <summary>
/// Color scheme for logging messages.
/// </summary>
public class ColorScheme
{
/// <summary>
/// Foreground color.
/// </summary>
public ConsoleColor Foreground = Console.ForegroundColor;
/// <summary>
/// Background color.
/// </summary>
public ConsoleColor Background = Console.BackgroundColor;
/// <summary>
/// Instantiates a new color scheme.
/// </summary>
/// <param name="foreground">Foreground color.</param>
/// <param name="background">Background color.</param>
public ColorScheme(ConsoleColor foreground, ConsoleColor background)
{
Foreground = foreground;
Background = background;
}
}
}

View File

@@ -0,0 +1,8 @@
namespace EonaCat.Logger.Managers
{
public enum EFileLoggingMode
{
Enabled = 0,
Disabled = 1,
}
}

View File

@@ -0,0 +1,7 @@
namespace EonaCat.Logger.Managers
{
public interface ILogManager
{
void Write(string message, ELogType logType = ELogType.INFO, ELogType? logLevel = null);
}
}

View File

@@ -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();
/// <summary>
/// Format a message with the specified header
/// </summary>
/// <param name="settings">Logger settings</param>
/// <param name="logType">logtype for the formatted message</param>
/// <param name="currentMessage">The actual message to format with the header</param>
/// <returns></returns>
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;
}
/// <summary>
/// Formats a given exception as a string
/// </summary>
/// <param name="exception">exception</param>
/// <param name="module">The name of the module which called the code (optional)</param>
/// <param name="method">The name of the method which waws called in code (optional)</param>
/// <returns></returns>
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)
{
}
}
}
}
}
}

View File

@@ -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;
/// <summary>
/// Default Logger Instance with it's default configuration
/// </summary>
public static LogManager Instance => InstanceInit();
/// <summary>
/// Logging settings.
/// </summary>
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<ILoggerProvider>();
LoggerFactory = serviceProvider.GetService<ILoggerFactory>();
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<SyslogServer>();
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;
}
if (!settings.EnableFileLogging) return;
public LogManager(string logFolder = null, bool defaultPrefix = true)
{
CreateDefaultFileLoggerOptions();
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);
}
}
}

View File

@@ -0,0 +1,173 @@
using System.IO;
using System;
using System.Collections.Generic;
using EonaCat.Logger.Syslog;
using System.Runtime;
namespace EonaCat.Logger.Managers
{
/// <summary>
/// Logger settings.
/// </summary>
public class LoggerSettings
{
/// <summary>
/// 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.
/// </summary>
public string HeaderFormat
{
get
{
return _HeaderFormat;
}
set
{
if (string.IsNullOrEmpty(value)) _HeaderFormat = "";
else _HeaderFormat = value;
}
}
/// <summary>
/// Timestamp format.
/// </summary>
public string TimestampFormat
{
get
{
return _TimestampFormat;
}
set
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(HeaderFormat));
_TimestampFormat = value;
}
}
/// <summary>
/// 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.
/// </summary>
public bool EnableConsole
{
get
{
return _EnableConsole;
}
set
{
if (value) _EnableConsole = ConsoleExists();
else _EnableConsole = false;
}
}
/// <summary>
/// Minimum severity required to send a message.
/// </summary>
public ESeverity MinimumSeverity { get; set; } = ESeverity.Debug;
/// <summary>
/// Enable or disable use of color for console messages.
/// </summary>
public bool EnableColors { get; set; } = true;
/// <summary>
/// Colors to use for console messages based on message severity.
/// </summary>
public ColorSchema Colors
{
get
{
return _colors;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(Colors));
}
_colors = value;
}
}
public bool SendToSyslogServers { get; set; }
public List<SyslogServer> SysLogServers { get; set; } = new List<SyslogServer> { new SyslogServer("127.0.0.1", 514) };
/// <summary>
/// Determines if the fileLogging is enabled
/// </summary>
public bool EnableFileLogging { get; set; } = true;
private FileLoggerOptions _fileLoggerOptions;
/// <summary>
/// FileLogger settings.
/// </summary>
public FileLoggerOptions FileLoggerOptions
{
get
{
if (_fileLoggerOptions == null)
{
_fileLoggerOptions = CreateDefaultFileLoggerOptions();
}
return _fileLoggerOptions;
}
set
{
_fileLoggerOptions = value;
}
}
private FileLoggerOptions CreateDefaultFileLoggerOptions()
{
return new FileLoggerOptions();
}
/// <summary>
/// Maximum message length. Must be greater than or equal to 32. Default is 1024.
/// </summary>
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;
}
}
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
namespace EonaCat.Logger.Syslog
{
/// <summary>
/// Syslog server.
/// </summary>
public class SyslogServer
{
/// <summary>
/// Hostname.
/// </summary>
public string Hostname
{
get
{
return _Hostname;
}
set
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
_Hostname = value;
SetUdp();
}
}
/// <summary>
/// UDP port.
/// </summary>
public int Port
{
get
{
return _Port;
}
set
{
if (value < 0) throw new ArgumentException("Port must be zero or greater.");
_Port = value;
SetUdp();
}
}
/// <summary>
/// IP:port of the server.
/// </summary>
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;
/// <summary>
/// Instantiate the object.
/// </summary>
public SyslogServer()
{
}
/// <summary>
/// Instantiate the object.
/// </summary>
/// <param name="hostname">Hostname.</param>
/// <param name="port">Port.</param>
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);
}
}
}

View File

@@ -3,3 +3,11 @@
EonaCat Logger
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