Updated
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
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.
|
||||
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.
|
||||
|
||||
public static class Constants
|
||||
public static class Constants
|
||||
{
|
||||
public static class DateTimeFormats
|
||||
{
|
||||
public static class DateTimeFormats
|
||||
{
|
||||
public static string LOGGING { get; } = "yyyy-MM-dd HH:mm:ss";
|
||||
}
|
||||
public static string LOGGING { get; } = "yyyy-MM-dd HH:mm:ss";
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,22 @@
|
||||
namespace EonaCat.Logger
|
||||
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.
|
||||
|
||||
public static class DllInfo
|
||||
{
|
||||
// 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 const string NAME = "EonaCatLogger";
|
||||
public const string VERSION = "1.1.0";
|
||||
|
||||
public static class DllInfo
|
||||
static DllInfo()
|
||||
{
|
||||
public const string NAME = "EonaCatLogger";
|
||||
public const string VERSION = "1.1.0";
|
||||
|
||||
static DllInfo()
|
||||
{
|
||||
bool isDebug = false;
|
||||
var isDebug = false;
|
||||
#if DEBUG
|
||||
isDebug = true;
|
||||
isDebug = true;
|
||||
#endif
|
||||
VersionName = isDebug ? "DEBUG" : "RELEASE";
|
||||
}
|
||||
|
||||
internal static string VersionName { get; }
|
||||
|
||||
public static string ApplicationName { get; internal set; } = "EonaCatLogger";
|
||||
VersionName = isDebug ? "DEBUG" : "RELEASE";
|
||||
}
|
||||
|
||||
internal static string VersionName { get; }
|
||||
|
||||
public static string ApplicationName { get; internal set; } = "EonaCatLogger";
|
||||
}
|
||||
@@ -1,188 +1,192 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace EonaCat.Logger
|
||||
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.
|
||||
|
||||
public enum ELogType
|
||||
{
|
||||
// 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.
|
||||
NONE = 0,
|
||||
INFO = 1,
|
||||
WARNING = 2,
|
||||
ERROR = 3,
|
||||
TRAFFIC = 4,
|
||||
DEBUG = 5,
|
||||
CRITICAL = 6,
|
||||
TRACE = 7
|
||||
}
|
||||
|
||||
public enum ELogType
|
||||
public static class LogTypeConverter
|
||||
{
|
||||
public static ELogType FromLogLevel(this LogLevel logLevel)
|
||||
{
|
||||
NONE = 0,
|
||||
INFO = 1,
|
||||
WARNING = 2,
|
||||
ERROR = 3,
|
||||
TRAFFIC = 4,
|
||||
DEBUG = 5,
|
||||
CRITICAL = 6,
|
||||
TRACE = 7
|
||||
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;
|
||||
case LogLevel.Information:
|
||||
return ELogType.INFO;
|
||||
default:
|
||||
return ELogType.TRAFFIC;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LogTypeConverter
|
||||
public static LogLevel ToLogLevel(this ELogType logLevel)
|
||||
{
|
||||
public static ELogType FromLogLevel(this LogLevel logLevel)
|
||||
switch (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;
|
||||
case LogLevel.Information:
|
||||
return ELogType.INFO;
|
||||
default:
|
||||
return ELogType.TRAFFIC;
|
||||
}
|
||||
}
|
||||
|
||||
public static LogLevel ToLogLevel(this ELogType logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case ELogType.NONE:
|
||||
return LogLevel.None;
|
||||
case ELogType.ERROR:
|
||||
return LogLevel.Error;
|
||||
case ELogType.DEBUG:
|
||||
return LogLevel.Debug;
|
||||
case ELogType.CRITICAL:
|
||||
return LogLevel.Critical;
|
||||
case ELogType.WARNING:
|
||||
return LogLevel.Warning;
|
||||
case ELogType.TRACE:
|
||||
return LogLevel.Trace;
|
||||
case ELogType.TRAFFIC:
|
||||
return LogLevel.Trace;
|
||||
case ELogType.INFO:
|
||||
return LogLevel.Information;
|
||||
default:
|
||||
return LogLevel.Information;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToString(this ELogType logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case ELogType.NONE:
|
||||
return "NONE";
|
||||
case ELogType.ERROR:
|
||||
return "ERROR";
|
||||
case ELogType.DEBUG:
|
||||
return "DEBUG";
|
||||
case ELogType.CRITICAL:
|
||||
return "CRITICAL";
|
||||
case ELogType.WARNING:
|
||||
return "WARNING";
|
||||
case ELogType.TRACE:
|
||||
return "TRACE";
|
||||
case ELogType.TRAFFIC:
|
||||
return "TRAFFIC";
|
||||
case ELogType.INFO:
|
||||
return "INFO";
|
||||
default:
|
||||
return "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.TRACE;
|
||||
case ESeverity.Critical:
|
||||
return ELogType.CRITICAL;
|
||||
case ESeverity.Alert:
|
||||
return ELogType.TRAFFIC;
|
||||
case ESeverity.Error:
|
||||
return ELogType.ERROR;
|
||||
default:
|
||||
return ELogType.INFO;
|
||||
}
|
||||
}
|
||||
|
||||
public static int ToGrayLogLevel(this ELogType logLevel)
|
||||
{
|
||||
// Loglevel to GELF format
|
||||
switch (logLevel.ToString())
|
||||
{
|
||||
case "TRAFFIC": return 7;
|
||||
case "TRACE": return 7;
|
||||
case "DEBUG": return 7;
|
||||
case "INFO": return 6;
|
||||
case "WARNING": return 4;
|
||||
case "ERROR": return 3;
|
||||
case "CRITICAL": return 2;
|
||||
default: return 6; // Default to INFO
|
||||
}
|
||||
}
|
||||
|
||||
public static ESeverity ToSeverity(this ELogType logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case ELogType.DEBUG:
|
||||
return ESeverity.Debug;
|
||||
case ELogType.WARNING:
|
||||
return ESeverity.Warn;
|
||||
case ELogType.CRITICAL:
|
||||
return ESeverity.Critical;
|
||||
case ELogType.TRACE:
|
||||
return ESeverity.Emergency;
|
||||
case ELogType.ERROR:
|
||||
return ESeverity.Error;
|
||||
case ELogType.TRAFFIC:
|
||||
return ESeverity.Alert;
|
||||
default:
|
||||
return ESeverity.Info;
|
||||
}
|
||||
case ELogType.NONE:
|
||||
return LogLevel.None;
|
||||
case ELogType.ERROR:
|
||||
return LogLevel.Error;
|
||||
case ELogType.DEBUG:
|
||||
return LogLevel.Debug;
|
||||
case ELogType.CRITICAL:
|
||||
return LogLevel.Critical;
|
||||
case ELogType.WARNING:
|
||||
return LogLevel.Warning;
|
||||
case ELogType.TRACE:
|
||||
return LogLevel.Trace;
|
||||
case ELogType.TRAFFIC:
|
||||
return LogLevel.Trace;
|
||||
case ELogType.INFO:
|
||||
return LogLevel.Information;
|
||||
default:
|
||||
return LogLevel.Information;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToString(this ELogType logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case ELogType.NONE:
|
||||
return "NONE";
|
||||
case ELogType.ERROR:
|
||||
return "ERROR";
|
||||
case ELogType.DEBUG:
|
||||
return "DEBUG";
|
||||
case ELogType.CRITICAL:
|
||||
return "CRITICAL";
|
||||
case ELogType.WARNING:
|
||||
return "WARNING";
|
||||
case ELogType.TRACE:
|
||||
return "TRACE";
|
||||
case ELogType.TRAFFIC:
|
||||
return "TRAFFIC";
|
||||
case ELogType.INFO:
|
||||
return "INFO";
|
||||
default:
|
||||
return "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.TRACE;
|
||||
case ESeverity.Critical:
|
||||
return ELogType.CRITICAL;
|
||||
case ESeverity.Alert:
|
||||
return ELogType.TRAFFIC;
|
||||
case ESeverity.Error:
|
||||
return ELogType.ERROR;
|
||||
default:
|
||||
return ELogType.INFO;
|
||||
}
|
||||
}
|
||||
|
||||
public static int ToGrayLogLevel(this ELogType logLevel)
|
||||
{
|
||||
// Loglevel to GELF format
|
||||
switch (logLevel.ToString())
|
||||
{
|
||||
case "TRAFFIC": return 7;
|
||||
case "TRACE": return 7;
|
||||
case "DEBUG": return 7;
|
||||
case "INFO": return 6;
|
||||
case "WARNING": return 4;
|
||||
case "ERROR": return 3;
|
||||
case "CRITICAL": return 2;
|
||||
default: return 6; // Default to INFO
|
||||
}
|
||||
}
|
||||
|
||||
public static ESeverity ToSeverity(this ELogType logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case ELogType.DEBUG:
|
||||
return ESeverity.Debug;
|
||||
case ELogType.WARNING:
|
||||
return ESeverity.Warn;
|
||||
case ELogType.CRITICAL:
|
||||
return ESeverity.Critical;
|
||||
case ELogType.TRACE:
|
||||
return ESeverity.Emergency;
|
||||
case ELogType.ERROR:
|
||||
return ESeverity.Error;
|
||||
case ELogType.TRAFFIC:
|
||||
return ESeverity.Alert;
|
||||
default:
|
||||
return ESeverity.Info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message severity.
|
||||
/// </summary>
|
||||
public enum ESeverity
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug messages.
|
||||
/// </summary>
|
||||
Debug = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Message severity.
|
||||
/// Informational messages.
|
||||
/// </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
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Version>1.2.5</Version>
|
||||
<FileVersion>1.2.5</FileVersion>
|
||||
<Authors>EonaCat (Jeroen Saey)</Authors>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Company>EonaCat (Jeroen Saey)</Company>
|
||||
@@ -24,7 +24,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EVRevisionFormat>1.0.0.0+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||
<EVRevisionFormat>1.2.5+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||
<EVDefault>true</EVDefault>
|
||||
<EVInfo>true</EVInfo>
|
||||
<EVTagMatch>v[0-9]*</EVTagMatch>
|
||||
|
||||
@@ -2,40 +2,34 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.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.
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for adding the <see cref="FileLoggerProvider" /> to the <see cref="ILoggingBuilder" />
|
||||
/// </summary>
|
||||
public static class FileLoggerFactoryExtensions
|
||||
{
|
||||
// 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.
|
||||
private static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for adding the <see cref="FileLoggerProvider" /> to the <see cref="ILoggingBuilder" />
|
||||
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
||||
/// </summary>
|
||||
public static class FileLoggerFactoryExtensions
|
||||
/// <param name="builder">The <see cref="ILoggingBuilder" /> to use.</param>
|
||||
/// <param name="filenamePrefix">Sets the filename prefix to use for log files (optional)</param>
|
||||
/// <param name="fileLoggerOptions">the options for the fileLogger that needs to be used (optional)</param>
|
||||
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix = null,
|
||||
FileLoggerOptions fileLoggerOptions = null)
|
||||
{
|
||||
private static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
||||
return builder;
|
||||
}
|
||||
if (fileLoggerOptions == null) fileLoggerOptions = new FileLoggerOptions();
|
||||
|
||||
/// <summary>
|
||||
/// 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 (optional)</param>
|
||||
/// <param name="fileLoggerOptions">the options for the fileLogger that needs to be used (optional)</param>
|
||||
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix = null, FileLoggerOptions fileLoggerOptions = null)
|
||||
{
|
||||
if (fileLoggerOptions == null)
|
||||
{
|
||||
fileLoggerOptions = new FileLoggerOptions();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(filenamePrefix))
|
||||
{
|
||||
fileLoggerOptions.FileNamePrefix = filenamePrefix;
|
||||
}
|
||||
builder.AddEonaCatFileLogger(options =>
|
||||
if (!string.IsNullOrWhiteSpace(filenamePrefix)) fileLoggerOptions.FileNamePrefix = filenamePrefix;
|
||||
builder.AddEonaCatFileLogger(options =>
|
||||
{
|
||||
options.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
|
||||
options.FlushPeriod = fileLoggerOptions.FlushPeriod;
|
||||
@@ -49,26 +43,22 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
|
||||
options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
|
||||
options.UseLocalTime = fileLoggerOptions.UseLocalTime;
|
||||
}
|
||||
);
|
||||
return builder;
|
||||
}
|
||||
|
||||
);
|
||||
return builder;
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
||||
/// </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 AddEonaCatFileLogger(this ILoggingBuilder builder,
|
||||
Action<FileLoggerOptions> configure)
|
||||
{
|
||||
if (configure == null) throw new ArgumentNullException(nameof(configure));
|
||||
builder.AddEonaCatFileLogger();
|
||||
builder.Services.Configure(configure);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
||||
/// </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 AddEonaCatFileLogger(this ILoggingBuilder builder, Action<FileLoggerOptions> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
builder.AddEonaCatFileLogger();
|
||||
builder.Services.Configure(configure);
|
||||
|
||||
return builder;
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -2,108 +2,93 @@
|
||||
using System.IO;
|
||||
using EonaCat.Logger.EonaCatCoreLogger.Internal;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger;
|
||||
// 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.
|
||||
|
||||
/// <summary>
|
||||
/// Options for file logging.
|
||||
/// </summary>
|
||||
public class FileLoggerOptions : BatchingLoggerOptions
|
||||
{
|
||||
// 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.
|
||||
private int _fileSizeLimit = 200 * 1024 * 1024;
|
||||
private int _maxRolloverFiles = 10;
|
||||
private int _retainedFileCountLimit = 50;
|
||||
|
||||
public static string DefaultPath =>
|
||||
AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Options for file logging.
|
||||
/// Gets or sets a strictly positive value representing the maximum log size in bytes or null for no limit.
|
||||
/// Once the log is full, no more messages will be appended.
|
||||
/// Defaults to <c>200MB</c>.
|
||||
/// </summary>
|
||||
public class FileLoggerOptions : BatchingLoggerOptions
|
||||
public int FileSizeLimit
|
||||
{
|
||||
private int _fileSizeLimit = 200 * 1024 * 1024;
|
||||
private int _retainedFileCountLimit = 50;
|
||||
private int _maxRolloverFiles = 10;
|
||||
private int _maxTries = 3;
|
||||
get => _fileSizeLimit;
|
||||
|
||||
public static string DefaultPath => AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum log size in bytes or null for no limit.
|
||||
/// Once the log is full, no more messages will be appended.
|
||||
/// Defaults to <c>200MB</c>.
|
||||
/// </summary>
|
||||
public int FileSizeLimit
|
||||
set
|
||||
{
|
||||
get => _fileSizeLimit;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive.");
|
||||
}
|
||||
_fileSizeLimit = value;
|
||||
}
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive.");
|
||||
_fileSizeLimit = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum retained file count or null for no limit.
|
||||
/// Defaults to <c>50</c>.
|
||||
/// </summary>
|
||||
public int RetainedFileCountLimit
|
||||
{
|
||||
get => _retainedFileCountLimit;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(RetainedFileCountLimit)} must be positive.");
|
||||
}
|
||||
_retainedFileCountLimit = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Determines if we need to use the local time in the logging or UTC (default:false)
|
||||
/// </summary>
|
||||
public bool UseLocalTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit.
|
||||
/// Defaults to <c>10</c>.
|
||||
/// </summary>
|
||||
public int MaxRolloverFiles
|
||||
{
|
||||
get => _maxRolloverFiles;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(MaxRolloverFiles)} must be positive.");
|
||||
}
|
||||
_maxRolloverFiles = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filename prefix to use for log files.
|
||||
/// Defaults to <c>EonaCat_</c>.
|
||||
/// </summary>
|
||||
public string FileNamePrefix { get; set; } = "EonaCat";
|
||||
|
||||
/// <summary>
|
||||
/// The directory in which log files will be written, relative to the app process.
|
||||
/// Default to <c>executablePath\logs</c>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string LogDirectory { get; set; } = Path.Combine(DefaultPath, "logs");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum retained file count or null for no limit.
|
||||
/// Defaults to <c>50</c>.
|
||||
/// </summary>
|
||||
public int RetainedFileCountLimit
|
||||
{
|
||||
get => _retainedFileCountLimit;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value),
|
||||
$"{nameof(RetainedFileCountLimit)} must be positive.");
|
||||
_retainedFileCountLimit = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the max times to try to write to the file.
|
||||
/// Defaults 3.
|
||||
/// </summary>
|
||||
public int MaxWriteTries { get; set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we need to use the local time in the logging or UTC (default:false)
|
||||
/// </summary>
|
||||
public bool UseLocalTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit.
|
||||
/// Defaults to <c>10</c>.
|
||||
/// </summary>
|
||||
public int MaxRolloverFiles
|
||||
{
|
||||
get => _maxRolloverFiles;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(MaxRolloverFiles)} must be positive.");
|
||||
_maxRolloverFiles = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filename prefix to use for log files.
|
||||
/// Defaults to <c>EonaCat_</c>.
|
||||
/// </summary>
|
||||
public string FileNamePrefix { get; set; } = "EonaCat";
|
||||
|
||||
/// <summary>
|
||||
/// The directory in which log files will be written, relative to the app process.
|
||||
/// Default to <c>executablePath\logs</c>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string LogDirectory { get; set; } = Path.Combine(DefaultPath, "logs");
|
||||
}
|
||||
@@ -8,244 +8,207 @@ using EonaCat.Logger.EonaCatCoreLogger.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger;
|
||||
// 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.
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="ILoggerProvider" /> that writes logs to a file
|
||||
/// </summary>
|
||||
[ProviderAlias("EonaCatFileLogger")]
|
||||
public class FileLoggerProvider : BatchingLoggerProvider
|
||||
{
|
||||
// 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.
|
||||
private static readonly object WriteLock = new();
|
||||
private static readonly object RollOverLock = new();
|
||||
private readonly string _fileNamePrefix;
|
||||
private readonly int _maxFileSize;
|
||||
private readonly int _maxRetainedFiles;
|
||||
private readonly int _maxRolloverFiles;
|
||||
private readonly int _maxTries;
|
||||
private readonly string _path;
|
||||
private string _logFile;
|
||||
private bool _rollingOver;
|
||||
private int _rollOverCount;
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="ILoggerProvider" /> that writes logs to a file
|
||||
/// Creates an instance of the <see cref="FileLoggerProvider" />
|
||||
/// </summary>
|
||||
[ProviderAlias("EonaCatFileLogger")]
|
||||
public class FileLoggerProvider : BatchingLoggerProvider
|
||||
/// <param name="options">The options object controlling the logger</param>
|
||||
public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly string _fileNamePrefix;
|
||||
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 static readonly object RollOverLock = new object();
|
||||
private string _logFile;
|
||||
private bool _rollingOver;
|
||||
var loggerOptions = options.Value;
|
||||
_path = loggerOptions.LogDirectory;
|
||||
_fileNamePrefix = loggerOptions.FileNamePrefix;
|
||||
_maxFileSize = loggerOptions.FileSizeLimit;
|
||||
_maxRetainedFiles = loggerOptions.RetainedFileCountLimit;
|
||||
_maxRolloverFiles = loggerOptions.MaxRolloverFiles;
|
||||
_maxTries = loggerOptions.MaxWriteTries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The file to which log messages should be appended.
|
||||
/// </summary>
|
||||
public string LogFile
|
||||
/// <summary>
|
||||
/// The file to which log messages should be appended.
|
||||
/// </summary>
|
||||
public string LogFile
|
||||
{
|
||||
get => _logFile;
|
||||
set
|
||||
{
|
||||
get
|
||||
_logFile = value;
|
||||
|
||||
if (!string.IsNullOrEmpty(_logFile))
|
||||
{
|
||||
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" />
|
||||
/// </summary>
|
||||
/// <param name="options">The options object controlling the logger</param>
|
||||
public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
|
||||
{
|
||||
FileLoggerOptions loggerOptions = options.Value;
|
||||
_path = loggerOptions.LogDirectory;
|
||||
_fileNamePrefix = loggerOptions.FileNamePrefix;
|
||||
_maxFileSize = loggerOptions.FileSizeLimit;
|
||||
_maxRetainedFiles = loggerOptions.RetainedFileCountLimit;
|
||||
_maxRolloverFiles = loggerOptions.MaxRolloverFiles;
|
||||
_maxTries = loggerOptions.MaxWriteTries;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken cancellationToken)
|
||||
{
|
||||
Directory.CreateDirectory(_path);
|
||||
|
||||
foreach (IGrouping<(int Year, int Month, int Day), LogMessage> group in messages.GroupBy(GetGrouping))
|
||||
{
|
||||
LogFile = GetFullName(group.Key);
|
||||
FileInfo fileInfo = new FileInfo(LogFile);
|
||||
if (_maxFileSize > 0 && fileInfo.Exists && fileInfo.Length > _maxFileSize)
|
||||
{
|
||||
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
|
||||
{
|
||||
if (_rollOverCount < _maxRolloverFiles)
|
||||
{
|
||||
var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log");
|
||||
if (File.Exists(rollOverFile))
|
||||
{
|
||||
File.Delete(rollOverFile);
|
||||
}
|
||||
fileInfo.CopyTo(rollOverFile);
|
||||
File.WriteAllText(LogFile, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (RollOverLock)
|
||||
{
|
||||
_rollingOver = true;
|
||||
MoveRolloverLogFiles();
|
||||
_rollingOver = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (_rollingOver)
|
||||
{
|
||||
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
lock (WriteLock)
|
||||
{
|
||||
int tries = 0;
|
||||
bool completed = false;
|
||||
|
||||
while (!completed)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.StreamWriter file = new System.IO.StreamWriter(LogFile, true);
|
||||
foreach (LogMessage item in group)
|
||||
{
|
||||
file.Write(item.Message);
|
||||
}
|
||||
file.Close();
|
||||
completed = true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
tries++;
|
||||
Task.Delay(100);
|
||||
if (tries >= _maxTries)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeleteOldLogFiles();
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFullName((int Year, int Month, int Day) group)
|
||||
{
|
||||
bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
|
||||
if (hasPrefix)
|
||||
{
|
||||
return Path.Combine(_path, $"{_fileNamePrefix}_{group.Year:0000}{group.Month:00}{group.Day:00}.log");
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(_path, $"{group.Year:0000}{group.Month:00}{group.Day:00}.log");
|
||||
}
|
||||
}
|
||||
|
||||
private (int Year, int Month, int Day) GetGrouping(LogMessage message)
|
||||
{
|
||||
return (message.Timestamp.Year, message.Timestamp.Month, message.Timestamp.Day);
|
||||
}
|
||||
|
||||
private static void MoveFile(string copyFromPath, string copyToPath)
|
||||
{
|
||||
var origin = new FileInfo(copyFromPath);
|
||||
origin.MoveTo(copyToPath);
|
||||
|
||||
var destination = new FileInfo(copyToPath);
|
||||
destination.CreationTime = origin.CreationTime;
|
||||
destination.LastWriteTime = origin.LastWriteTime;
|
||||
destination.LastAccessTime = origin.LastAccessTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rollover logFiles
|
||||
/// </summary>
|
||||
protected void MoveRolloverLogFiles()
|
||||
{
|
||||
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
|
||||
{
|
||||
if (_rollOverCount >= _maxRolloverFiles)
|
||||
{
|
||||
var maxRollover = _rollOverCount;
|
||||
bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
|
||||
IEnumerable<FileInfo> files;
|
||||
if (hasPrefix)
|
||||
{
|
||||
files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*").OrderBy(x => x.CreationTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
files = new DirectoryInfo(_path).GetFiles("*").OrderBy(x => x.CreationTime);
|
||||
}
|
||||
|
||||
for (int i = files.Count() -1; i >= 0; i--)
|
||||
{
|
||||
var currentFile = files.ElementAt(i);
|
||||
if (i == 0)
|
||||
{
|
||||
// Temporary move first file
|
||||
var newFilename2 = Path.GetFileName(currentFile.FullName).Replace($".log", $"_{i + 1}.log");
|
||||
MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == files.Count() - 1)
|
||||
{
|
||||
// Delete the last file
|
||||
File.Delete(currentFile.FullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
var newFilename = Path.GetFileName(currentFile.FullName).Replace($"_{i}.log", $"_{i + 1}.log");
|
||||
MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename}");
|
||||
}
|
||||
_rollOverCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes old log files, keeping a number of files defined by <see cref="FileLoggerOptions.RetainedFileCountLimit" />
|
||||
/// </summary>
|
||||
protected void DeleteOldLogFiles()
|
||||
{
|
||||
if (_maxRetainedFiles > 0)
|
||||
{
|
||||
bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
|
||||
IEnumerable<FileInfo> files = null;
|
||||
|
||||
if (hasPrefix)
|
||||
{
|
||||
files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*");
|
||||
}
|
||||
else
|
||||
{
|
||||
files = new DirectoryInfo(_path).GetFiles("*");
|
||||
}
|
||||
|
||||
files = files.OrderByDescending(file => file.Name).Skip(_maxRetainedFiles);
|
||||
|
||||
foreach (FileInfo item in files)
|
||||
{
|
||||
item.Delete();
|
||||
}
|
||||
var dir = Path.GetDirectoryName(_logFile);
|
||||
if (!string.IsNullOrEmpty(dir))
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task WriteMessagesAsync(IEnumerable<LogMessage> messages,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Directory.CreateDirectory(_path);
|
||||
|
||||
foreach (var group in messages.GroupBy(GetGrouping))
|
||||
{
|
||||
LogFile = GetFullName(group.Key);
|
||||
var fileInfo = new FileInfo(LogFile);
|
||||
if (_maxFileSize > 0 && fileInfo.Exists && fileInfo.Length > _maxFileSize)
|
||||
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
|
||||
{
|
||||
if (_rollOverCount < _maxRolloverFiles)
|
||||
{
|
||||
var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log");
|
||||
if (File.Exists(rollOverFile)) File.Delete(rollOverFile);
|
||||
fileInfo.CopyTo(rollOverFile);
|
||||
File.WriteAllText(LogFile, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (RollOverLock)
|
||||
{
|
||||
_rollingOver = true;
|
||||
MoveRolloverLogFiles();
|
||||
_rollingOver = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (_rollingOver) await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
lock (WriteLock)
|
||||
{
|
||||
var tries = 0;
|
||||
var completed = false;
|
||||
|
||||
while (!completed)
|
||||
try
|
||||
{
|
||||
var file = new StreamWriter(LogFile, true);
|
||||
foreach (var item in group) file.Write(item.Message);
|
||||
file.Close();
|
||||
completed = true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
tries++;
|
||||
Task.Delay(100);
|
||||
if (tries >= _maxTries)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
DeleteOldLogFiles();
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFullName((int Year, int Month, int Day) group)
|
||||
{
|
||||
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
|
||||
if (hasPrefix)
|
||||
return Path.Combine(_path, $"{_fileNamePrefix}_{group.Year:0000}{group.Month:00}{group.Day:00}.log");
|
||||
return Path.Combine(_path, $"{group.Year:0000}{group.Month:00}{group.Day:00}.log");
|
||||
}
|
||||
|
||||
private (int Year, int Month, int Day) GetGrouping(LogMessage message)
|
||||
{
|
||||
return (message.Timestamp.Year, message.Timestamp.Month, message.Timestamp.Day);
|
||||
}
|
||||
|
||||
private static void MoveFile(string copyFromPath, string copyToPath)
|
||||
{
|
||||
var origin = new FileInfo(copyFromPath);
|
||||
origin.MoveTo(copyToPath);
|
||||
|
||||
var destination = new FileInfo(copyToPath);
|
||||
destination.CreationTime = origin.CreationTime;
|
||||
destination.LastWriteTime = origin.LastWriteTime;
|
||||
destination.LastAccessTime = origin.LastAccessTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rollover logFiles
|
||||
/// </summary>
|
||||
protected void MoveRolloverLogFiles()
|
||||
{
|
||||
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
|
||||
if (_rollOverCount >= _maxRolloverFiles)
|
||||
{
|
||||
var maxRollover = _rollOverCount;
|
||||
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
|
||||
IEnumerable<FileInfo> files;
|
||||
if (hasPrefix)
|
||||
files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*").OrderBy(x => x.CreationTime);
|
||||
else
|
||||
files = new DirectoryInfo(_path).GetFiles("*").OrderBy(x => x.CreationTime);
|
||||
|
||||
for (var i = files.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
var currentFile = files.ElementAt(i);
|
||||
if (i == 0)
|
||||
{
|
||||
// Temporary move first file
|
||||
var newFilename2 = Path.GetFileName(currentFile.FullName).Replace(".log", $"_{i + 1}.log");
|
||||
MoveFile(currentFile.FullName,
|
||||
$@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == files.Count() - 1)
|
||||
{
|
||||
// Delete the last file
|
||||
File.Delete(currentFile.FullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
var newFilename = Path.GetFileName(currentFile.FullName).Replace($"_{i}.log", $"_{i + 1}.log");
|
||||
MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename}");
|
||||
}
|
||||
|
||||
_rollOverCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes old log files, keeping a number of files defined by <see cref="FileLoggerOptions.RetainedFileCountLimit" />
|
||||
/// </summary>
|
||||
protected void DeleteOldLogFiles()
|
||||
{
|
||||
if (_maxRetainedFiles > 0)
|
||||
{
|
||||
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
|
||||
IEnumerable<FileInfo> files = null;
|
||||
|
||||
if (hasPrefix)
|
||||
files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*");
|
||||
else
|
||||
files = new DirectoryInfo(_path).GetFiles("*");
|
||||
|
||||
files = files.OrderByDescending(file => file.Name).Skip(_maxRetainedFiles);
|
||||
|
||||
foreach (var item in files) item.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,68 +4,67 @@ using EonaCat.Logger.Extensions;
|
||||
using EonaCat.Logger.Managers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
|
||||
// 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 BatchingLogger : ILogger
|
||||
{
|
||||
// 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.
|
||||
private readonly string _category;
|
||||
private readonly BatchingLoggerProvider _provider;
|
||||
private LoggerSettings _loggerSettings;
|
||||
|
||||
public class BatchingLogger : ILogger
|
||||
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings)
|
||||
{
|
||||
private LoggerSettings _loggerSettings;
|
||||
private readonly BatchingLoggerProvider _provider;
|
||||
private readonly string _category;
|
||||
private DateTimeOffset CurrentDateTimeOffset => _loggerSettings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||
private DateTime CurrentDateTme => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
_loggerSettings = loggerSettings;
|
||||
_provider = loggerProvider;
|
||||
_category = categoryName;
|
||||
}
|
||||
|
||||
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings)
|
||||
private DateTimeOffset CurrentDateTimeOffset =>
|
||||
_loggerSettings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||
|
||||
private DateTime CurrentDateTme => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return logLevel != LogLevel.None;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
|
||||
Func<TState, Exception, string> formatter)
|
||||
{
|
||||
Log(CurrentDateTimeOffset, logLevel, eventId, state, exception, formatter);
|
||||
}
|
||||
|
||||
public void Log<TState>(DateTimeOffset timestamp, LogLevel logLevel, EventId eventId, TState state,
|
||||
Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel)) return;
|
||||
|
||||
if (_loggerSettings == null) _loggerSettings = new LoggerSettings();
|
||||
|
||||
var message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(),
|
||||
formatter(state, exception), timestamp.DateTime) + Environment.NewLine;
|
||||
if (exception != null) message = exception.FormatExceptionToMessage() + Environment.NewLine;
|
||||
|
||||
_provider.AddMessage(timestamp, message);
|
||||
|
||||
var currentMessage = new EonaCatLogMessage
|
||||
{
|
||||
_loggerSettings = loggerSettings;
|
||||
_provider = loggerProvider;
|
||||
_category = categoryName;
|
||||
}
|
||||
DateTime = timestamp.DateTime,
|
||||
Message = message,
|
||||
LogType = logLevel.FromLogLevel()
|
||||
};
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
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 (_loggerSettings == null)
|
||||
{
|
||||
_loggerSettings = new LoggerSettings();
|
||||
}
|
||||
|
||||
var message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), formatter(state, exception), timestamp.DateTime) + Environment.NewLine;
|
||||
if (exception != null)
|
||||
{
|
||||
message = exception.FormatExceptionToMessage() + Environment.NewLine;
|
||||
}
|
||||
|
||||
_provider.AddMessage(timestamp, message);
|
||||
|
||||
var currentMessage = new EonaCatLogMessage
|
||||
{
|
||||
DateTime = timestamp.DateTime,
|
||||
Message = message,
|
||||
LogType = logLevel.FromLogLevel()
|
||||
};
|
||||
|
||||
if (_loggerSettings == null) return;
|
||||
currentMessage.Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin) ? "BatchingLogger" : _loggerSettings.LogOrigin;
|
||||
_loggerSettings?.OnLogEvent(currentMessage);
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
Log(CurrentDateTimeOffset, logLevel, eventId, state, exception, formatter);
|
||||
}
|
||||
currentMessage.Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin)
|
||||
? "BatchingLogger"
|
||||
: _loggerSettings.LogOrigin;
|
||||
_loggerSettings?.OnLogEvent(currentMessage);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
|
||||
// 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 BatchingLoggerOptions
|
||||
{
|
||||
// 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.
|
||||
private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100);
|
||||
|
||||
public class BatchingLoggerOptions
|
||||
/// <summary>
|
||||
/// Gets or sets the period after which logs will be flushed to the store.
|
||||
/// </summary>
|
||||
public TimeSpan FlushPeriod
|
||||
{
|
||||
private int _batchSize = 0;
|
||||
private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100);
|
||||
get => _flushPeriod;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the period after which logs will be flushed to the store.
|
||||
/// </summary>
|
||||
public TimeSpan FlushPeriod
|
||||
set
|
||||
{
|
||||
get => _flushPeriod;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive.");
|
||||
}
|
||||
_flushPeriod = value;
|
||||
}
|
||||
if (value <= TimeSpan.Zero)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive.");
|
||||
_flushPeriod = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a maximum number of events to include in a single batch or less than 1 for no limit.
|
||||
/// </summary>
|
||||
public int BatchSize
|
||||
{
|
||||
get => _batchSize;
|
||||
|
||||
set
|
||||
{
|
||||
_batchSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets value indicating if logger accepts and queues writes.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a maximum number of events to include in a single batch or less than 1 for no limit.
|
||||
/// </summary>
|
||||
public int BatchSize { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets value indicating if logger accepts and queues writes.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
||||
@@ -7,161 +7,151 @@ using EonaCat.Logger.Managers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
|
||||
// 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 abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
||||
{
|
||||
// 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.
|
||||
private readonly int _batchSize;
|
||||
|
||||
public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
||||
private readonly List<LogMessage> _currentBatch = new();
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private LoggerSettings _loggerSettings;
|
||||
|
||||
private ConcurrentQueue<LogMessage> _messageQueue;
|
||||
private Task _outputTask;
|
||||
|
||||
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
|
||||
{
|
||||
protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||
protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
var loggerOptions = options.Value;
|
||||
|
||||
private readonly List<LogMessage> _currentBatch = new List<LogMessage>();
|
||||
private readonly TimeSpan _interval;
|
||||
private readonly int _batchSize;
|
||||
if (loggerOptions.FlushPeriod <= TimeSpan.Zero)
|
||||
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod),
|
||||
$"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
|
||||
|
||||
private ConcurrentQueue<LogMessage> _messageQueue;
|
||||
private Task _outputTask;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private LoggerSettings _loggerSettings;
|
||||
if (options.Value is FileLoggerOptions fileLoggerOptions) UseLocalTime = fileLoggerOptions.UseLocalTime;
|
||||
_batchSize = loggerOptions.BatchSize;
|
||||
|
||||
protected bool UseLocalTime { get; set; }
|
||||
Start();
|
||||
}
|
||||
|
||||
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
|
||||
protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||
protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
|
||||
protected bool UseLocalTime { get; set; }
|
||||
|
||||
protected LoggerSettings LoggerSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
BatchingLoggerOptions loggerOptions = options.Value;
|
||||
if (_loggerSettings != null) return _loggerSettings;
|
||||
|
||||
if (loggerOptions.FlushPeriod <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
|
||||
}
|
||||
|
||||
if (options.Value is FileLoggerOptions fileLoggerOptions)
|
||||
{
|
||||
UseLocalTime = fileLoggerOptions.UseLocalTime;
|
||||
}
|
||||
|
||||
_interval = loggerOptions.FlushPeriod;
|
||||
_batchSize = loggerOptions.BatchSize;
|
||||
|
||||
Start();
|
||||
_loggerSettings = new LoggerSettings();
|
||||
_loggerSettings.UseLocalTime = UseLocalTime;
|
||||
return _loggerSettings;
|
||||
}
|
||||
|
||||
protected LoggerSettings LoggerSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_loggerSettings != null) return _loggerSettings;
|
||||
set => _loggerSettings = value;
|
||||
}
|
||||
|
||||
_loggerSettings = new LoggerSettings();
|
||||
_loggerSettings.UseLocalTime = UseLocalTime;
|
||||
return _loggerSettings;
|
||||
public bool IsStarted { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
while (!_messageQueue.IsEmpty) Task.Delay(10);
|
||||
|
||||
StopAsync().GetAwaiter().GetResult();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new BatchingLogger(this, categoryName, LoggerSettings);
|
||||
}
|
||||
|
||||
protected abstract Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token);
|
||||
|
||||
private async Task ProcessLogQueueAsync(object state)
|
||||
{
|
||||
var startupMessage = $"{DllInfo.ApplicationName} started.{Environment.NewLine}";
|
||||
startupMessage =
|
||||
LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, startupMessage, CurrentDateTme);
|
||||
AddMessage(DateTimeOffset.Now, startupMessage);
|
||||
|
||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
var limit = _batchSize <= 0 ? int.MaxValue : _batchSize;
|
||||
while (limit > 0 && _messageQueue.TryDequeue(out var message))
|
||||
{
|
||||
_currentBatch.Add(message);
|
||||
limit--;
|
||||
}
|
||||
|
||||
set => _loggerSettings = value;
|
||||
}
|
||||
|
||||
protected abstract Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token);
|
||||
|
||||
private async Task ProcessLogQueueAsync(object state)
|
||||
{
|
||||
var startupMessage = $"{DllInfo.ApplicationName} started.{Environment.NewLine}";
|
||||
startupMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, startupMessage, CurrentDateTme);
|
||||
|
||||
AddMessage(DateTimeOffset.Now, startupMessage);
|
||||
|
||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
int limit = _batchSize <= 0 ? int.MaxValue : _batchSize;
|
||||
|
||||
while (limit > 0 && _messageQueue.TryDequeue(out LogMessage message))
|
||||
if (_currentBatch.Count > 0)
|
||||
try
|
||||
{
|
||||
_currentBatch.Add(message);
|
||||
limit--;
|
||||
await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
_currentBatch.Clear();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (_currentBatch.Count > 0)
|
||||
{
|
||||
bool isBatchWritten = false;
|
||||
try
|
||||
{
|
||||
await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
isBatchWritten = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (isBatchWritten)
|
||||
{
|
||||
_currentBatch.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
await IntervalAsync(_interval, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
await WriteMessagesAsync(new List<LogMessage> { new LogMessage { Message = $"[{DllInfo.ApplicationName}] {DllInfo.ApplicationName} stopped.{Environment.NewLine}", Timestamp = CurrentDateTimeOffset } }, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected virtual Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.Delay(interval, cancellationToken);
|
||||
}
|
||||
|
||||
internal void AddMessage(DateTimeOffset timestamp, string message)
|
||||
{
|
||||
_messageQueue.Enqueue(new LogMessage { Message = message, Timestamp = timestamp });
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_messageQueue = new ConcurrentQueue<LogMessage>();
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_outputTask = Task.Factory.StartNew(
|
||||
ProcessLogQueueAsync,
|
||||
null,
|
||||
TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
private async Task StopAsync()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
|
||||
try
|
||||
{
|
||||
await _outputTask.ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (AggregateException exception) when (exception.InnerExceptions.Count == 1 && exception.InnerExceptions[0] is TaskCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
while (!_messageQueue.IsEmpty)
|
||||
{
|
||||
_messageQueue.TryDequeue(out _);
|
||||
}
|
||||
|
||||
StopAsync().GetAwaiter().GetResult();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~BatchingLoggerProvider()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new BatchingLogger(this, categoryName, LoggerSettings);
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddMessage(DateTimeOffset timestamp, string message)
|
||||
{
|
||||
_messageQueue.Enqueue(new LogMessage { Message = message, Timestamp = timestamp });
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
IsStarted = true;
|
||||
_messageQueue = new ConcurrentQueue<LogMessage>();
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_outputTask = Task.Factory.StartNew(
|
||||
ProcessLogQueueAsync,
|
||||
null,
|
||||
TaskCreationOptions.LongRunning).ContinueWith(async x =>
|
||||
{
|
||||
var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}";
|
||||
stopMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, stopMessage, CurrentDateTme);
|
||||
await WriteMessagesAsync(
|
||||
new List<LogMessage>(new List<LogMessage>
|
||||
{ new() { Message = stopMessage, Timestamp = CurrentDateTme } }),
|
||||
_cancellationTokenSource.Token)
|
||||
.ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task StopAsync()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
|
||||
try
|
||||
{
|
||||
while (_outputTask.Status != TaskStatus.RanToCompletion && _outputTask.Status != TaskStatus.Canceled)
|
||||
{
|
||||
await _outputTask.ConfigureAwait(false);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (AggregateException exception) when (exception.InnerExceptions.Count == 1 &&
|
||||
exception.InnerExceptions[0] is TaskCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
~BatchingLoggerProvider()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||
{
|
||||
// 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.
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
|
||||
// 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 struct LogMessage
|
||||
{
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
public struct LogMessage
|
||||
{
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
using System;
|
||||
using EonaCat.Logger.Helpers;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Models
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Models;
|
||||
// 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 EonaCatLogMessage
|
||||
{
|
||||
// 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 DateTime DateTime { get; set; }
|
||||
public string Message { get; set; }
|
||||
public ELogType LogType { get; set; }
|
||||
public string Origin { get; set; }
|
||||
|
||||
public class EonaCatLogMessage
|
||||
public override string ToString()
|
||||
{
|
||||
public DateTime DateTime { get; set; }
|
||||
public string Message { get; set; }
|
||||
public ELogType LogType { get; set; }
|
||||
public string Origin { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper<ELogType>.ToString(LogType)}] {Message}";
|
||||
}
|
||||
return
|
||||
$"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper<ELogType>.ToString(LogType)}] {Message}";
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace EonaCat.Logger.Exceptions
|
||||
namespace EonaCat.Logger.Exceptions;
|
||||
|
||||
public class EonaCatLoggerAssertionException : Exception
|
||||
{
|
||||
public class EonaCatLoggerAssertionException : Exception
|
||||
public EonaCatLoggerAssertionException(string message) : base(message)
|
||||
{
|
||||
public EonaCatLoggerAssertionException(string message) : base(message) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace EonaCat.Logger.Extensions
|
||||
namespace EonaCat.Logger.Extensions;
|
||||
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
public static class DateTimeExtensions
|
||||
public static long ToUnixTimestamp(this DateTime dateTime)
|
||||
{
|
||||
public static long ToUnixTimestamp(this DateTime dateTime)
|
||||
{
|
||||
return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
|
||||
}
|
||||
return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,70 +3,73 @@ using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace EonaCat.Logger.Extensions
|
||||
namespace EonaCat.Logger.Extensions;
|
||||
|
||||
public static class ExceptionExtensions
|
||||
{
|
||||
public static class ExceptionExtensions
|
||||
public static string FormatExceptionToMessage(this Exception exception, string module = null, string method = null)
|
||||
{
|
||||
public static string FormatExceptionToMessage(this Exception exception, string module = null, string method = null)
|
||||
{
|
||||
if (exception == null)
|
||||
return string.Empty;
|
||||
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();
|
||||
var st = new StackTrace(exception, true);
|
||||
var frame = st.GetFrame(0);
|
||||
var fileLine = frame.GetFileLineNumber();
|
||||
var filename = frame.GetFileName();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("--- Exception details provided by EonaCatLogger ---");
|
||||
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("---");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("--- Exception details provided by EonaCatLogger ---");
|
||||
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)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (DictionaryEntry entry in data)
|
||||
{
|
||||
sb.Append(" | ")
|
||||
.Append(entry.Key)
|
||||
.Append(": ")
|
||||
.AppendLine(entry.Value.ToString());
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string FormatInnerException(Exception innerException)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine(innerException.GetType().ToString())
|
||||
.AppendLine(" Message : " + innerException.Message)
|
||||
.AppendLine(" Source : " + innerException.Source)
|
||||
.AppendLine(" StackTrace : " + innerException.StackTrace)
|
||||
.AppendLine(" ToString : " + innerException.ToString())
|
||||
.Append(" Data : ")
|
||||
.AppendLine(innerException.Data != null && innerException.Data.Count > 0 ? FormatExceptionData(innerException.Data) : "(none)");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatExceptionData(IDictionary data)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (DictionaryEntry entry in data)
|
||||
sb.Append(" | ")
|
||||
.Append(entry.Key)
|
||||
.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();
|
||||
}
|
||||
}
|
||||
@@ -1,261 +1,194 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace EonaCat.Logger.Extensions
|
||||
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 class OffsetStream : Stream
|
||||
{
|
||||
// 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.
|
||||
private const int BufferSize = 4096;
|
||||
|
||||
public class OffsetStream : Stream
|
||||
public OffsetStream(Stream stream, long offset = 0, long length = 0, bool readOnly = false, bool ownStream = false)
|
||||
{
|
||||
private const int BufferSize = 4096;
|
||||
|
||||
public OffsetStream(Stream stream, long offset = 0, long length = 0, bool readOnly = false, bool ownStream = false)
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
if (offset > stream.Length)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
if (offset > stream.Length) throw new EndOfStreamException();
|
||||
|
||||
BaseStreamOffset = offset;
|
||||
|
||||
if (length > stream.Length - offset)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
Length1 = stream.Length - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
Length1 = length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseStreamOffset = 0;
|
||||
Length1 = length;
|
||||
}
|
||||
|
||||
BaseStream = stream;
|
||||
ReadOnly = readOnly;
|
||||
OwnStream = ownStream;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
if (OwnStream & (BaseStream != null))
|
||||
{
|
||||
BaseStream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Disposed = true;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override bool CanRead => BaseStream.CanRead;
|
||||
|
||||
public override bool CanSeek => BaseStream.CanSeek;
|
||||
|
||||
public override bool CanWrite => BaseStream.CanWrite && !ReadOnly;
|
||||
|
||||
public override long Length => Length1;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => Position1;
|
||||
|
||||
set
|
||||
{
|
||||
if (value > Length1)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
if (!BaseStream.CanSeek)
|
||||
{
|
||||
throw new NotSupportedException("Cannot seek stream.");
|
||||
}
|
||||
|
||||
Position1 = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
if (ReadOnly)
|
||||
{
|
||||
throw new IOException("OffsetStream is read only.");
|
||||
}
|
||||
|
||||
BaseStream.Flush();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Count cannot be less than 1.");
|
||||
}
|
||||
|
||||
if (Position1 >= Length1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count > Length1 - Position1)
|
||||
{
|
||||
count = Convert.ToInt32(Length1 - Position1);
|
||||
}
|
||||
|
||||
if (BaseStream.CanSeek)
|
||||
{
|
||||
BaseStream.Position = BaseStreamOffset + Position1;
|
||||
}
|
||||
|
||||
int bytesRead = BaseStream.Read(buffer, offset, count);
|
||||
Position1 += bytesRead;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
if (!BaseStream.CanSeek)
|
||||
{
|
||||
throw new IOException("Stream is not seekable.");
|
||||
}
|
||||
|
||||
long pos;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
pos = offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.Current:
|
||||
pos = Position1 + offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.End:
|
||||
pos = Length1 + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos < 0 || pos >= Length1)
|
||||
{
|
||||
throw new EndOfStreamException("OffsetStream reached begining/end of stream.");
|
||||
}
|
||||
|
||||
Position1 = pos;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
if (ReadOnly)
|
||||
{
|
||||
throw new IOException("OffsetStream is read only.");
|
||||
}
|
||||
|
||||
BaseStream.SetLength(BaseStreamOffset + value);
|
||||
Length1 = value;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (ReadOnly)
|
||||
{
|
||||
throw new IOException("OffsetStream is read only.");
|
||||
}
|
||||
|
||||
if (count < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long pos = Position1 + count;
|
||||
|
||||
if (pos > Length1)
|
||||
{
|
||||
throw new EndOfStreamException("OffsetStream reached end of stream.");
|
||||
}
|
||||
|
||||
if (BaseStream.CanSeek)
|
||||
{
|
||||
BaseStream.Position = BaseStreamOffset + Position1;
|
||||
}
|
||||
|
||||
BaseStream.Write(buffer, offset, count);
|
||||
Position1 = pos;
|
||||
}
|
||||
|
||||
public long BaseStreamOffset { get; private set; }
|
||||
|
||||
public Stream BaseStream { get; }
|
||||
public long Length1 { get; set; }
|
||||
public long Position1 { get; set; }
|
||||
|
||||
public bool ReadOnly { get; }
|
||||
|
||||
public bool Disposed { get; set; }
|
||||
|
||||
public bool OwnStream { get; }
|
||||
|
||||
public void Reset(long offset, long length, long position)
|
||||
{
|
||||
BaseStreamOffset = offset;
|
||||
|
||||
if (length > stream.Length - offset) throw new EndOfStreamException();
|
||||
|
||||
if (length == 0)
|
||||
Length1 = stream.Length - offset;
|
||||
else
|
||||
Length1 = length;
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseStreamOffset = 0;
|
||||
Length1 = length;
|
||||
Position1 = position;
|
||||
}
|
||||
|
||||
public void WriteTo(Stream stream)
|
||||
BaseStream = stream;
|
||||
ReadOnly = readOnly;
|
||||
OwnStream = ownStream;
|
||||
}
|
||||
|
||||
public override bool CanRead => BaseStream.CanRead;
|
||||
|
||||
public override bool CanSeek => BaseStream.CanSeek;
|
||||
|
||||
public override bool CanWrite => BaseStream.CanWrite && !ReadOnly;
|
||||
|
||||
public override long Length => Length1;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => Position1;
|
||||
|
||||
set
|
||||
{
|
||||
WriteTo(stream, BufferSize);
|
||||
if (value > Length1) throw new EndOfStreamException();
|
||||
|
||||
if (!BaseStream.CanSeek) throw new NotSupportedException("Cannot seek stream.");
|
||||
|
||||
Position1 = value;
|
||||
}
|
||||
}
|
||||
|
||||
public long BaseStreamOffset { get; private set; }
|
||||
|
||||
public Stream BaseStream { get; }
|
||||
public long Length1 { get; set; }
|
||||
public long Position1 { get; set; }
|
||||
|
||||
public bool ReadOnly { get; }
|
||||
|
||||
public bool Disposed { get; set; }
|
||||
|
||||
public bool OwnStream { get; }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed) return;
|
||||
|
||||
if (disposing)
|
||||
if (OwnStream & (BaseStream != null))
|
||||
BaseStream.Dispose();
|
||||
|
||||
Disposed = true;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
if (ReadOnly) throw new IOException("OffsetStream is read only.");
|
||||
|
||||
BaseStream.Flush();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count < 1) throw new ArgumentOutOfRangeException("Count cannot be less than 1.");
|
||||
|
||||
if (Position1 >= Length1) return 0;
|
||||
|
||||
if (count > Length1 - Position1) count = Convert.ToInt32(Length1 - Position1);
|
||||
|
||||
if (BaseStream.CanSeek) BaseStream.Position = BaseStreamOffset + Position1;
|
||||
|
||||
var bytesRead = BaseStream.Read(buffer, offset, count);
|
||||
Position1 += bytesRead;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
if (!BaseStream.CanSeek) throw new IOException("Stream is not seekable.");
|
||||
|
||||
long pos;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
pos = offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.Current:
|
||||
pos = Position1 + offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.End:
|
||||
pos = Length1 + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
public void WriteTo(Stream stream, int bufferSize)
|
||||
if (pos < 0 || pos >= Length1) throw new EndOfStreamException("OffsetStream reached begining/end of stream.");
|
||||
|
||||
Position1 = pos;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
if (ReadOnly) throw new IOException("OffsetStream is read only.");
|
||||
|
||||
BaseStream.SetLength(BaseStreamOffset + value);
|
||||
Length1 = value;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (ReadOnly) throw new IOException("OffsetStream is read only.");
|
||||
|
||||
if (count < 1) return;
|
||||
|
||||
var pos = Position1 + count;
|
||||
|
||||
if (pos > Length1) throw new EndOfStreamException("OffsetStream reached end of stream.");
|
||||
|
||||
if (BaseStream.CanSeek) BaseStream.Position = BaseStreamOffset + Position1;
|
||||
|
||||
BaseStream.Write(buffer, offset, count);
|
||||
Position1 = pos;
|
||||
}
|
||||
|
||||
public void Reset(long offset, long length, long position)
|
||||
{
|
||||
BaseStreamOffset = offset;
|
||||
Length1 = length;
|
||||
Position1 = position;
|
||||
}
|
||||
|
||||
public void WriteTo(Stream stream)
|
||||
{
|
||||
WriteTo(stream, BufferSize);
|
||||
}
|
||||
|
||||
public void WriteTo(Stream stream, int bufferSize)
|
||||
{
|
||||
if (!BaseStream.CanSeek) throw new IOException("Stream is not seekable.");
|
||||
|
||||
if (Length1 < bufferSize) bufferSize = Convert.ToInt32(Length1);
|
||||
|
||||
var previousPosition = Position1;
|
||||
Position1 = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (!BaseStream.CanSeek)
|
||||
{
|
||||
throw new IOException("Stream is not seekable.");
|
||||
}
|
||||
|
||||
if (Length1 < bufferSize)
|
||||
{
|
||||
bufferSize = Convert.ToInt32(Length1);
|
||||
}
|
||||
|
||||
long previousPosition = Position1;
|
||||
Position1 = 0;
|
||||
|
||||
try
|
||||
{
|
||||
CopyTo(stream, bufferSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Position1 = previousPosition;
|
||||
}
|
||||
CopyTo(stream, bufferSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Position1 = previousPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +1,77 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EonaCat.Logger.GrayLog
|
||||
namespace EonaCat.Logger.GrayLog;
|
||||
// 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.
|
||||
|
||||
/// <summary>
|
||||
/// Syslog server.
|
||||
/// </summary>
|
||||
public class GrayLogServer
|
||||
{
|
||||
// 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.
|
||||
internal readonly object SendLock = new();
|
||||
private string _Hostname = "127.0.0.1";
|
||||
private int _Port = 12201;
|
||||
internal UdpClient Udp;
|
||||
|
||||
/// <summary>
|
||||
/// Syslog server.
|
||||
/// Instantiate the object.
|
||||
/// </summary>
|
||||
public class GrayLogServer
|
||||
public GrayLogServer()
|
||||
{
|
||||
/// <summary>
|
||||
/// Hostname.
|
||||
/// </summary>
|
||||
public string Hostname
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiate the object.
|
||||
/// </summary>
|
||||
/// <param name="hostname">Hostname.</param>
|
||||
/// <param name="port">Port.</param>
|
||||
public GrayLogServer(string hostname = "127.0.0.1", int port = 12201)
|
||||
{
|
||||
Hostname = hostname;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hostname.
|
||||
/// </summary>
|
||||
public string Hostname
|
||||
{
|
||||
get => _Hostname;
|
||||
|
||||
set
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Hostname;
|
||||
}
|
||||
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
|
||||
_Hostname = value;
|
||||
|
||||
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 = 12201;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiate the object.
|
||||
/// </summary>
|
||||
public GrayLogServer()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiate the object.
|
||||
/// </summary>
|
||||
/// <param name="hostname">Hostname.</param>
|
||||
/// <param name="port">Port.</param>
|
||||
public GrayLogServer(string hostname = "127.0.0.1", int port = 12201)
|
||||
{
|
||||
Hostname = hostname;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
private void SetUdp()
|
||||
{
|
||||
Udp = null;
|
||||
Udp = new UdpClient(_Hostname, _Port);
|
||||
SetUdp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UDP port.
|
||||
/// </summary>
|
||||
public int Port
|
||||
{
|
||||
get => _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 => _Hostname + ":" + _Port;
|
||||
|
||||
private void SetUdp()
|
||||
{
|
||||
Udp = null;
|
||||
Udp = new UdpClient(_Hostname, _Port);
|
||||
}
|
||||
}
|
||||
@@ -2,134 +2,131 @@
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
|
||||
namespace EonaCat.Logger.Helpers
|
||||
namespace EonaCat.Logger.Helpers;
|
||||
|
||||
public static class ColorHelper
|
||||
{
|
||||
public static class ColorHelper
|
||||
public static string ColorToHexString(Color c)
|
||||
{
|
||||
public static string ColorToHexString(Color c)
|
||||
return "#" + c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2");
|
||||
}
|
||||
|
||||
public static string ColorToRGBString(Color c)
|
||||
{
|
||||
return "RGB(" + c.R + "," + c.G + "," + c.B + ")";
|
||||
}
|
||||
|
||||
public static Color ConsoleColorToColor(this ConsoleColor consoleColor)
|
||||
{
|
||||
switch (consoleColor)
|
||||
{
|
||||
return "#" + c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2");
|
||||
}
|
||||
case ConsoleColor.Black:
|
||||
return Color.Black;
|
||||
|
||||
public static string ColorToRGBString(Color c)
|
||||
{
|
||||
return "RGB(" + c.R.ToString() + "," + c.G.ToString() + "," + c.B.ToString() + ")";
|
||||
}
|
||||
case ConsoleColor.DarkBlue:
|
||||
return HexStringToColor("#000080");
|
||||
|
||||
public static Color ConsoleColorToColor(this ConsoleColor consoleColor)
|
||||
{
|
||||
switch (consoleColor)
|
||||
{
|
||||
case ConsoleColor.Black:
|
||||
return Color.Black;
|
||||
case ConsoleColor.DarkGreen:
|
||||
return HexStringToColor("#008000");
|
||||
|
||||
case ConsoleColor.DarkBlue:
|
||||
return HexStringToColor("#000080");
|
||||
case ConsoleColor.DarkCyan:
|
||||
return HexStringToColor("#008080");
|
||||
|
||||
case ConsoleColor.DarkGreen:
|
||||
return HexStringToColor("#008000");
|
||||
case ConsoleColor.DarkRed:
|
||||
return HexStringToColor("#800000");
|
||||
|
||||
case ConsoleColor.DarkCyan:
|
||||
return HexStringToColor("#008080");
|
||||
case ConsoleColor.DarkMagenta:
|
||||
return HexStringToColor("#800080");
|
||||
|
||||
case ConsoleColor.DarkRed:
|
||||
return HexStringToColor("#800000");
|
||||
case ConsoleColor.DarkYellow:
|
||||
return HexStringToColor("#808000");
|
||||
|
||||
case ConsoleColor.DarkMagenta:
|
||||
return HexStringToColor("#800080");
|
||||
case ConsoleColor.Gray:
|
||||
return HexStringToColor("#C0C0C0");
|
||||
|
||||
case ConsoleColor.DarkYellow:
|
||||
return HexStringToColor("#808000");
|
||||
case ConsoleColor.DarkGray:
|
||||
return HexStringToColor("#808080");
|
||||
|
||||
case ConsoleColor.Gray:
|
||||
return HexStringToColor("#C0C0C0");
|
||||
case ConsoleColor.Blue:
|
||||
return Color.Blue;
|
||||
|
||||
case ConsoleColor.DarkGray:
|
||||
return HexStringToColor("#808080");
|
||||
case ConsoleColor.Green:
|
||||
return Color.Lime;
|
||||
|
||||
case ConsoleColor.Blue:
|
||||
return Color.Blue;
|
||||
case ConsoleColor.Cyan:
|
||||
return Color.Cyan;
|
||||
|
||||
case ConsoleColor.Green:
|
||||
return Color.Lime;
|
||||
case ConsoleColor.Red:
|
||||
return Color.Red;
|
||||
|
||||
case ConsoleColor.Cyan:
|
||||
return Color.Cyan;
|
||||
case ConsoleColor.Magenta:
|
||||
return Color.Magenta;
|
||||
|
||||
case ConsoleColor.Red:
|
||||
return Color.Red;
|
||||
case ConsoleColor.Yellow:
|
||||
return Color.Yellow;
|
||||
|
||||
case ConsoleColor.Magenta:
|
||||
return Color.Magenta;
|
||||
case ConsoleColor.White:
|
||||
return Color.White;
|
||||
|
||||
case ConsoleColor.Yellow:
|
||||
return Color.Yellow;
|
||||
|
||||
case ConsoleColor.White:
|
||||
return Color.White;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static Color HexStringToColor(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF) => Color.FromArgb(HexColorToArgb(htmlColor, requireHexSpecified, defaultAlpha));
|
||||
|
||||
public static int HexColorToArgb(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF)
|
||||
{
|
||||
if (string.IsNullOrEmpty(htmlColor))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(htmlColor));
|
||||
}
|
||||
|
||||
if (!htmlColor.StartsWith("#") && requireHexSpecified)
|
||||
{
|
||||
throw new ArgumentException($"Provided parameter '{htmlColor}' is not valid");
|
||||
}
|
||||
|
||||
htmlColor = htmlColor.TrimStart('#');
|
||||
|
||||
|
||||
var symbolCount = htmlColor.Length;
|
||||
var value = int.Parse(htmlColor, NumberStyles.HexNumber);
|
||||
switch (symbolCount)
|
||||
{
|
||||
case 3: // RGB short hand
|
||||
{
|
||||
return defaultAlpha << 24
|
||||
| value & 0xF
|
||||
| (value & 0xF) << 4
|
||||
| (value & 0xF0) << 4
|
||||
| (value & 0xF0) << 8
|
||||
| (value & 0xF00) << 8
|
||||
| (value & 0xF00) << 12
|
||||
;
|
||||
}
|
||||
case 4: // RGBA short hand
|
||||
{
|
||||
// Inline alpha swap
|
||||
return (value & 0xF) << 24
|
||||
| (value & 0xF) << 28
|
||||
| (value & 0xF0) >> 4
|
||||
| value & 0xF0
|
||||
| value & 0xF00
|
||||
| (value & 0xF00) << 4
|
||||
| (value & 0xF000) << 4
|
||||
| (value & 0xF000) << 8
|
||||
;
|
||||
}
|
||||
case 6: // RGB complete definition
|
||||
{
|
||||
return defaultAlpha << 24 | value;
|
||||
}
|
||||
case 8: // RGBA complete definition
|
||||
{
|
||||
// Alpha swap
|
||||
return (value & 0xFF) << 24 | value >> 8;
|
||||
}
|
||||
default:
|
||||
throw new FormatException("Invalid HTML Color");
|
||||
}
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color HexStringToColor(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF)
|
||||
{
|
||||
return Color.FromArgb(HexColorToArgb(htmlColor, requireHexSpecified, defaultAlpha));
|
||||
}
|
||||
|
||||
public static int HexColorToArgb(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF)
|
||||
{
|
||||
if (string.IsNullOrEmpty(htmlColor)) throw new ArgumentNullException(nameof(htmlColor));
|
||||
|
||||
if (!htmlColor.StartsWith("#") && requireHexSpecified)
|
||||
throw new ArgumentException($"Provided parameter '{htmlColor}' is not valid");
|
||||
|
||||
htmlColor = htmlColor.TrimStart('#');
|
||||
|
||||
|
||||
var symbolCount = htmlColor.Length;
|
||||
var value = int.Parse(htmlColor, NumberStyles.HexNumber);
|
||||
switch (symbolCount)
|
||||
{
|
||||
case 3: // RGB short hand
|
||||
{
|
||||
return (defaultAlpha << 24)
|
||||
| (value & 0xF)
|
||||
| ((value & 0xF) << 4)
|
||||
| ((value & 0xF0) << 4)
|
||||
| ((value & 0xF0) << 8)
|
||||
| ((value & 0xF00) << 8)
|
||||
| ((value & 0xF00) << 12)
|
||||
;
|
||||
}
|
||||
case 4: // RGBA short hand
|
||||
{
|
||||
// Inline alpha swap
|
||||
return ((value & 0xF) << 24)
|
||||
| ((value & 0xF) << 28)
|
||||
| ((value & 0xF0) >> 4)
|
||||
| (value & 0xF0)
|
||||
| (value & 0xF00)
|
||||
| ((value & 0xF00) << 4)
|
||||
| ((value & 0xF000) << 4)
|
||||
| ((value & 0xF000) << 8)
|
||||
;
|
||||
}
|
||||
case 6: // RGB complete definition
|
||||
{
|
||||
return (defaultAlpha << 24) | value;
|
||||
}
|
||||
case 8: // RGBA complete definition
|
||||
{
|
||||
// Alpha swap
|
||||
return ((value & 0xFF) << 24) | (value >> 8);
|
||||
}
|
||||
default:
|
||||
throw new FormatException("Invalid HTML Color");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Logger.Helpers
|
||||
namespace EonaCat.Logger.Helpers;
|
||||
// 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.
|
||||
|
||||
internal static class EnumHelper<T>
|
||||
where T : struct
|
||||
{
|
||||
// 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.
|
||||
|
||||
internal static class EnumHelper<T>
|
||||
where T : struct
|
||||
static EnumHelper()
|
||||
{
|
||||
static EnumHelper()
|
||||
var names = Enum.GetNames(typeof(T));
|
||||
var values = (T[])Enum.GetValues(typeof(T));
|
||||
|
||||
Names = new Dictionary<T, string>(names.Length);
|
||||
Values = new Dictionary<string, T>(names.Length * 2);
|
||||
|
||||
for (var i = 0; i < names.Length; i++)
|
||||
{
|
||||
string[] names = Enum.GetNames(typeof(T));
|
||||
T[] values = (T[])Enum.GetValues(typeof(T));
|
||||
|
||||
Names = new Dictionary<T, string>(names.Length);
|
||||
Values = new Dictionary<string, T>(names.Length * 2);
|
||||
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
Names[values[i]] = names[i];
|
||||
Values[names[i]] = values[i];
|
||||
Values[names[i].ToLower()] = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<T, string> Names { get; }
|
||||
|
||||
public static Dictionary<string, T> Values { get; }
|
||||
|
||||
public static string ToString(T value)
|
||||
{
|
||||
return Names.TryGetValue(value, out string result) ? result : Convert.ToInt64(value).ToString();
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, bool ignoreCase, out T value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
return Values.TryGetValue(ignoreCase ? input.ToLower() : input, out value);
|
||||
}
|
||||
|
||||
internal static T Parse(string input, bool ignoreCase, T defaultValue)
|
||||
{
|
||||
return TryParse(input, ignoreCase, out T result) ? result : defaultValue;
|
||||
Names[values[i]] = names[i];
|
||||
Values[names[i]] = values[i];
|
||||
Values[names[i].ToLower()] = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<T, string> Names { get; }
|
||||
|
||||
public static Dictionary<string, T> Values { get; }
|
||||
|
||||
public static string ToString(T value)
|
||||
{
|
||||
return Names.TryGetValue(value, out var result) ? result : Convert.ToInt64(value).ToString();
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, bool ignoreCase, out T value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
return Values.TryGetValue(ignoreCase ? input.ToLower() : input, out value);
|
||||
}
|
||||
|
||||
internal static T Parse(string input, bool ignoreCase, T defaultValue)
|
||||
{
|
||||
return TryParse(input, ignoreCase, out var result) ? result : defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +1,73 @@
|
||||
using System;
|
||||
|
||||
namespace EonaCat.Logger.Managers
|
||||
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.
|
||||
|
||||
/// <summary>
|
||||
/// Colors to use when writing to the console.
|
||||
/// </summary>
|
||||
public class ColorSchema
|
||||
{
|
||||
// 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.
|
||||
/// <summary>
|
||||
/// The color to use for critical messages.
|
||||
/// </summary>
|
||||
public ColorScheme Critical = new(ConsoleColor.DarkRed, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// Colors to use when writing to the console.
|
||||
/// The color to use for debug messages.
|
||||
/// </summary>
|
||||
public class ColorSchema
|
||||
{
|
||||
/// <summary>
|
||||
/// The color to use for debug messages.
|
||||
/// </summary>
|
||||
public ColorScheme Debug = new ColorScheme(ConsoleColor.Green, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for informational messages.
|
||||
/// </summary>
|
||||
public ColorScheme Info = new ColorScheme(ConsoleColor.Blue, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for warning messages.
|
||||
/// </summary>
|
||||
public ColorScheme Warning = new ColorScheme(ConsoleColor.DarkYellow, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for error messages.
|
||||
/// </summary>
|
||||
public ColorScheme Error = new ColorScheme(ConsoleColor.Red, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for alert messages.
|
||||
/// </summary>
|
||||
public ColorScheme Traffic = new ColorScheme(ConsoleColor.DarkMagenta, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for critical messages.
|
||||
/// </summary>
|
||||
public ColorScheme Critical = new ColorScheme(ConsoleColor.DarkRed, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for emergency messages.
|
||||
/// </summary>
|
||||
public ColorScheme Trace = new ColorScheme(ConsoleColor.Cyan, ConsoleColor.Black);
|
||||
}
|
||||
public ColorScheme Debug = new(ConsoleColor.Green, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// Color scheme for logging messages.
|
||||
/// The color to use for error messages.
|
||||
/// </summary>
|
||||
public class ColorScheme
|
||||
public ColorScheme Error = new(ConsoleColor.Red, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for informational messages.
|
||||
/// </summary>
|
||||
public ColorScheme Info = new(ConsoleColor.Blue, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for emergency messages.
|
||||
/// </summary>
|
||||
public ColorScheme Trace = new(ConsoleColor.Cyan, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for alert messages.
|
||||
/// </summary>
|
||||
public ColorScheme Traffic = new(ConsoleColor.DarkMagenta, ConsoleColor.Black);
|
||||
|
||||
/// <summary>
|
||||
/// The color to use for warning messages.
|
||||
/// </summary>
|
||||
public ColorScheme Warning = new(ConsoleColor.DarkYellow, ConsoleColor.Black);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color scheme for logging messages.
|
||||
/// </summary>
|
||||
public class ColorScheme
|
||||
{
|
||||
/// <summary>
|
||||
/// Background color.
|
||||
/// </summary>
|
||||
public ConsoleColor Background = Console.BackgroundColor;
|
||||
|
||||
/// <summary>
|
||||
/// Foreground color.
|
||||
/// </summary>
|
||||
public ConsoleColor Foreground = Console.ForegroundColor;
|
||||
|
||||
/// <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)
|
||||
{
|
||||
/// <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;
|
||||
}
|
||||
Foreground = foreground;
|
||||
Background = background;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EonaCat.Logger.Managers
|
||||
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 interface ILogManager
|
||||
{
|
||||
// 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.
|
||||
void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null,
|
||||
bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null,
|
||||
bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null,
|
||||
string grayLogVersion = "1.1");
|
||||
|
||||
public interface ILogManager
|
||||
{
|
||||
void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1");
|
||||
|
||||
void Write(Exception exception, string module = null, string method = null, bool criticalException = false,
|
||||
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1");
|
||||
}
|
||||
void Write(Exception exception, string module = null, string method = null, bool criticalException = false,
|
||||
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null,
|
||||
string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
|
||||
string grayLogSource = null, string grayLogVersion = "1.1");
|
||||
}
|
||||
@@ -13,215 +13,251 @@ using Microsoft.Extensions.Logging;
|
||||
// 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.
|
||||
|
||||
namespace EonaCat.Logger.Managers
|
||||
namespace EonaCat.Logger.Managers;
|
||||
|
||||
public class ErrorMessage
|
||||
{
|
||||
public class ErrorMessage
|
||||
public Exception Exception { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
internal static class LogHelper
|
||||
{
|
||||
internal static event EventHandler<ErrorMessage> OnException;
|
||||
|
||||
internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage,
|
||||
DateTime dateTime)
|
||||
{
|
||||
public Exception Exception { get; set; }
|
||||
public string Message { get; set; }
|
||||
if (string.IsNullOrWhiteSpace(currentMessage))
|
||||
return currentMessage;
|
||||
|
||||
var sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]");
|
||||
|
||||
sb.Replace("{ts}",
|
||||
dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " +
|
||||
(settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]"))
|
||||
.Replace("{host}", $"[Host:{Dns.GetHostName()}]")
|
||||
.Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]")
|
||||
.Replace("{sev}", $"[{logType}]");
|
||||
|
||||
if (!settings?.RemoveMessagePrefix ?? (false && !currentMessage.Contains("[EonaCatLogger]")))
|
||||
sb.Insert(0, "[EonaCatLogger] ");
|
||||
|
||||
sb.Append(" ").Append(currentMessage);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal static class LogHelper
|
||||
internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message, bool writeToConsole)
|
||||
{
|
||||
internal static event EventHandler<ErrorMessage> OnException;
|
||||
if (settings == null || !writeToConsole || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage, DateTime dateTime)
|
||||
if (settings.EnableColors && settings.Colors != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(currentMessage))
|
||||
return currentMessage;
|
||||
var prevForeground = Console.ForegroundColor;
|
||||
var prevBackground = Console.BackgroundColor;
|
||||
|
||||
StringBuilder sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]");
|
||||
|
||||
sb.Replace("{ts}", dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " + (settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]"))
|
||||
.Replace("{host}", $"[Host:{Dns.GetHostName()}]")
|
||||
.Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]")
|
||||
.Replace("{sev}", $"[{logType}]");
|
||||
|
||||
if (!settings?.RemoveMessagePrefix ?? false && !currentMessage.Contains("[EonaCatLogger]"))
|
||||
sb.Insert(0, "[EonaCatLogger] ");
|
||||
|
||||
sb.Append(" ").Append(currentMessage);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message, bool writeToConsole)
|
||||
{
|
||||
if (settings == null || !writeToConsole || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
if (settings.EnableColors && settings.Colors != null)
|
||||
ConsoleColor foregroundColor;
|
||||
ConsoleColor backgroundColor;
|
||||
switch (logType)
|
||||
{
|
||||
ConsoleColor prevForeground = Console.ForegroundColor;
|
||||
ConsoleColor prevBackground = Console.BackgroundColor;
|
||||
case ELogType.DEBUG:
|
||||
foregroundColor = settings.Colors.Debug.Foreground;
|
||||
backgroundColor = settings.Colors.Debug.Background;
|
||||
break;
|
||||
case ELogType.INFO:
|
||||
foregroundColor = settings.Colors.Info.Foreground;
|
||||
backgroundColor = settings.Colors.Info.Background;
|
||||
break;
|
||||
case ELogType.WARNING:
|
||||
foregroundColor = settings.Colors.Warning.Foreground;
|
||||
backgroundColor = settings.Colors.Warning.Background;
|
||||
break;
|
||||
case ELogType.ERROR:
|
||||
foregroundColor = settings.Colors.Error.Foreground;
|
||||
backgroundColor = settings.Colors.Error.Background;
|
||||
break;
|
||||
case ELogType.TRAFFIC:
|
||||
foregroundColor = settings.Colors.Traffic.Foreground;
|
||||
backgroundColor = settings.Colors.Traffic.Background;
|
||||
break;
|
||||
case ELogType.CRITICAL:
|
||||
foregroundColor = settings.Colors.Critical.Foreground;
|
||||
backgroundColor = settings.Colors.Critical.Background;
|
||||
break;
|
||||
case ELogType.TRACE:
|
||||
foregroundColor = settings.Colors.Trace.Foreground;
|
||||
backgroundColor = settings.Colors.Trace.Background;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
ConsoleColor foregroundColor;
|
||||
ConsoleColor backgroundColor;
|
||||
switch (logType)
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
Console.BackgroundColor = backgroundColor;
|
||||
Console.WriteLine(message);
|
||||
Console.ForegroundColor = prevForeground;
|
||||
Console.BackgroundColor = prevBackground;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message)
|
||||
{
|
||||
if (logger == null || settings == null || !settings.EnableFileLogging ||
|
||||
string.IsNullOrWhiteSpace(message)) return;
|
||||
|
||||
var logLevel = logType.ToLogLevel();
|
||||
if (IsLogLevelEnabled(settings, logLevel)) Log(logger, logLevel, message);
|
||||
}
|
||||
|
||||
public static bool IsLogLevelEnabled(LoggerSettings settings, LogLevel logLevel)
|
||||
{
|
||||
return settings.MinLogType.ToLogLevel() <= logLevel;
|
||||
}
|
||||
|
||||
private static void Log(ILogger logger, LogLevel logLevel, string message)
|
||||
{
|
||||
logger.Log(logLevel, message);
|
||||
}
|
||||
|
||||
public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload,
|
||||
bool sendToSplunkServer)
|
||||
{
|
||||
if (settings == null || !sendToSplunkServer || splunkPayload == null)
|
||||
return;
|
||||
|
||||
if (settings.SplunkServers == null) settings.SplunkServers = new List<SplunkServer.SplunkServer>();
|
||||
|
||||
foreach (var splunkServer in settings.SplunkServers)
|
||||
{
|
||||
if (!splunkServer.HasHecUrl || !splunkServer.HasHecToken)
|
||||
{
|
||||
OnException?.Invoke(null,
|
||||
new ErrorMessage
|
||||
{
|
||||
Message =
|
||||
$"Splunk server HecUrl or HecToken not specified, skipping splunkServer '{splunkServer.SplunkHecUrl}'"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await splunkServer.SendAsync(splunkPayload);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
OnException?.Invoke(null,
|
||||
new ErrorMessage
|
||||
{
|
||||
Message =
|
||||
$"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}"
|
||||
});
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
OnException?.Invoke(null,
|
||||
new ErrorMessage
|
||||
{
|
||||
Exception = exception,
|
||||
Message =
|
||||
$"Error while logging to Splunk Server '{splunkServer.SplunkHecUrl}': {exception.Message}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message,
|
||||
bool sendToSplunkServer)
|
||||
{
|
||||
if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
var splunkPayload = new SplunkPayload
|
||||
{
|
||||
Host = Environment.MachineName,
|
||||
EventData = message,
|
||||
SourceType = logType
|
||||
};
|
||||
|
||||
await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer);
|
||||
}
|
||||
|
||||
internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel,
|
||||
string facility, string source, bool sendToGrayLogServer, string version = "1.1")
|
||||
{
|
||||
if (settings == null || !sendToGrayLogServer || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
foreach (var grayLogServer in settings.GrayLogServers ?? new List<GrayLogServer> { new("127.0.0.1") })
|
||||
try
|
||||
{
|
||||
var gelfMessage = new
|
||||
{
|
||||
case ELogType.DEBUG:
|
||||
foregroundColor = settings.Colors.Debug.Foreground;
|
||||
backgroundColor = settings.Colors.Debug.Background;
|
||||
break;
|
||||
case ELogType.INFO:
|
||||
foregroundColor = settings.Colors.Info.Foreground;
|
||||
backgroundColor = settings.Colors.Info.Background;
|
||||
break;
|
||||
case ELogType.WARNING:
|
||||
foregroundColor = settings.Colors.Warning.Foreground;
|
||||
backgroundColor = settings.Colors.Warning.Background;
|
||||
break;
|
||||
case ELogType.ERROR:
|
||||
foregroundColor = settings.Colors.Error.Foreground;
|
||||
backgroundColor = settings.Colors.Error.Background;
|
||||
break;
|
||||
case ELogType.TRAFFIC:
|
||||
foregroundColor = settings.Colors.Traffic.Foreground;
|
||||
backgroundColor = settings.Colors.Traffic.Background;
|
||||
break;
|
||||
case ELogType.CRITICAL:
|
||||
foregroundColor = settings.Colors.Critical.Foreground;
|
||||
backgroundColor = settings.Colors.Critical.Background;
|
||||
break;
|
||||
case ELogType.TRACE:
|
||||
foregroundColor = settings.Colors.Trace.Foreground;
|
||||
backgroundColor = settings.Colors.Trace.Background;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
version,
|
||||
host = Environment.MachineName,
|
||||
short_message = message,
|
||||
level = logLevel.ToGrayLogLevel(),
|
||||
facility,
|
||||
source,
|
||||
timestamp = DateTime.UtcNow.ToUnixTimestamp()
|
||||
};
|
||||
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
Console.BackgroundColor = backgroundColor;
|
||||
Console.WriteLine(message);
|
||||
Console.ForegroundColor = prevForeground;
|
||||
Console.BackgroundColor = prevBackground;
|
||||
var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage));
|
||||
await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length,
|
||||
new IPEndPoint(IPAddress.Parse(grayLogServer.Hostname), grayLogServer.Port));
|
||||
}
|
||||
else
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
OnException?.Invoke(null,
|
||||
new ErrorMessage
|
||||
{
|
||||
Exception = exception,
|
||||
Message =
|
||||
$"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message)
|
||||
{
|
||||
if (logger == null || settings == null || !settings.EnableFileLogging || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message,
|
||||
bool sendToSyslogServers)
|
||||
{
|
||||
if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
LogLevel logLevel = logType.ToLogLevel();
|
||||
|
||||
if (logLevel >= settings.MaxLogType.ToLogLevel())
|
||||
await Task.Run(() => logger.Log(logLevel, message));
|
||||
}
|
||||
|
||||
public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, bool sendToSplunkServer)
|
||||
{
|
||||
if (settings == null || !sendToSplunkServer || splunkPayload == null)
|
||||
return;
|
||||
|
||||
if (settings.SplunkServers == null)
|
||||
foreach (var server in settings.SysLogServers ?? new List<SyslogServer> { new("127.0.0.1") })
|
||||
try
|
||||
{
|
||||
settings.SplunkServers = new List<SplunkServer.SplunkServer>();
|
||||
}
|
||||
|
||||
foreach (var splunkServer in settings.SplunkServers)
|
||||
{
|
||||
if (!splunkServer.HasHecUrl || !splunkServer.HasHecToken)
|
||||
if (string.IsNullOrWhiteSpace(server.Hostname))
|
||||
{
|
||||
OnException?.Invoke(null, new ErrorMessage { Message = $"Splunk server HecUrl or HecToken not specified, skipping splunkServer '{splunkServer.SplunkHecUrl}'" });
|
||||
OnException?.Invoke(null,
|
||||
new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" });
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
if (server.Port < 0)
|
||||
{
|
||||
var response = await splunkServer.SendAsync(splunkPayload);
|
||||
OnException?.Invoke(null,
|
||||
new ErrorMessage { Message = "Server port must be zero or greater, skipping SysLog Server" });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
OnException?.Invoke(null, new ErrorMessage { Message = $"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}" });
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to Splunk Server '{splunkServer.SplunkHecUrl}': {exception.Message}" });
|
||||
}
|
||||
var data = Encoding.UTF8.GetBytes(message);
|
||||
await server.Udp.SendAsync(data, data.Length,
|
||||
new IPEndPoint(IPAddress.Parse(server.Hostname), server.Port));
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message, bool sendToSplunkServer)
|
||||
{
|
||||
if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
var splunkPayload = new SplunkPayload
|
||||
catch (Exception exception)
|
||||
{
|
||||
Host = Environment.MachineName,
|
||||
EventData = message,
|
||||
SourceType = logType
|
||||
};
|
||||
|
||||
await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer);
|
||||
}
|
||||
|
||||
internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel, string facility, string source, bool sendToGrayLogServer, string version = "1.1")
|
||||
{
|
||||
if (settings == null || !sendToGrayLogServer || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
foreach (var grayLogServer in settings.GrayLogServers ?? new List<GrayLogServer> { new GrayLogServer("127.0.0.1", 12201) })
|
||||
{
|
||||
try
|
||||
{
|
||||
var gelfMessage = new
|
||||
OnException?.Invoke(null,
|
||||
new ErrorMessage
|
||||
{
|
||||
version,
|
||||
host = Environment.MachineName,
|
||||
short_message = message,
|
||||
level = logLevel.ToGrayLogLevel(),
|
||||
facility,
|
||||
source,
|
||||
timestamp = DateTime.UtcNow.ToUnixTimestamp(),
|
||||
};
|
||||
|
||||
var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage));
|
||||
await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length, new IPEndPoint(IPAddress.Parse(grayLogServer.Hostname), grayLogServer.Port));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}" });
|
||||
}
|
||||
Exception = exception,
|
||||
Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message, bool sendToSyslogServers)
|
||||
{
|
||||
if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
foreach (var server in settings.SysLogServers ?? new List<SyslogServer> { new SyslogServer("127.0.0.1", 514) })
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(server.Hostname))
|
||||
{
|
||||
OnException?.Invoke(null, new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (server.Port < 0)
|
||||
{
|
||||
OnException?.Invoke(null, new ErrorMessage { Message = "Server port must be zero or greater, skipping SysLog Server" });
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] data = Encoding.UTF8.GetBytes(message);
|
||||
await server.Udp.SendAsync(data, data.Length, new IPEndPoint(IPAddress.Parse(server.Hostname), server.Port));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
using EonaCat.Logger.Syslog;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
@@ -10,86 +7,139 @@ using EonaCat.Logger.EonaCatCoreLogger;
|
||||
using EonaCat.Logger.EonaCatCoreLogger.Extensions;
|
||||
using EonaCat.Logger.EonaCatCoreLogger.Models;
|
||||
using EonaCat.Logger.Extensions;
|
||||
using EonaCat.Logger.Syslog;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace EonaCat.Logger.Managers
|
||||
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 : ILogManager, IDisposable
|
||||
{
|
||||
// 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.
|
||||
private static LogManager _instance;
|
||||
|
||||
public partial class LogManager : ILogManager, IDisposable
|
||||
private readonly CancellationTokenSource _tokenSource = new();
|
||||
private DateTime _logDate;
|
||||
|
||||
public LogManager(LoggerSettings settings, string serverIp, int serverPort)
|
||||
{
|
||||
public event EventHandler<ErrorMessage> OnException;
|
||||
private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
private DateTime _logDate;
|
||||
public ILoggerProvider LoggerProvider { get; private set; }
|
||||
public ILoggerFactory LoggerFactory { get; private set; }
|
||||
public ILogger Logger { get; private set; }
|
||||
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider ? fileLoggerProvider.LogFile : string.Empty;
|
||||
if (string.IsNullOrEmpty(serverIp))
|
||||
throw new ArgumentNullException(nameof(serverIp));
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
private static LogManager _instance;
|
||||
if (serverPort < 0)
|
||||
throw new ArgumentException("Server port must be zero or greater.");
|
||||
|
||||
private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
|
||||
|
||||
public static LogManager Instance => InstanceInit();
|
||||
|
||||
public LoggerSettings Settings { get; set; } = CreateDefaultSettings();
|
||||
|
||||
private static LogManager InstanceInit()
|
||||
settings.SysLogServers = new List<SyslogServer>
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new LogManager(CreateDefaultSettings());
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
new(serverIp, serverPort)
|
||||
};
|
||||
|
||||
private static LoggerSettings CreateDefaultSettings()
|
||||
{
|
||||
var settings = new LoggerSettings();
|
||||
settings.Id = "EonaCatLogger";
|
||||
settings.MaxLogType = ELogType.TRACE;
|
||||
return settings;
|
||||
}
|
||||
Settings = settings;
|
||||
SetupLogManager();
|
||||
}
|
||||
|
||||
protected virtual async Task DisposeAsync(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
await StopLoggingAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
public LogManager(LoggerSettings settings)
|
||||
{
|
||||
Settings = settings;
|
||||
SetupFileLogger(settings);
|
||||
SetupLogManager();
|
||||
LogHelper.OnException += LogHelper_OnException;
|
||||
}
|
||||
|
||||
public async Task StartNewLogAsync()
|
||||
{
|
||||
if (_tokenSource.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
public ILoggerProvider LoggerProvider { get; private set; }
|
||||
public ILoggerFactory LoggerFactory { get; private set; }
|
||||
public ILogger Logger { get; private set; }
|
||||
|
||||
if (IsRunning && CurrentDateTme.Date > _logDate.Date)
|
||||
{
|
||||
await StopLoggingAsync().ConfigureAwait(false);
|
||||
}
|
||||
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider
|
||||
? fileLoggerProvider.LogFile
|
||||
: string.Empty;
|
||||
|
||||
IsRunning = true;
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
CreateLogger();
|
||||
public static LogManager Instance => InstanceInit();
|
||||
|
||||
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
|
||||
public LoggerSettings Settings { get; set; } = CreateDefaultSettings();
|
||||
|
||||
_logDate = CurrentDateTme;
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeAsync(true).GetAwaiter().GetResult();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void CreateLogger()
|
||||
{
|
||||
// Dispose of previous ServiceProvider if it exists
|
||||
(LoggerProvider as IDisposable)?.Dispose();
|
||||
(LoggerFactory as IDisposable)?.Dispose();
|
||||
public void Write(Exception exception, string module = null, string method = null, bool criticalException = false,
|
||||
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null,
|
||||
string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
|
||||
string grayLogSource = null, string grayLogVersion = "1.1")
|
||||
{
|
||||
if (exception == null)
|
||||
return;
|
||||
|
||||
IServiceCollection serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MaxLogType.ToLogLevel()).AddEonaCatFileLogger(configuration =>
|
||||
Write(exception.FormatExceptionToMessage(module, method),
|
||||
criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers,
|
||||
sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource,
|
||||
grayLogVersion);
|
||||
}
|
||||
|
||||
public void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null,
|
||||
bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null,
|
||||
bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null,
|
||||
string grayLogVersion = "1.1")
|
||||
{
|
||||
if (logType == ELogType.NONE)
|
||||
return;
|
||||
|
||||
if (!IsRunning) StartNewLogAsync().ConfigureAwait(false);
|
||||
InternalWriteAsync(CurrentDateTme, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers,
|
||||
customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion);
|
||||
}
|
||||
|
||||
public event EventHandler<ErrorMessage> OnException;
|
||||
|
||||
private static LogManager InstanceInit()
|
||||
{
|
||||
if (_instance == null) _instance = new LogManager(CreateDefaultSettings());
|
||||
return _instance;
|
||||
}
|
||||
|
||||
private static LoggerSettings CreateDefaultSettings()
|
||||
{
|
||||
var settings = new LoggerSettings();
|
||||
settings.Id = "EonaCatLogger";
|
||||
settings.MinLogType = ELogType.INFO;
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected virtual async Task DisposeAsync(bool disposing)
|
||||
{
|
||||
if (disposing) await StopLoggingAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task StartNewLogAsync()
|
||||
{
|
||||
if (_tokenSource.IsCancellationRequested) return;
|
||||
|
||||
if (IsRunning && CurrentDateTme.Date > _logDate.Date) await StopLoggingAsync().ConfigureAwait(false);
|
||||
|
||||
IsRunning = true;
|
||||
|
||||
CreateLogger();
|
||||
|
||||
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
|
||||
|
||||
_logDate = CurrentDateTme;
|
||||
}
|
||||
|
||||
private void CreateLogger()
|
||||
{
|
||||
// Dispose of previous ServiceProvider if it exists
|
||||
LoggerProvider?.Dispose();
|
||||
LoggerFactory?.Dispose();
|
||||
|
||||
IServiceCollection serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MinLogType.ToLogLevel())
|
||||
.AddEonaCatFileLogger(configuration =>
|
||||
{
|
||||
var fileLoggerOptions = Settings.FileLoggerOptions;
|
||||
configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries;
|
||||
@@ -104,166 +154,108 @@ namespace EonaCat.Logger.Managers
|
||||
configuration.UseLocalTime = Settings.UseLocalTime;
|
||||
}));
|
||||
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
LoggerProvider = serviceProvider.GetService<ILoggerProvider>();
|
||||
LoggerFactory = serviceProvider.GetService<ILoggerFactory>();
|
||||
Logger = LoggerFactory.CreateLogger(Settings.Id);
|
||||
|
||||
// Dispose of the current ServiceProvider
|
||||
(serviceProvider as IDisposable)?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null, string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1")
|
||||
{
|
||||
if (string.IsNullOrEmpty(message) || logType == ELogType.NONE || (int)logType >= (int)Settings.MaxLogType)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime);
|
||||
bool writeToConsoleValue = writeToConsole ?? Settings.EnableConsole;
|
||||
bool sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers;
|
||||
bool sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers;
|
||||
bool sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers;
|
||||
|
||||
if (writeToConsoleValue)
|
||||
{
|
||||
LogHelper.SendToConsole(Settings, logType, messageWithHeader, true);
|
||||
}
|
||||
|
||||
if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
if (sendToSyslogServersValue)
|
||||
{
|
||||
await LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true);
|
||||
}
|
||||
|
||||
if (sendToSplunkServersValue)
|
||||
{
|
||||
await LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(), messageWithHeader, true);
|
||||
}
|
||||
|
||||
if (sendToGrayLogServersValue)
|
||||
{
|
||||
await LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility, grayLogSource, true, grayLogVersion);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var logMessage = new EonaCatLogMessage
|
||||
{
|
||||
DateTime = dateTime,
|
||||
Message = message,
|
||||
LogType = logType,
|
||||
Origin = string.IsNullOrWhiteSpace(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin
|
||||
};
|
||||
|
||||
Settings.OnLogEvent(logMessage);
|
||||
}
|
||||
|
||||
public void Reset() => Settings.ResetLogEvent();
|
||||
|
||||
public LogManager(LoggerSettings settings, string serverIp, int serverPort)
|
||||
{
|
||||
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>
|
||||
{
|
||||
new SyslogServer(serverIp, serverPort)
|
||||
};
|
||||
|
||||
Settings = settings;
|
||||
SetupLogManager();
|
||||
}
|
||||
|
||||
public LogManager(LoggerSettings settings)
|
||||
{
|
||||
Settings = settings;
|
||||
SetupFileLogger(settings, null, true);
|
||||
SetupLogManager();
|
||||
LogHelper.OnException += LogHelper_OnException;
|
||||
}
|
||||
|
||||
private void LogHelper_OnException(object sender, ErrorMessage e)
|
||||
{
|
||||
OnException?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true)
|
||||
{
|
||||
if (settings == null)
|
||||
settings = Settings;
|
||||
|
||||
if (!settings.EnableFileLogging)
|
||||
return;
|
||||
|
||||
if (logFolder != null)
|
||||
settings.FileLoggerOptions.LogDirectory = logFolder;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix))
|
||||
settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty;
|
||||
}
|
||||
|
||||
private void SetupLogManager()
|
||||
{
|
||||
AppDomain.CurrentDomain.ProcessExit += ProcessExit;
|
||||
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||
_logDate = CurrentDateTme;
|
||||
}
|
||||
|
||||
private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void ProcessExit(object sender, EventArgs e)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private async Task<Task> StopLoggingAsync()
|
||||
{
|
||||
IsRunning = false;
|
||||
await InternalWriteAsync(CurrentDateTme, $"{DllInfo.ApplicationName} stopped.").ConfigureAwait(false);
|
||||
return Task.Delay(500);
|
||||
}
|
||||
|
||||
public void DeleteCurrentLogFile()
|
||||
{
|
||||
if (CurrentLogFile != null)
|
||||
File.Delete(CurrentLogFile);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeAsync(true).GetAwaiter().GetResult();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Write(Exception exception, string module = null, string method = null, bool criticalException = false, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1")
|
||||
{
|
||||
if (exception == null)
|
||||
return;
|
||||
|
||||
Write(exception.FormatExceptionToMessage(module, method), criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion);
|
||||
}
|
||||
|
||||
public void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1")
|
||||
{
|
||||
if (logType == ELogType.NONE)
|
||||
return;
|
||||
|
||||
if (!IsRunning)
|
||||
StartNewLogAsync().ConfigureAwait(false);
|
||||
|
||||
InternalWriteAsync(CurrentDateTme, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion);
|
||||
}
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
LoggerProvider = serviceProvider.GetService<ILoggerProvider>();
|
||||
LoggerFactory = serviceProvider.GetService<ILoggerFactory>();
|
||||
Logger = LoggerFactory.CreateLogger(Settings.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO,
|
||||
bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null,
|
||||
string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
|
||||
string grayLogSource = null, string grayLogVersion = "1.1")
|
||||
{
|
||||
if (string.IsNullOrEmpty(message) || logType == ELogType.NONE ||
|
||||
(int)logType < (int)Settings.MinLogType) return;
|
||||
|
||||
var messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime);
|
||||
var writeToConsoleValue = writeToConsole ?? Settings.EnableConsole;
|
||||
var sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers;
|
||||
var sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers;
|
||||
var sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers;
|
||||
|
||||
LogHelper.SendToFile(Logger, Settings, logType, message);
|
||||
|
||||
if (writeToConsoleValue) LogHelper.SendToConsole(Settings, logType, messageWithHeader, true);
|
||||
|
||||
if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue)
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
if (sendToSyslogServersValue)
|
||||
await LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true);
|
||||
|
||||
if (sendToSplunkServersValue)
|
||||
await LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(),
|
||||
messageWithHeader, true);
|
||||
|
||||
if (sendToGrayLogServersValue)
|
||||
await LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility,
|
||||
grayLogSource, true, grayLogVersion);
|
||||
});
|
||||
|
||||
var logMessage = new EonaCatLogMessage
|
||||
{
|
||||
DateTime = dateTime,
|
||||
Message = message,
|
||||
LogType = logType,
|
||||
Origin = string.IsNullOrWhiteSpace(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin
|
||||
};
|
||||
|
||||
Settings.OnLogEvent(logMessage);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Settings.ResetLogEvent();
|
||||
}
|
||||
|
||||
private void LogHelper_OnException(object sender, ErrorMessage e)
|
||||
{
|
||||
OnException?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true)
|
||||
{
|
||||
if (settings == null)
|
||||
settings = Settings;
|
||||
|
||||
if (!settings.EnableFileLogging)
|
||||
return;
|
||||
|
||||
if (logFolder != null)
|
||||
settings.FileLoggerOptions.LogDirectory = logFolder;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix))
|
||||
settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty;
|
||||
}
|
||||
|
||||
private void SetupLogManager()
|
||||
{
|
||||
AppDomain.CurrentDomain.ProcessExit += ProcessExit;
|
||||
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||
_logDate = CurrentDateTme;
|
||||
}
|
||||
|
||||
private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void ProcessExit(object sender, EventArgs e)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private Task StopLoggingAsync()
|
||||
{
|
||||
IsRunning = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void DeleteCurrentLogFile()
|
||||
{
|
||||
if (CurrentLogFile != null)
|
||||
File.Delete(CurrentLogFile);
|
||||
}
|
||||
}
|
||||
@@ -5,192 +5,172 @@ using EonaCat.Logger.EonaCatCoreLogger.Models;
|
||||
using EonaCat.Logger.GrayLog;
|
||||
using EonaCat.Logger.Syslog;
|
||||
|
||||
namespace EonaCat.Logger.Managers
|
||||
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.
|
||||
|
||||
/// <summary>
|
||||
/// Logger settings.
|
||||
/// </summary>
|
||||
public class LoggerSettings
|
||||
{
|
||||
// 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 delegate void LogDelegate(EonaCatLogMessage message);
|
||||
|
||||
private ColorSchema _colors = new();
|
||||
private bool _enableConsole = true;
|
||||
|
||||
private FileLoggerOptions _fileLoggerOptions;
|
||||
|
||||
private string _headerFormat = "{ts} {host} {thread} {sev}";
|
||||
private int _maxMessageLength = 1024;
|
||||
private string _timestampFormat = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
/// <summary>
|
||||
/// Logger settings.
|
||||
/// Determines if we need to use the local time in the logging or UTC (default:false)
|
||||
/// </summary>
|
||||
public class LoggerSettings
|
||||
public bool UseLocalTime { get; set; }
|
||||
|
||||
public string Id { get; set; } = "EonaCatLogger";
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we need to remove the prefix [EonaCatLogger] from each message (default: false)
|
||||
/// </summary>
|
||||
public bool RemoveMessagePrefix { get; set; }
|
||||
|
||||
|
||||
/// <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
|
||||
{
|
||||
public event LogDelegate OnLog;
|
||||
public delegate void LogDelegate(EonaCatLogMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we need to use the local time in the logging or UTC (default:false)
|
||||
/// </summary>
|
||||
public bool UseLocalTime { get; set; }
|
||||
|
||||
public string Id { get; set; } = "EonaCatLogger";
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we need to remove the prefix [EonaCatLogger] from each message (default: false)
|
||||
/// </summary>
|
||||
public bool RemoveMessagePrefix { get; set; }
|
||||
|
||||
|
||||
/// <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 => _headerFormat;
|
||||
set
|
||||
{
|
||||
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>
|
||||
/// 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 ELogType MaxLogType { get; set; } = ELogType.TRACE;
|
||||
|
||||
public bool SendToSyslogServers { get; set; }
|
||||
|
||||
public List<SyslogServer> SysLogServers { get; set; }
|
||||
|
||||
public bool SendToSplunkServers { get; set; }
|
||||
|
||||
public bool SendToGrayLogServers { get; set; }
|
||||
|
||||
public List<SplunkServer.SplunkServer> SplunkServers { get; set; }
|
||||
|
||||
public List<GrayLogServer> GrayLogServers { get; set; }
|
||||
|
||||
/// <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 static FileLoggerOptions CreateDefaultFileLoggerOptions()
|
||||
{
|
||||
return new FileLoggerOptions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the origin of where the OnLog event was initiated
|
||||
/// </summary>
|
||||
public string LogOrigin { get; set; }
|
||||
|
||||
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 static bool ConsoleExists()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool test1 = Environment.UserInteractive;
|
||||
bool test2 = Console.WindowHeight > 0;
|
||||
return test1 && test2;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnLogEvent(EonaCatLogMessage eonaCatLogMessage)
|
||||
{
|
||||
OnLog?.Invoke(eonaCatLogMessage);
|
||||
}
|
||||
|
||||
internal void ResetLogEvent()
|
||||
{
|
||||
OnLog = null;
|
||||
if (string.IsNullOrEmpty(value)) _headerFormat = "";
|
||||
else _headerFormat = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp format.
|
||||
/// </summary>
|
||||
public string TimestampFormat
|
||||
{
|
||||
get => _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 => _enableConsole;
|
||||
set
|
||||
{
|
||||
if (value) _enableConsole = ConsoleExists();
|
||||
else _enableConsole = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 => _colors;
|
||||
set
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException(nameof(Colors));
|
||||
_colors = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ELogType MinLogType { get; set; } = ELogType.INFO;
|
||||
|
||||
public bool SendToSyslogServers { get; set; }
|
||||
|
||||
public List<SyslogServer> SysLogServers { get; set; }
|
||||
|
||||
public bool SendToSplunkServers { get; set; }
|
||||
|
||||
public bool SendToGrayLogServers { get; set; }
|
||||
|
||||
public List<SplunkServer.SplunkServer> SplunkServers { get; set; }
|
||||
|
||||
public List<GrayLogServer> GrayLogServers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the fileLogging is enabled
|
||||
/// </summary>
|
||||
public bool EnableFileLogging { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// FileLogger settings.
|
||||
/// </summary>
|
||||
public FileLoggerOptions FileLoggerOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_fileLoggerOptions == null) _fileLoggerOptions = CreateDefaultFileLoggerOptions();
|
||||
return _fileLoggerOptions;
|
||||
}
|
||||
|
||||
set => _fileLoggerOptions = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the origin of where the OnLog event was initiated
|
||||
/// </summary>
|
||||
public string LogOrigin { get; set; }
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
namespace EonaCat.Logger.Splunk.Models;
|
||||
|
||||
namespace EonaCat.Logger.Splunk.Models
|
||||
public class SplunkPayload
|
||||
{
|
||||
public class SplunkPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Event data for splunk
|
||||
/// </summary>
|
||||
public string EventData { get; set; }
|
||||
/// <summary>
|
||||
/// Event data for splunk
|
||||
/// </summary>
|
||||
public string EventData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SourceType for splunk
|
||||
/// </summary>
|
||||
public string SourceType { get; set; }
|
||||
/// <summary>
|
||||
/// SourceType for splunk
|
||||
/// </summary>
|
||||
public string SourceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Host for splunk
|
||||
/// </summary>
|
||||
public string Host { get; set; }
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Host for splunk
|
||||
/// </summary>
|
||||
public string Host { get; set; }
|
||||
}
|
||||
@@ -1,154 +1,129 @@
|
||||
using EonaCat.Json;
|
||||
using EonaCat.Logger.Splunk.Models;
|
||||
using System;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EonaCat.Json;
|
||||
using EonaCat.Logger.Splunk.Models;
|
||||
|
||||
namespace EonaCat.Logger.SplunkServer
|
||||
namespace EonaCat.Logger.SplunkServer;
|
||||
// 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.
|
||||
|
||||
/// <summary>
|
||||
/// Splunk Server.
|
||||
/// </summary>
|
||||
public class SplunkServer
|
||||
{
|
||||
// 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.
|
||||
internal readonly object SendLock = new();
|
||||
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Splunk Server.
|
||||
/// Instantiate the object.
|
||||
/// </summary>
|
||||
public class SplunkServer
|
||||
/// <param name="splunkHecUrl">splunkHecUrl.</param>
|
||||
/// <param name="splunkHecToken">splunkHecToken.</param>
|
||||
/// <param name="httpClientHandler">httpClientHandler. (optional)</param>
|
||||
public SplunkServer(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// SplunkHecUrl
|
||||
/// </summary>
|
||||
public string SplunkHecUrl
|
||||
SplunkHecUrl = splunkHecUrl;
|
||||
SplunkHecToken = splunkHecToken;
|
||||
|
||||
if (httpClientHandler == null) httpClientHandler = new HttpClientHandler();
|
||||
|
||||
SplunkClientHandler = httpClientHandler;
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SplunkHecUrl
|
||||
/// </summary>
|
||||
public string SplunkHecUrl
|
||||
{
|
||||
get => _splunkHecUrl;
|
||||
|
||||
set
|
||||
{
|
||||
get
|
||||
{
|
||||
return _splunkHecUrl;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(_splunkHecUrl) && !_splunkHecUrl.ToLower().Contains("http"))
|
||||
{
|
||||
value = $"http://{value}";
|
||||
}
|
||||
_splunkHecUrl = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHttpsHecUrl => HasHecUrl && _splunkHecUrl.ToLower().StartsWith("https");
|
||||
|
||||
/// <summary>
|
||||
/// SplunkHecToken
|
||||
/// </summary>
|
||||
public string SplunkHecToken
|
||||
{
|
||||
get
|
||||
{
|
||||
return _splunkHecToken;
|
||||
}
|
||||
set
|
||||
{
|
||||
_splunkHecToken = value;
|
||||
}
|
||||
}
|
||||
|
||||
public HttpClientHandler SplunkClientHandler { get; internal set; }
|
||||
public HttpClient HttpClient { get; private set; }
|
||||
|
||||
internal readonly object SendLock = new object();
|
||||
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
|
||||
private string _splunkHecToken = "splunk-hec-token";
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a HEC token is available
|
||||
/// </summary>
|
||||
public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token";
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a HEC url is available
|
||||
/// </summary>
|
||||
public bool HasHecUrl => !string.IsNullOrWhiteSpace(SplunkHecUrl);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the splunk URL is local
|
||||
/// </summary>
|
||||
public bool IsLocalHost => HasHecUrl && (SplunkHecUrl.ToLower().Contains("127.0.0.1") || SplunkHecUrl.ToLower().Contains("localhost"));
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Instantiate the object.
|
||||
/// </summary>
|
||||
/// <param name="splunkHecUrl">splunkHecUrl.</param>
|
||||
/// <param name="splunkHecToken">splunkHecToken.</param>
|
||||
/// <param name="httpClientHandler">httpClientHandler. (optional)</param>
|
||||
public SplunkServer(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null)
|
||||
{
|
||||
SplunkHecUrl = splunkHecUrl;
|
||||
SplunkHecToken = splunkHecToken;
|
||||
|
||||
if (httpClientHandler == null)
|
||||
{
|
||||
httpClientHandler = new HttpClientHandler();
|
||||
}
|
||||
|
||||
SplunkClientHandler = httpClientHandler;
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
private void CreateHttpClient()
|
||||
{
|
||||
HttpClient = new HttpClient(SplunkClientHandler);
|
||||
HttpClient.BaseAddress = new Uri(SplunkHecUrl);
|
||||
|
||||
// Add the HEC token to the request headers for authorization
|
||||
HttpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables SSL validation (can be used for insecure https urls)
|
||||
/// <note>This overwrites your own httpClientHandler</note>
|
||||
/// </summary>
|
||||
public void DisableSSLValidation()
|
||||
{
|
||||
HttpClientHandler clientHandler = new HttpClientHandler()
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true,
|
||||
};
|
||||
|
||||
SplunkClientHandler = clientHandler;
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> SendAsync(SplunkPayload splunkPayload)
|
||||
{
|
||||
if (splunkPayload == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create an event object with the required fields
|
||||
var eventObject = new
|
||||
{
|
||||
@event = splunkPayload.EventData,
|
||||
sourcetype = splunkPayload.SourceType,
|
||||
host = splunkPayload.Host
|
||||
};
|
||||
|
||||
// Serialize the event object to JSON
|
||||
string eventJson = JsonHelper.ToJson(eventObject);
|
||||
|
||||
if (!HasHecToken)
|
||||
{
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
// Create an HTTP content with the event data
|
||||
var content = new StringContent(eventJson, Encoding.UTF8, "application/json");
|
||||
|
||||
// Send the event to Splunk
|
||||
HttpResponseMessage response = await HttpClient.PostAsync("/services/collector/event", content);
|
||||
|
||||
return response;
|
||||
if (!string.IsNullOrWhiteSpace(_splunkHecUrl) && !_splunkHecUrl.ToLower().Contains("http"))
|
||||
value = $"http://{value}";
|
||||
_splunkHecUrl = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHttpsHecUrl => HasHecUrl && _splunkHecUrl.ToLower().StartsWith("https");
|
||||
|
||||
/// <summary>
|
||||
/// SplunkHecToken
|
||||
/// </summary>
|
||||
public string SplunkHecToken { get; set; } = "splunk-hec-token";
|
||||
|
||||
public HttpClientHandler SplunkClientHandler { get; internal set; }
|
||||
public HttpClient HttpClient { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a HEC token is available
|
||||
/// </summary>
|
||||
public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token";
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a HEC url is available
|
||||
/// </summary>
|
||||
public bool HasHecUrl => !string.IsNullOrWhiteSpace(SplunkHecUrl);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the splunk URL is local
|
||||
/// </summary>
|
||||
public bool IsLocalHost => HasHecUrl &&
|
||||
(SplunkHecUrl.ToLower().Contains("127.0.0.1") ||
|
||||
SplunkHecUrl.ToLower().Contains("localhost"));
|
||||
|
||||
private void CreateHttpClient()
|
||||
{
|
||||
HttpClient = new HttpClient(SplunkClientHandler);
|
||||
HttpClient.BaseAddress = new Uri(SplunkHecUrl);
|
||||
|
||||
// Add the HEC token to the request headers for authorization
|
||||
HttpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables SSL validation (can be used for insecure https urls)
|
||||
/// <note>This overwrites your own httpClientHandler</note>
|
||||
/// </summary>
|
||||
public void DisableSSLValidation()
|
||||
{
|
||||
var clientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
|
||||
};
|
||||
|
||||
SplunkClientHandler = clientHandler;
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> SendAsync(SplunkPayload splunkPayload)
|
||||
{
|
||||
if (splunkPayload == null) return null;
|
||||
|
||||
// Create an event object with the required fields
|
||||
var eventObject = new
|
||||
{
|
||||
@event = splunkPayload.EventData,
|
||||
sourcetype = splunkPayload.SourceType,
|
||||
host = splunkPayload.Host
|
||||
};
|
||||
|
||||
// Serialize the event object to JSON
|
||||
var eventJson = JsonHelper.ToJson(eventObject);
|
||||
|
||||
if (!HasHecToken) CreateHttpClient();
|
||||
|
||||
// Create an HTTP content with the event data
|
||||
var content = new StringContent(eventJson, Encoding.UTF8, "application/json");
|
||||
|
||||
// Send the event to Splunk
|
||||
var response = await HttpClient.PostAsync("/services/collector/event", content);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace EonaCat.Logger.Syslog
|
||||
namespace EonaCat.Logger.Syslog;
|
||||
// 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.
|
||||
|
||||
/// <summary>
|
||||
/// Syslog server.
|
||||
/// </summary>
|
||||
public class SyslogServer
|
||||
{
|
||||
// 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.
|
||||
private string _Hostname = "127.0.0.1";
|
||||
private int _Port = 514;
|
||||
|
||||
internal UdpClient Udp;
|
||||
|
||||
/// <summary>
|
||||
/// Syslog server.
|
||||
/// Instantiate the object.
|
||||
/// </summary>
|
||||
public class SyslogServer
|
||||
public SyslogServer()
|
||||
{
|
||||
/// <summary>
|
||||
/// Hostname.
|
||||
/// </summary>
|
||||
public string Hostname
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hostname.
|
||||
/// </summary>
|
||||
public string Hostname
|
||||
{
|
||||
get => _Hostname;
|
||||
|
||||
set
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Hostname;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
|
||||
_Hostname = value;
|
||||
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 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);
|
||||
SetUdp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UDP port.
|
||||
/// </summary>
|
||||
public int Port
|
||||
{
|
||||
get => _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 => _Hostname + ":" + _Port;
|
||||
|
||||
private void SetUdp()
|
||||
{
|
||||
Udp = null;
|
||||
Udp = new UdpClient(_Hostname, _Port);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\EonaCat.Logger\EonaCat.Logger.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,14 +0,0 @@
|
||||
using EonaCat.Logger;
|
||||
using EonaCat.Logger.Managers;
|
||||
|
||||
LogManager.Instance.Write("INFO", ELogType.INFO, true);
|
||||
LogManager.Instance.Write("WARNING", ELogType.WARNING, true);
|
||||
LogManager.Instance.Write("ERROR", ELogType.ERROR, true);
|
||||
LogManager.Instance.Write("DEBUG", ELogType.DEBUG, true);
|
||||
LogManager.Instance.Write("CRITICAL", ELogType.CRITICAL, true);
|
||||
LogManager.Instance.Write("TRACE", ELogType.TRACE, true);
|
||||
LogManager.Instance.Write("TRAFFIC", ELogType.TRAFFIC, true);
|
||||
LogManager.Instance.Write("NONE", ELogType.NONE, true);
|
||||
|
||||
Console.WriteLine("Press a key to exit");
|
||||
Console.ReadKey();
|
||||
@@ -1,118 +1,120 @@
|
||||
using EonaCat.Logger.Managers;
|
||||
using System.IO.Compression;
|
||||
using System.IO.Compression;
|
||||
using EonaCat.Logger.EonaCatCoreLogger;
|
||||
using EonaCat.Logger.Extensions;
|
||||
using EonaCat.Logger.Managers;
|
||||
|
||||
namespace EonaCat.Logger.Test.Web
|
||||
namespace EonaCat.Logger.Test.Web;
|
||||
|
||||
public static class Logger
|
||||
{
|
||||
public static class Logger
|
||||
private static LogManager LogManager;
|
||||
public static ELogType MinLogType { get; set; }
|
||||
public static bool UseLocalTime { get; set; }
|
||||
public static string LogFolder => Path.Combine(FileLoggerOptions.DefaultPath, "logs");
|
||||
public static string CurrentLogFile => LogManager.CurrentLogFile;
|
||||
public static bool IsDisabled { get; set; }
|
||||
|
||||
public static void DeleteCurrentLogFile()
|
||||
{
|
||||
public static ELogType MaxLogType { get; set; }
|
||||
public static bool UseLocalTime { get; set; }
|
||||
private static LogManager LogManager;
|
||||
public static string LogFolder => "Logs";
|
||||
public static string CurrentLogFile => LogManager.CurrentLogFile;
|
||||
public static bool IsDisabled { get; set; }
|
||||
if (IsDisabled)
|
||||
return;
|
||||
|
||||
public static void DeleteCurrentLogFile()
|
||||
LogManager.DeleteCurrentLogFile();
|
||||
}
|
||||
|
||||
private static string ConvertToAbsolutePath(string path)
|
||||
{
|
||||
return Path.IsPathRooted(path) ? path : Path.Combine(LogFolder, path);
|
||||
}
|
||||
|
||||
public static async Task DownloadLogAsync(HttpContext context, string logName, long limit)
|
||||
{
|
||||
var logFileName = logName + ".log";
|
||||
|
||||
await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open,
|
||||
FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true);
|
||||
var response = context.Response;
|
||||
|
||||
response.ContentType = "text/plain";
|
||||
response.Headers.ContentDisposition = "attachment;filename=" + logFileName;
|
||||
|
||||
if (limit > fS.Length || limit < 1)
|
||||
limit = fS.Length;
|
||||
|
||||
var oFS = new OffsetStream(fS, 0, limit);
|
||||
var request = context.Request;
|
||||
Stream stream;
|
||||
|
||||
string acceptEncoding = request.Headers["Accept-Encoding"];
|
||||
if (string.IsNullOrEmpty(acceptEncoding))
|
||||
{
|
||||
if (IsDisabled)
|
||||
return;
|
||||
|
||||
LogManager.DeleteCurrentLogFile();
|
||||
stream = response.Body;
|
||||
}
|
||||
|
||||
private static string ConvertToAbsolutePath(string path)
|
||||
else
|
||||
{
|
||||
return Path.IsPathRooted(path) ? path : Path.Combine(LogFolder, path);
|
||||
}
|
||||
var acceptEncodingParts = acceptEncoding.Split(new[] { ',' },
|
||||
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
|
||||
public static async Task DownloadLogAsync(HttpContext context, string logName, long limit)
|
||||
{
|
||||
var logFileName = logName + ".log";
|
||||
|
||||
await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true);
|
||||
var response = context.Response;
|
||||
|
||||
response.ContentType = "text/plain";
|
||||
response.Headers.ContentDisposition = "attachment;filename=" + logFileName;
|
||||
|
||||
if ((limit > fS.Length) || (limit < 1))
|
||||
limit = fS.Length;
|
||||
|
||||
var oFS = new OffsetStream(fS, 0, limit);
|
||||
var request = context.Request;
|
||||
Stream stream;
|
||||
|
||||
string acceptEncoding = request.Headers["Accept-Encoding"];
|
||||
if (string.IsNullOrEmpty(acceptEncoding))
|
||||
if (acceptEncodingParts.Contains("br"))
|
||||
{
|
||||
stream = response.Body;
|
||||
response.Headers.ContentEncoding = "br";
|
||||
stream = new BrotliStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
else if (acceptEncodingParts.Contains("gzip"))
|
||||
{
|
||||
response.Headers.ContentEncoding = "gzip";
|
||||
stream = new GZipStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
else if (acceptEncodingParts.Contains("deflate"))
|
||||
{
|
||||
response.Headers.ContentEncoding = "deflate";
|
||||
stream = new DeflateStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] acceptEncodingParts = acceptEncoding.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
|
||||
if (acceptEncodingParts.Contains("br"))
|
||||
{
|
||||
response.Headers.ContentEncoding = "br";
|
||||
stream = new BrotliStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
else if (acceptEncodingParts.Contains("gzip"))
|
||||
{
|
||||
response.Headers.ContentEncoding = "gzip";
|
||||
stream = new GZipStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
else if (acceptEncodingParts.Contains("deflate"))
|
||||
{
|
||||
response.Headers.ContentEncoding = "deflate";
|
||||
stream = new DeflateStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = response.Body;
|
||||
}
|
||||
}
|
||||
|
||||
await using (stream)
|
||||
{
|
||||
await oFS.CopyToAsync(stream).ConfigureAwait(false);
|
||||
|
||||
if (fS.Length > limit)
|
||||
await stream.WriteAsync("\r\n####__EONACATLOGGER_TRUNCATED___####"u8.ToArray()).ConfigureAwait(false);
|
||||
stream = response.Body;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Log(string message, ELogType logType = ELogType.INFO, bool writeToConsole = true)
|
||||
await using (stream)
|
||||
{
|
||||
if (IsDisabled)
|
||||
return;
|
||||
await oFS.CopyToAsync(stream).ConfigureAwait(false);
|
||||
|
||||
LogManager.Write(message, logType, writeToConsole);
|
||||
}
|
||||
|
||||
public static void Log(Exception exception, string message = "", bool writeToConsole = true)
|
||||
{
|
||||
if (IsDisabled)
|
||||
return;
|
||||
|
||||
LogManager.Write(exception, module: message, writeToConsole: writeToConsole);
|
||||
}
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
var loggerSettings = new LoggerSettings
|
||||
{
|
||||
Id = "EonaCatDnsLogger",
|
||||
MaxLogType = ELogType.TRACE,
|
||||
UseLocalTime = UseLocalTime,
|
||||
FileLoggerOptions =
|
||||
{
|
||||
LogDirectory = "Logs",
|
||||
FileSizeLimit = 20_000_000, // 20 MB,
|
||||
UseLocalTime = UseLocalTime,
|
||||
}
|
||||
};
|
||||
LogManager = new LogManager(loggerSettings);
|
||||
if (fS.Length > limit)
|
||||
await stream.WriteAsync("\r\n####__EONACATLOGGER_TRUNCATED___####"u8.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Log(string message, ELogType logType = ELogType.INFO, bool writeToConsole = true)
|
||||
{
|
||||
if (IsDisabled)
|
||||
return;
|
||||
|
||||
LogManager.Write(message, logType, writeToConsole);
|
||||
}
|
||||
|
||||
public static void Log(Exception exception, string message = "", bool writeToConsole = true)
|
||||
{
|
||||
if (IsDisabled)
|
||||
return;
|
||||
|
||||
LogManager.Write(exception, message, writeToConsole: writeToConsole);
|
||||
}
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
var loggerSettings = new LoggerSettings
|
||||
{
|
||||
Id = "EonaCatTestLogger",
|
||||
MinLogType = ELogType.INFO,
|
||||
UseLocalTime = UseLocalTime,
|
||||
FileLoggerOptions =
|
||||
{
|
||||
LogDirectory = LogFolder,
|
||||
FileSizeLimit = 20_000_000, // 20 MB,
|
||||
UseLocalTime = UseLocalTime
|
||||
}
|
||||
};
|
||||
LogManager = new LogManager(loggerSettings);
|
||||
}
|
||||
}
|
||||
@@ -23,4 +23,4 @@
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
</p>
|
||||
@@ -1,27 +1,26 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace EonaCat.Logger.Web.Pages
|
||||
namespace EonaCat.Logger.Web.Pages;
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public class ErrorModel : PageModel
|
||||
{
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public class ErrorModel : PageModel
|
||||
private readonly ILogger<ErrorModel> _logger;
|
||||
|
||||
public ErrorModel(ILogger<ErrorModel> logger)
|
||||
{
|
||||
public string? RequestId { get; set; }
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
public string? RequestId { get; set; }
|
||||
|
||||
private readonly ILogger<ErrorModel> _logger;
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
public ErrorModel(ILogger<ErrorModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||
}
|
||||
public void OnGet()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,4 @@
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,19 +1,17 @@
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace EonaCat.Logger.Web.Pages
|
||||
namespace EonaCat.Logger.Web.Pages;
|
||||
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public class IndexModel : PageModel
|
||||
private readonly ILogger<IndexModel> _logger;
|
||||
|
||||
public IndexModel(ILogger<IndexModel> logger)
|
||||
{
|
||||
private readonly ILogger<IndexModel> _logger;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IndexModel(ILogger<IndexModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,4 @@
|
||||
}
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
|
||||
<p>Use this page to detail your site's privacy policy.</p>
|
||||
<p>Use this page to detail your site's privacy policy.</p>
|
||||
@@ -1,19 +1,17 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace EonaCat.Logger.Web.Pages
|
||||
namespace EonaCat.Logger.Web.Pages;
|
||||
|
||||
public class PrivacyModel : PageModel
|
||||
{
|
||||
public class PrivacyModel : PageModel
|
||||
private readonly ILogger<PrivacyModel> _logger;
|
||||
|
||||
public PrivacyModel(ILogger<PrivacyModel> logger)
|
||||
{
|
||||
private readonly ILogger<PrivacyModel> _logger;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public PrivacyModel(ILogger<PrivacyModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>@ViewData["Title"] - EonaCat.Logger.Web</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/EonaCat.Logger.Web.styles.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true"/>
|
||||
<link rel="stylesheet" href="~/EonaCat.Logger.Web.styles.css" asp-append-version="true"/>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" asp-area="" asp-page="/Index">EonaCat.Logger.Web</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="border-top footer text-muted">
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container">
|
||||
© 2022 - EonaCat.Logger.Web - <a asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
<a class="navbar-brand" asp-area="" asp-page="/Index">EonaCat.Logger.Web</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
<footer class="border-top footer text-muted">
|
||||
<div class="container">
|
||||
© 2022 - EonaCat.Logger.Web - <a asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
|
||||
@await RenderSectionAsync("Scripts", false)
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,2 +1,2 @@
|
||||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||
@@ -1,3 +1,3 @@
|
||||
@using EonaCat.Logger.Web
|
||||
@namespace EonaCat.Logger.Web.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@@ -1,3 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ using EonaCat.Web.Tracer.Extensions;
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
FileLoggerOptions options = new FileLoggerOptions();
|
||||
var options = new FileLoggerOptions();
|
||||
options.MaxRolloverFiles = 5;
|
||||
options.FileSizeLimit = 1 * 1024 * 1024 / 4;
|
||||
options.UseLocalTime = true;
|
||||
builder.Logging.AddEonaCatFileLogger(fileLoggerOptions: options, filenamePrefix:"web");
|
||||
builder.Logging.AddEonaCatFileLogger(fileLoggerOptions: options, filenamePrefix: "web");
|
||||
|
||||
builder.Services.AddRazorPages();
|
||||
|
||||
@@ -35,6 +35,7 @@ rateOptions.AddDefaultConfiguration(config =>
|
||||
);
|
||||
|
||||
RateLimiterMiddleware.OnLimitResponseCreated += RateLimiterMiddlewareOnLimitResponseCreatedAsync;
|
||||
|
||||
async void RateLimiterMiddlewareOnLimitResponseCreatedAsync(object? sender, HttpContext httpContext)
|
||||
{
|
||||
await httpContext.Response.WriteAsync(" THIS IS MY CUSTOM RATE LIMIT MESSAGE").ConfigureAwait(false);
|
||||
@@ -61,7 +62,7 @@ builder.Services.AddMemoryCache();
|
||||
var app = builder.Build();
|
||||
|
||||
Logger.UseLocalTime = true;
|
||||
Logger.MaxLogType = ELogType.TRACE;
|
||||
Logger.MinLogType = ELogType.TRACE;
|
||||
Logger.Configure();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
@@ -83,6 +84,7 @@ if (!app.Environment.IsDevelopment())
|
||||
// await httpContext.Response.WriteAsync("THIS IS MY CUSTOM RATE LIMIT MESSAGE");
|
||||
//}
|
||||
WebTracer.OnLog += WebTracer_OnLog;
|
||||
|
||||
void WebTracer_OnLog(object? sender, string e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
@@ -103,10 +105,10 @@ void RunLoggingExceptionTests()
|
||||
var loggerSettings = new LoggerSettings();
|
||||
loggerSettings.FileLoggerOptions.UseLocalTime = true;
|
||||
loggerSettings.UseLocalTime = true;
|
||||
loggerSettings.MaxLogType = ELogType.TRACE;
|
||||
loggerSettings.MinLogType = ELogType.INFO;
|
||||
var logger = new LogManager(loggerSettings);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -117,19 +119,20 @@ void RunLoggingExceptionTests()
|
||||
logger.Write(exception);
|
||||
Console.WriteLine($"Normal ExceptionLogged: {i}");
|
||||
}
|
||||
|
||||
Task.Delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
Task.Run(RunWebLoggerTests);
|
||||
Task.Run(RunWebLoggingTests);
|
||||
//Task.Run(RunWebLoggerTests);
|
||||
//Task.Run(RunWebLoggingTests);
|
||||
Task.Run(RunLoggingTests);
|
||||
Task.Run(RunLoggingExceptionTests);
|
||||
Task.Run(RunWebLoggingExceptionTests);
|
||||
//Task.Run(RunLoggingExceptionTests);
|
||||
//Task.Run(RunWebLoggingExceptionTests);
|
||||
|
||||
void RunWebLoggingExceptionTests()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -145,18 +148,16 @@ void RunWebLoggingExceptionTests()
|
||||
app.Logger.LogInformation(exception, "INFORMATION");
|
||||
Console.WriteLine($"WebExceptionLogged: {i}");
|
||||
}
|
||||
|
||||
Task.Delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
void RunWebLoggingTests()
|
||||
{
|
||||
if (!Directory.Exists(Logger.LogFolder))
|
||||
{
|
||||
Directory.CreateDirectory(Logger.LogFolder);
|
||||
}
|
||||
if (!Directory.Exists(Logger.LogFolder)) Directory.CreateDirectory(Logger.LogFolder);
|
||||
|
||||
for (int i = 0; i < 9000000; i++)
|
||||
for (var i = 0; i < 9000000; i++)
|
||||
{
|
||||
app.Logger.LogInformation($"web-test {i}");
|
||||
File.AppendAllText(Path.Combine(Logger.LogFolder, "test.log"), $"WebLogged: {i}{Environment.NewLine}");
|
||||
@@ -170,15 +171,15 @@ void RunLoggingTests()
|
||||
var loggerSettings = new LoggerSettings();
|
||||
loggerSettings.UseLocalTime = true;
|
||||
loggerSettings.FileLoggerOptions.UseLocalTime = true;
|
||||
loggerSettings.MaxLogType = ELogType.TRACE;
|
||||
loggerSettings.MinLogType = ELogType.INFO;
|
||||
loggerSettings.FileLoggerOptions.FileSizeLimit = 1024 * 1024 * 1;
|
||||
loggerSettings.FileLoggerOptions.FileNamePrefix = "AllTypes";
|
||||
loggerSettings.FileLoggerOptions.MaxRolloverFiles = 5;
|
||||
var logger = new LogManager(loggerSettings);
|
||||
|
||||
for (int i = 0; i < 9000000; i++)
|
||||
for (var i = 0; i < 9000000; i++)
|
||||
{
|
||||
logger.Write($"test to file {i} INFO", ELogType.INFO);
|
||||
logger.Write($"test to file {i} INFO");
|
||||
logger.Write($"test to file {i} CRITICAL", ELogType.CRITICAL);
|
||||
logger.Write($"test to file {i} DEBUG", ELogType.DEBUG);
|
||||
logger.Write($"test to file {i} ERROR", ELogType.ERROR);
|
||||
@@ -193,9 +194,9 @@ void RunLoggingTests()
|
||||
|
||||
void RunWebLoggerTests()
|
||||
{
|
||||
for (int i = 0; i < 9000000; i++)
|
||||
for (var i = 0; i < 9000000; i++)
|
||||
{
|
||||
Logger.Log($"test via logger {i} INFO", ELogType.INFO);
|
||||
Logger.Log($"test via logger {i} INFO");
|
||||
Logger.Log($"test via logger {i} CRITICAL", ELogType.CRITICAL);
|
||||
Logger.Log($"test via logger {i} DEBUG", ELogType.DEBUG);
|
||||
Logger.Log($"test via logger {i} ERROR", ELogType.ERROR);
|
||||
@@ -208,4 +209,4 @@ void RunWebLoggerTests()
|
||||
}
|
||||
}
|
||||
|
||||
app.Run();
|
||||
app.Run();
|
||||
Reference in New Issue
Block a user