Updated
This commit is contained in:
@@ -1,13 +1,11 @@
|
|||||||
namespace EonaCat.Logger
|
namespace EonaCat.Logger;
|
||||||
{
|
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||||
// 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.
|
||||||
// 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.
|
public const string NAME = "EonaCatLogger";
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
public const string VERSION = "1.1.0";
|
||||||
|
|
||||||
public static class DllInfo
|
static DllInfo()
|
||||||
{
|
{
|
||||||
public const string NAME = "EonaCatLogger";
|
var isDebug = false;
|
||||||
public const string VERSION = "1.1.0";
|
|
||||||
|
|
||||||
static DllInfo()
|
|
||||||
{
|
|
||||||
bool isDebug = false;
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
isDebug = true;
|
isDebug = true;
|
||||||
#endif
|
#endif
|
||||||
VersionName = isDebug ? "DEBUG" : "RELEASE";
|
VersionName = isDebug ? "DEBUG" : "RELEASE";
|
||||||
}
|
|
||||||
|
|
||||||
internal static string VersionName { get; }
|
|
||||||
|
|
||||||
public static string ApplicationName { get; internal set; } = "EonaCatLogger";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static string VersionName { get; }
|
||||||
|
|
||||||
|
public static string ApplicationName { get; internal set; } = "EonaCatLogger";
|
||||||
}
|
}
|
||||||
@@ -1,188 +1,192 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
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.
|
NONE = 0,
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
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,
|
switch (logLevel)
|
||||||
INFO = 1,
|
|
||||||
WARNING = 2,
|
|
||||||
ERROR = 3,
|
|
||||||
TRAFFIC = 4,
|
|
||||||
DEBUG = 5,
|
|
||||||
CRITICAL = 6,
|
|
||||||
TRACE = 7
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LogTypeConverter
|
|
||||||
{
|
|
||||||
public static ELogType FromLogLevel(this LogLevel logLevel)
|
|
||||||
{
|
{
|
||||||
switch (logLevel)
|
case LogLevel.None:
|
||||||
{
|
return ELogType.NONE;
|
||||||
case LogLevel.None:
|
case LogLevel.Error:
|
||||||
return ELogType.NONE;
|
return ELogType.ERROR;
|
||||||
case LogLevel.Error:
|
case LogLevel.Debug:
|
||||||
return ELogType.ERROR;
|
return ELogType.DEBUG;
|
||||||
case LogLevel.Debug:
|
case LogLevel.Critical:
|
||||||
return ELogType.DEBUG;
|
return ELogType.CRITICAL;
|
||||||
case LogLevel.Critical:
|
case LogLevel.Warning:
|
||||||
return ELogType.CRITICAL;
|
return ELogType.WARNING;
|
||||||
case LogLevel.Warning:
|
case LogLevel.Trace:
|
||||||
return ELogType.WARNING;
|
return ELogType.TRACE;
|
||||||
case LogLevel.Trace:
|
case LogLevel.Information:
|
||||||
return ELogType.TRACE;
|
return ELogType.INFO;
|
||||||
case LogLevel.Information:
|
default:
|
||||||
return ELogType.INFO;
|
return ELogType.TRAFFIC;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static LogLevel ToLogLevel(this ELogType logLevel)
|
||||||
/// Message severity.
|
|
||||||
/// </summary>
|
|
||||||
public enum ESeverity
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
switch (logLevel)
|
||||||
/// Debug messages.
|
{
|
||||||
/// </summary>
|
case ELogType.NONE:
|
||||||
Debug = 0,
|
return LogLevel.None;
|
||||||
/// <summary>
|
case ELogType.ERROR:
|
||||||
/// Informational messages.
|
return LogLevel.Error;
|
||||||
/// </summary>
|
case ELogType.DEBUG:
|
||||||
Info = 1,
|
return LogLevel.Debug;
|
||||||
/// <summary>
|
case ELogType.CRITICAL:
|
||||||
/// Warning messages.
|
return LogLevel.Critical;
|
||||||
/// </summary>
|
case ELogType.WARNING:
|
||||||
Warn = 2,
|
return LogLevel.Warning;
|
||||||
/// <summary>
|
case ELogType.TRACE:
|
||||||
/// Error messages.
|
return LogLevel.Trace;
|
||||||
/// </summary>
|
case ELogType.TRAFFIC:
|
||||||
Error = 3,
|
return LogLevel.Trace;
|
||||||
/// <summary>
|
case ELogType.INFO:
|
||||||
/// Alert messages.
|
return LogLevel.Information;
|
||||||
/// </summary>
|
default:
|
||||||
Alert = 4,
|
return LogLevel.Information;
|
||||||
/// <summary>
|
}
|
||||||
/// Critical messages.
|
}
|
||||||
/// </summary>
|
|
||||||
Critical = 5,
|
public static string ToString(this ELogType logLevel)
|
||||||
/// <summary>
|
{
|
||||||
/// Emergency messages.
|
switch (logLevel)
|
||||||
/// </summary>
|
{
|
||||||
Emergency = 6
|
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>
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks>
|
<TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks>
|
||||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<Version>1.2.5</Version>
|
<FileVersion>1.2.5</FileVersion>
|
||||||
<Authors>EonaCat (Jeroen Saey)</Authors>
|
<Authors>EonaCat (Jeroen Saey)</Authors>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Company>EonaCat (Jeroen Saey)</Company>
|
<Company>EonaCat (Jeroen Saey)</Company>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<EVRevisionFormat>1.0.0.0+{chash:10}.{c:ymd}</EVRevisionFormat>
|
<EVRevisionFormat>1.2.5+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||||
<EVDefault>true</EVDefault>
|
<EVDefault>true</EVDefault>
|
||||||
<EVInfo>true</EVInfo>
|
<EVInfo>true</EVInfo>
|
||||||
<EVTagMatch>v[0-9]*</EVTagMatch>
|
<EVTagMatch>v[0-9]*</EVTagMatch>
|
||||||
|
|||||||
@@ -2,40 +2,34 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
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.
|
private static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
{
|
||||||
|
builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extensions for adding the <see cref="FileLoggerProvider" /> to the <see cref="ILoggingBuilder" />
|
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
||||||
/// </summary>
|
/// </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)
|
if (fileLoggerOptions == null) fileLoggerOptions = new FileLoggerOptions();
|
||||||
{
|
|
||||||
builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
if (!string.IsNullOrWhiteSpace(filenamePrefix)) fileLoggerOptions.FileNamePrefix = filenamePrefix;
|
||||||
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
builder.AddEonaCatFileLogger(options =>
|
||||||
/// </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 =>
|
|
||||||
{
|
{
|
||||||
options.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
|
options.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
|
||||||
options.FlushPeriod = fileLoggerOptions.FlushPeriod;
|
options.FlushPeriod = fileLoggerOptions.FlushPeriod;
|
||||||
@@ -49,26 +43,22 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
|
|||||||
options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
|
options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
|
||||||
options.UseLocalTime = fileLoggerOptions.UseLocalTime;
|
options.UseLocalTime = fileLoggerOptions.UseLocalTime;
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
);
|
/// <summary>
|
||||||
return builder;
|
/// 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>
|
return builder;
|
||||||
/// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,108 +2,93 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using EonaCat.Logger.EonaCatCoreLogger.Internal;
|
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.
|
private int _fileSizeLimit = 200 * 1024 * 1024;
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
private int _maxRolloverFiles = 10;
|
||||||
|
private int _retainedFileCountLimit = 50;
|
||||||
|
|
||||||
|
public static string DefaultPath =>
|
||||||
|
AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public class FileLoggerOptions : BatchingLoggerOptions
|
public int FileSizeLimit
|
||||||
{
|
{
|
||||||
private int _fileSizeLimit = 200 * 1024 * 1024;
|
get => _fileSizeLimit;
|
||||||
private int _retainedFileCountLimit = 50;
|
|
||||||
private int _maxRolloverFiles = 10;
|
|
||||||
private int _maxTries = 3;
|
|
||||||
|
|
||||||
public static string DefaultPath => AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
|
set
|
||||||
|
|
||||||
/// <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
|
|
||||||
{
|
{
|
||||||
get => _fileSizeLimit;
|
if (value <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive.");
|
||||||
set
|
_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.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
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.
|
private static readonly object WriteLock = new();
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
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>
|
/// <summary>
|
||||||
/// An <see cref="ILoggerProvider" /> that writes logs to a file
|
/// Creates an instance of the <see cref="FileLoggerProvider" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ProviderAlias("EonaCatFileLogger")]
|
/// <param name="options">The options object controlling the logger</param>
|
||||||
public class FileLoggerProvider : BatchingLoggerProvider
|
public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
|
||||||
{
|
{
|
||||||
private readonly string _path;
|
var loggerOptions = options.Value;
|
||||||
private readonly string _fileNamePrefix;
|
_path = loggerOptions.LogDirectory;
|
||||||
private readonly int _maxFileSize;
|
_fileNamePrefix = loggerOptions.FileNamePrefix;
|
||||||
private readonly int _maxRetainedFiles;
|
_maxFileSize = loggerOptions.FileSizeLimit;
|
||||||
private readonly int _maxRolloverFiles;
|
_maxRetainedFiles = loggerOptions.RetainedFileCountLimit;
|
||||||
private readonly int _maxTries;
|
_maxRolloverFiles = loggerOptions.MaxRolloverFiles;
|
||||||
private int _rollOverCount = 0;
|
_maxTries = loggerOptions.MaxWriteTries;
|
||||||
private static readonly object WriteLock = new object();
|
}
|
||||||
private static readonly object RollOverLock = new object();
|
|
||||||
private string _logFile;
|
|
||||||
private bool _rollingOver;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The file to which log messages should be appended.
|
/// The file to which log messages should be appended.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LogFile
|
public string LogFile
|
||||||
|
{
|
||||||
|
get => _logFile;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
get
|
_logFile = value;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_logFile))
|
||||||
{
|
{
|
||||||
return _logFile;
|
var dir = Path.GetDirectoryName(_logFile);
|
||||||
}
|
if (!string.IsNullOrEmpty(dir))
|
||||||
set
|
if (!Directory.Exists(dir))
|
||||||
{
|
Directory.CreateDirectory(dir);
|
||||||
_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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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 EonaCat.Logger.Managers;
|
||||||
using Microsoft.Extensions.Logging;
|
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.
|
private readonly string _category;
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
private readonly BatchingLoggerProvider _provider;
|
||||||
|
private LoggerSettings _loggerSettings;
|
||||||
|
|
||||||
public class BatchingLogger : ILogger
|
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings)
|
||||||
{
|
{
|
||||||
private LoggerSettings _loggerSettings;
|
_loggerSettings = loggerSettings;
|
||||||
private readonly BatchingLoggerProvider _provider;
|
_provider = loggerProvider;
|
||||||
private readonly string _category;
|
_category = categoryName;
|
||||||
private DateTimeOffset CurrentDateTimeOffset => _loggerSettings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
}
|
||||||
private DateTime CurrentDateTme => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
|
||||||
|
|
||||||
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;
|
DateTime = timestamp.DateTime,
|
||||||
_provider = loggerProvider;
|
Message = message,
|
||||||
_category = categoryName;
|
LogType = logLevel.FromLogLevel()
|
||||||
}
|
};
|
||||||
|
|
||||||
public IDisposable BeginScope<TState>(TState state)
|
currentMessage.Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin)
|
||||||
{
|
? "BatchingLogger"
|
||||||
return null;
|
: _loggerSettings.LogOrigin;
|
||||||
}
|
_loggerSettings?.OnLogEvent(currentMessage);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,48 +1,35 @@
|
|||||||
using System;
|
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.
|
private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100);
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
||||||
|
|
||||||
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;
|
get => _flushPeriod;
|
||||||
private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100);
|
|
||||||
|
|
||||||
/// <summary>
|
set
|
||||||
/// Gets or sets the period after which logs will be flushed to the store.
|
|
||||||
/// </summary>
|
|
||||||
public TimeSpan FlushPeriod
|
|
||||||
{
|
{
|
||||||
get => _flushPeriod;
|
if (value <= TimeSpan.Zero)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive.");
|
||||||
set
|
_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.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
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.
|
private readonly int _batchSize;
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
||||||
|
|
||||||
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;
|
var loggerOptions = options.Value;
|
||||||
protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
|
||||||
|
|
||||||
private readonly List<LogMessage> _currentBatch = new List<LogMessage>();
|
if (loggerOptions.FlushPeriod <= TimeSpan.Zero)
|
||||||
private readonly TimeSpan _interval;
|
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod),
|
||||||
private readonly int _batchSize;
|
$"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
|
||||||
|
|
||||||
private ConcurrentQueue<LogMessage> _messageQueue;
|
if (options.Value is FileLoggerOptions fileLoggerOptions) UseLocalTime = fileLoggerOptions.UseLocalTime;
|
||||||
private Task _outputTask;
|
_batchSize = loggerOptions.BatchSize;
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private LoggerSettings _loggerSettings;
|
|
||||||
|
|
||||||
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)
|
_loggerSettings = new LoggerSettings();
|
||||||
{
|
_loggerSettings.UseLocalTime = UseLocalTime;
|
||||||
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
|
return _loggerSettings;
|
||||||
}
|
|
||||||
|
|
||||||
if (options.Value is FileLoggerOptions fileLoggerOptions)
|
|
||||||
{
|
|
||||||
UseLocalTime = fileLoggerOptions.UseLocalTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
_interval = loggerOptions.FlushPeriod;
|
|
||||||
_batchSize = loggerOptions.BatchSize;
|
|
||||||
|
|
||||||
Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LoggerSettings LoggerSettings
|
set => _loggerSettings = value;
|
||||||
{
|
}
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_loggerSettings != null) return _loggerSettings;
|
|
||||||
|
|
||||||
_loggerSettings = new LoggerSettings();
|
public bool IsStarted { get; set; }
|
||||||
_loggerSettings.UseLocalTime = UseLocalTime;
|
|
||||||
return _loggerSettings;
|
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;
|
if (_currentBatch.Count > 0)
|
||||||
}
|
try
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
_currentBatch.Add(message);
|
await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
limit--;
|
_currentBatch.Clear();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentBatch.Count > 0)
|
Thread.Sleep(10);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
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.
|
||||||
// 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.
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
||||||
|
|
||||||
public struct LogMessage
|
public struct LogMessage
|
||||||
{
|
{
|
||||||
public DateTimeOffset Timestamp { get; set; }
|
public DateTimeOffset Timestamp { get; set; }
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using EonaCat.Logger.Helpers;
|
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.
|
public DateTime DateTime { get; set; }
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
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; }
|
return
|
||||||
public string Message { get; set; }
|
$"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper<ELogType>.ToString(LogType)}] {Message}";
|
||||||
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}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
using System;
|
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;
|
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.Diagnostics;
|
||||||
using System.Text;
|
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 st = new StackTrace(exception, true);
|
||||||
var frame = st.GetFrame(0);
|
var frame = st.GetFrame(0);
|
||||||
int fileLine = frame.GetFileLineNumber();
|
var fileLine = frame.GetFileLineNumber();
|
||||||
string filename = frame.GetFileName();
|
var filename = frame.GetFileName();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine("--- Exception details provided by EonaCatLogger ---");
|
sb.AppendLine("--- Exception details provided by EonaCatLogger ---");
|
||||||
if (!string.IsNullOrEmpty(module))
|
if (!string.IsNullOrEmpty(module))
|
||||||
sb.AppendLine(" Module : " + module);
|
sb.AppendLine(" Module : " + module);
|
||||||
if (!string.IsNullOrEmpty(method))
|
if (!string.IsNullOrEmpty(method))
|
||||||
sb.AppendLine(" Method : " + method);
|
sb.AppendLine(" Method : " + method);
|
||||||
sb.Append(" Type : ").AppendLine(exception.GetType().ToString());
|
sb.Append(" Type : ").AppendLine(exception.GetType().ToString());
|
||||||
sb.Append(" Data : ").AppendLine(exception.Data != null && exception.Data.Count > 0 ? FormatExceptionData(exception.Data) : "(none)");
|
sb.Append(" Data : ").AppendLine(exception.Data != null && exception.Data.Count > 0
|
||||||
sb.Append(" Inner : ").AppendLine(exception.InnerException != null ? FormatInnerException(exception.InnerException) : "(null)");
|
? FormatExceptionData(exception.Data)
|
||||||
sb.Append(" Message : ").AppendLine(exception.Message);
|
: "(none)");
|
||||||
sb.Append(" Source : ").AppendLine(exception.Source);
|
sb.Append(" Inner : ").AppendLine(exception.InnerException != null
|
||||||
sb.Append(" StackTrace : ").AppendLine(exception.StackTrace);
|
? FormatInnerException(exception.InnerException)
|
||||||
sb.Append(" Line : ").AppendLine(fileLine.ToString());
|
: "(null)");
|
||||||
sb.Append(" File : ").AppendLine(filename);
|
sb.Append(" Message : ").AppendLine(exception.Message);
|
||||||
sb.Append(" ToString : ").AppendLine(exception.ToString());
|
sb.Append(" Source : ").AppendLine(exception.Source);
|
||||||
sb.AppendLine("---");
|
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();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatExceptionData(IDictionary data)
|
private static string FormatExceptionData(IDictionary data)
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
foreach (DictionaryEntry entry in data)
|
foreach (DictionaryEntry entry in data)
|
||||||
{
|
sb.Append(" | ")
|
||||||
sb.Append(" | ")
|
.Append(entry.Key)
|
||||||
.Append(entry.Key)
|
.Append(": ")
|
||||||
.Append(": ")
|
.AppendLine(entry.Value.ToString());
|
||||||
.AppendLine(entry.Value.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatInnerException(Exception innerException)
|
private static string FormatInnerException(Exception innerException)
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
sb.AppendLine(innerException.GetType().ToString())
|
sb.AppendLine(innerException.GetType().ToString())
|
||||||
.AppendLine(" Message : " + innerException.Message)
|
.AppendLine(" Message : " + innerException.Message)
|
||||||
.AppendLine(" Source : " + innerException.Source)
|
.AppendLine(" Source : " + innerException.Source)
|
||||||
.AppendLine(" StackTrace : " + innerException.StackTrace)
|
.AppendLine(" StackTrace : " + innerException.StackTrace)
|
||||||
.AppendLine(" ToString : " + innerException.ToString())
|
.AppendLine(" ToString : " + innerException)
|
||||||
.Append(" Data : ")
|
.Append(" Data : ")
|
||||||
.AppendLine(innerException.Data != null && innerException.Data.Count > 0 ? FormatExceptionData(innerException.Data) : "(none)");
|
.AppendLine(innerException.Data != null && innerException.Data.Count > 0
|
||||||
|
? FormatExceptionData(innerException.Data)
|
||||||
|
: "(none)");
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,261 +1,194 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
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.
|
private const int BufferSize = 4096;
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
||||||
|
|
||||||
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;
|
if (stream.CanSeek)
|
||||||
|
|
||||||
public OffsetStream(Stream stream, long offset = 0, long length = 0, bool readOnly = false, bool ownStream = false)
|
|
||||||
{
|
{
|
||||||
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;
|
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;
|
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)
|
CopyTo(stream, bufferSize);
|
||||||
{
|
}
|
||||||
throw new IOException("Stream is not seekable.");
|
finally
|
||||||
}
|
{
|
||||||
|
Position1 = previousPosition;
|
||||||
if (Length1 < bufferSize)
|
|
||||||
{
|
|
||||||
bufferSize = Convert.ToInt32(Length1);
|
|
||||||
}
|
|
||||||
|
|
||||||
long previousPosition = Position1;
|
|
||||||
Position1 = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CopyTo(stream, bufferSize);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Position1 = previousPosition;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,91 +1,77 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.Sockets;
|
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.
|
internal readonly object SendLock = new();
|
||||||
// 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 = 12201;
|
||||||
|
internal UdpClient Udp;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Syslog server.
|
/// Instantiate the object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GrayLogServer
|
public GrayLogServer()
|
||||||
{
|
{
|
||||||
/// <summary>
|
}
|
||||||
/// Hostname.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public string Hostname
|
/// 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
|
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
|
||||||
{
|
_Hostname = value;
|
||||||
return _Hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
SetUdp();
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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.Drawing;
|
||||||
using System.Globalization;
|
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;
|
||||||
|
|
||||||
|
case ConsoleColor.DarkBlue:
|
||||||
|
return HexStringToColor("#000080");
|
||||||
|
|
||||||
|
case ConsoleColor.DarkGreen:
|
||||||
|
return HexStringToColor("#008000");
|
||||||
|
|
||||||
|
case ConsoleColor.DarkCyan:
|
||||||
|
return HexStringToColor("#008080");
|
||||||
|
|
||||||
|
case ConsoleColor.DarkRed:
|
||||||
|
return HexStringToColor("#800000");
|
||||||
|
|
||||||
|
case ConsoleColor.DarkMagenta:
|
||||||
|
return HexStringToColor("#800080");
|
||||||
|
|
||||||
|
case ConsoleColor.DarkYellow:
|
||||||
|
return HexStringToColor("#808000");
|
||||||
|
|
||||||
|
case ConsoleColor.Gray:
|
||||||
|
return HexStringToColor("#C0C0C0");
|
||||||
|
|
||||||
|
case ConsoleColor.DarkGray:
|
||||||
|
return HexStringToColor("#808080");
|
||||||
|
|
||||||
|
case ConsoleColor.Blue:
|
||||||
|
return Color.Blue;
|
||||||
|
|
||||||
|
case ConsoleColor.Green:
|
||||||
|
return Color.Lime;
|
||||||
|
|
||||||
|
case ConsoleColor.Cyan:
|
||||||
|
return Color.Cyan;
|
||||||
|
|
||||||
|
case ConsoleColor.Red:
|
||||||
|
return Color.Red;
|
||||||
|
|
||||||
|
case ConsoleColor.Magenta:
|
||||||
|
return Color.Magenta;
|
||||||
|
|
||||||
|
case ConsoleColor.Yellow:
|
||||||
|
return Color.Yellow;
|
||||||
|
|
||||||
|
case ConsoleColor.White:
|
||||||
|
return Color.White;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string ColorToRGBString(Color c)
|
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)
|
||||||
{
|
{
|
||||||
return "RGB(" + c.R.ToString() + "," + c.G.ToString() + "," + c.B.ToString() + ")";
|
case 3: // RGB short hand
|
||||||
}
|
|
||||||
|
|
||||||
public static Color ConsoleColorToColor(this ConsoleColor consoleColor)
|
|
||||||
{
|
|
||||||
switch (consoleColor)
|
|
||||||
{
|
{
|
||||||
case ConsoleColor.Black:
|
return (defaultAlpha << 24)
|
||||||
return Color.Black;
|
| (value & 0xF)
|
||||||
|
| ((value & 0xF) << 4)
|
||||||
case ConsoleColor.DarkBlue:
|
| ((value & 0xF0) << 4)
|
||||||
return HexStringToColor("#000080");
|
| ((value & 0xF0) << 8)
|
||||||
|
| ((value & 0xF00) << 8)
|
||||||
case ConsoleColor.DarkGreen:
|
| ((value & 0xF00) << 12)
|
||||||
return HexStringToColor("#008000");
|
;
|
||||||
|
|
||||||
case ConsoleColor.DarkCyan:
|
|
||||||
return HexStringToColor("#008080");
|
|
||||||
|
|
||||||
case ConsoleColor.DarkRed:
|
|
||||||
return HexStringToColor("#800000");
|
|
||||||
|
|
||||||
case ConsoleColor.DarkMagenta:
|
|
||||||
return HexStringToColor("#800080");
|
|
||||||
|
|
||||||
case ConsoleColor.DarkYellow:
|
|
||||||
return HexStringToColor("#808000");
|
|
||||||
|
|
||||||
case ConsoleColor.Gray:
|
|
||||||
return HexStringToColor("#C0C0C0");
|
|
||||||
|
|
||||||
case ConsoleColor.DarkGray:
|
|
||||||
return HexStringToColor("#808080");
|
|
||||||
|
|
||||||
case ConsoleColor.Blue:
|
|
||||||
return Color.Blue;
|
|
||||||
|
|
||||||
case ConsoleColor.Green:
|
|
||||||
return Color.Lime;
|
|
||||||
|
|
||||||
case ConsoleColor.Cyan:
|
|
||||||
return Color.Cyan;
|
|
||||||
|
|
||||||
case ConsoleColor.Red:
|
|
||||||
return Color.Red;
|
|
||||||
|
|
||||||
case ConsoleColor.Magenta:
|
|
||||||
return Color.Magenta;
|
|
||||||
|
|
||||||
case ConsoleColor.Yellow:
|
|
||||||
return Color.Yellow;
|
|
||||||
|
|
||||||
case ConsoleColor.White:
|
|
||||||
return Color.White;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
}
|
||||||
}
|
case 4: // RGBA short hand
|
||||||
|
|
||||||
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));
|
// 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
|
||||||
if (!htmlColor.StartsWith("#") && requireHexSpecified)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Provided parameter '{htmlColor}' is not valid");
|
return (defaultAlpha << 24) | value;
|
||||||
}
|
}
|
||||||
|
case 8: // RGBA complete definition
|
||||||
htmlColor = htmlColor.TrimStart('#');
|
|
||||||
|
|
||||||
|
|
||||||
var symbolCount = htmlColor.Length;
|
|
||||||
var value = int.Parse(htmlColor, NumberStyles.HexNumber);
|
|
||||||
switch (symbolCount)
|
|
||||||
{
|
{
|
||||||
case 3: // RGB short hand
|
// Alpha swap
|
||||||
{
|
return ((value & 0xFF) << 24) | (value >> 8);
|
||||||
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 FormatException("Invalid HTML Color");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,53 +1,51 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
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.
|
static EnumHelper()
|
||||||
// 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()
|
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));
|
Names[values[i]] = names[i];
|
||||||
T[] values = (T[])Enum.GetValues(typeof(T));
|
Values[names[i]] = values[i];
|
||||||
|
Values[names[i].ToLower()] = values[i];
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
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.
|
/// <summary>
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
/// The color to use for critical messages.
|
||||||
|
/// </summary>
|
||||||
|
public ColorScheme Critical = new(ConsoleColor.DarkRed, ConsoleColor.Black);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Colors to use when writing to the console.
|
/// The color to use for debug messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ColorSchema
|
public ColorScheme Debug = new(ConsoleColor.Green, ConsoleColor.Black);
|
||||||
{
|
|
||||||
/// <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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Color scheme for logging messages.
|
/// The color to use for error messages.
|
||||||
/// </summary>
|
/// </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 = foreground;
|
||||||
/// Foreground color.
|
Background = background;
|
||||||
/// </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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
using System;
|
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.
|
void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null,
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
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(Exception exception, string module = null, string method = null, bool criticalException = false,
|
||||||
{
|
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null,
|
||||||
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");
|
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.
|
// 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.
|
// 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; }
|
if (string.IsNullOrWhiteSpace(currentMessage))
|
||||||
public string Message { get; set; }
|
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))
|
var prevForeground = Console.ForegroundColor;
|
||||||
return currentMessage;
|
var prevBackground = Console.BackgroundColor;
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]");
|
ConsoleColor foregroundColor;
|
||||||
|
ConsoleColor backgroundColor;
|
||||||
sb.Replace("{ts}", dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " + (settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]"))
|
switch (logType)
|
||||||
.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 prevForeground = Console.ForegroundColor;
|
case ELogType.DEBUG:
|
||||||
ConsoleColor prevBackground = Console.BackgroundColor;
|
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;
|
Console.ForegroundColor = foregroundColor;
|
||||||
ConsoleColor backgroundColor;
|
Console.BackgroundColor = backgroundColor;
|
||||||
switch (logType)
|
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:
|
version,
|
||||||
foregroundColor = settings.Colors.Debug.Foreground;
|
host = Environment.MachineName,
|
||||||
backgroundColor = settings.Colors.Debug.Background;
|
short_message = message,
|
||||||
break;
|
level = logLevel.ToGrayLogLevel(),
|
||||||
case ELogType.INFO:
|
facility,
|
||||||
foregroundColor = settings.Colors.Info.Foreground;
|
source,
|
||||||
backgroundColor = settings.Colors.Info.Background;
|
timestamp = DateTime.UtcNow.ToUnixTimestamp()
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.ForegroundColor = foregroundColor;
|
var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage));
|
||||||
Console.BackgroundColor = backgroundColor;
|
await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length,
|
||||||
Console.WriteLine(message);
|
new IPEndPoint(IPAddress.Parse(grayLogServer.Hostname), grayLogServer.Port));
|
||||||
Console.ForegroundColor = prevForeground;
|
|
||||||
Console.BackgroundColor = prevBackground;
|
|
||||||
}
|
}
|
||||||
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)
|
internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message,
|
||||||
{
|
bool sendToSyslogServers)
|
||||||
if (logger == null || settings == null || !settings.EnableFileLogging || string.IsNullOrWhiteSpace(message))
|
{
|
||||||
return;
|
if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message))
|
||||||
|
return;
|
||||||
|
|
||||||
LogLevel logLevel = logType.ToLogLevel();
|
foreach (var server in settings.SysLogServers ?? new List<SyslogServer> { new("127.0.0.1") })
|
||||||
|
try
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
settings.SplunkServers = new List<SplunkServer.SplunkServer>();
|
if (string.IsNullOrWhiteSpace(server.Hostname))
|
||||||
}
|
|
||||||
|
|
||||||
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}'" });
|
OnException?.Invoke(null,
|
||||||
|
new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" });
|
||||||
continue;
|
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)
|
var data = Encoding.UTF8.GetBytes(message);
|
||||||
{
|
await server.Udp.SendAsync(data, data.Length,
|
||||||
OnException?.Invoke(null, new ErrorMessage { Message = $"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}" });
|
new IPEndPoint(IPAddress.Parse(server.Hostname), server.Port));
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to Splunk Server '{splunkServer.SplunkHecUrl}': {exception.Message}" });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
catch (Exception exception)
|
||||||
|
|
||||||
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,
|
OnException?.Invoke(null,
|
||||||
EventData = message,
|
new ErrorMessage
|
||||||
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
|
|
||||||
{
|
{
|
||||||
version,
|
Exception = exception,
|
||||||
host = Environment.MachineName,
|
Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}"
|
||||||
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}" });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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 System;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -10,86 +7,139 @@ using EonaCat.Logger.EonaCatCoreLogger;
|
|||||||
using EonaCat.Logger.EonaCatCoreLogger.Extensions;
|
using EonaCat.Logger.EonaCatCoreLogger.Extensions;
|
||||||
using EonaCat.Logger.EonaCatCoreLogger.Models;
|
using EonaCat.Logger.EonaCatCoreLogger.Models;
|
||||||
using EonaCat.Logger.Extensions;
|
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.
|
private static LogManager _instance;
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
||||||
|
|
||||||
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;
|
if (string.IsNullOrEmpty(serverIp))
|
||||||
private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
throw new ArgumentNullException(nameof(serverIp));
|
||||||
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;
|
|
||||||
|
|
||||||
public bool IsRunning { get; private set; }
|
if (serverPort < 0)
|
||||||
|
throw new ArgumentException("Server port must be zero or greater.");
|
||||||
|
|
||||||
private static LogManager _instance;
|
settings.SysLogServers = new List<SyslogServer>
|
||||||
|
|
||||||
private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
public static LogManager Instance => InstanceInit();
|
|
||||||
|
|
||||||
public LoggerSettings Settings { get; set; } = CreateDefaultSettings();
|
|
||||||
|
|
||||||
private static LogManager InstanceInit()
|
|
||||||
{
|
{
|
||||||
if (_instance == null)
|
new(serverIp, serverPort)
|
||||||
{
|
};
|
||||||
_instance = new LogManager(CreateDefaultSettings());
|
|
||||||
}
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LoggerSettings CreateDefaultSettings()
|
Settings = settings;
|
||||||
{
|
SetupLogManager();
|
||||||
var settings = new LoggerSettings();
|
}
|
||||||
settings.Id = "EonaCatLogger";
|
|
||||||
settings.MaxLogType = ELogType.TRACE;
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual async Task DisposeAsync(bool disposing)
|
public LogManager(LoggerSettings settings)
|
||||||
{
|
{
|
||||||
if (disposing)
|
Settings = settings;
|
||||||
{
|
SetupFileLogger(settings);
|
||||||
await StopLoggingAsync().ConfigureAwait(false);
|
SetupLogManager();
|
||||||
}
|
LogHelper.OnException += LogHelper_OnException;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartNewLogAsync()
|
private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||||
{
|
public ILoggerProvider LoggerProvider { get; private set; }
|
||||||
if (_tokenSource.IsCancellationRequested)
|
public ILoggerFactory LoggerFactory { get; private set; }
|
||||||
{
|
public ILogger Logger { get; private set; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsRunning && CurrentDateTme.Date > _logDate.Date)
|
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider
|
||||||
{
|
? fileLoggerProvider.LogFile
|
||||||
await StopLoggingAsync().ConfigureAwait(false);
|
: 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()
|
public void Write(Exception exception, string module = null, string method = null, bool criticalException = false,
|
||||||
{
|
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null,
|
||||||
// Dispose of previous ServiceProvider if it exists
|
string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
|
||||||
(LoggerProvider as IDisposable)?.Dispose();
|
string grayLogSource = null, string grayLogVersion = "1.1")
|
||||||
(LoggerFactory as IDisposable)?.Dispose();
|
{
|
||||||
|
if (exception == null)
|
||||||
|
return;
|
||||||
|
|
||||||
IServiceCollection serviceCollection = new ServiceCollection();
|
Write(exception.FormatExceptionToMessage(module, method),
|
||||||
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MaxLogType.ToLogLevel()).AddEonaCatFileLogger(configuration =>
|
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;
|
var fileLoggerOptions = Settings.FileLoggerOptions;
|
||||||
configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries;
|
configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries;
|
||||||
@@ -104,166 +154,108 @@ namespace EonaCat.Logger.Managers
|
|||||||
configuration.UseLocalTime = Settings.UseLocalTime;
|
configuration.UseLocalTime = Settings.UseLocalTime;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
LoggerProvider = serviceProvider.GetService<ILoggerProvider>();
|
LoggerProvider = serviceProvider.GetService<ILoggerProvider>();
|
||||||
LoggerFactory = serviceProvider.GetService<ILoggerFactory>();
|
LoggerFactory = serviceProvider.GetService<ILoggerFactory>();
|
||||||
Logger = LoggerFactory.CreateLogger(Settings.Id);
|
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")
|
private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO,
|
||||||
{
|
bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null,
|
||||||
if (string.IsNullOrEmpty(message) || logType == ELogType.NONE || (int)logType >= (int)Settings.MaxLogType)
|
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 () =>
|
||||||
{
|
{
|
||||||
return;
|
if (sendToSyslogServersValue)
|
||||||
}
|
await LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true);
|
||||||
|
|
||||||
string messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime);
|
if (sendToSplunkServersValue)
|
||||||
bool writeToConsoleValue = writeToConsole ?? Settings.EnableConsole;
|
await LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(),
|
||||||
bool sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers;
|
messageWithHeader, true);
|
||||||
bool sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers;
|
|
||||||
bool sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers;
|
|
||||||
|
|
||||||
if (writeToConsoleValue)
|
if (sendToGrayLogServersValue)
|
||||||
{
|
await LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility,
|
||||||
LogHelper.SendToConsole(Settings, logType, messageWithHeader, true);
|
grayLogSource, true, grayLogVersion);
|
||||||
}
|
});
|
||||||
|
|
||||||
if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue)
|
var logMessage = new EonaCatLogMessage
|
||||||
{
|
|
||||||
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))
|
DateTime = dateTime,
|
||||||
throw new ArgumentNullException(nameof(serverIp));
|
Message = message,
|
||||||
|
LogType = logType,
|
||||||
|
Origin = string.IsNullOrWhiteSpace(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin
|
||||||
|
};
|
||||||
|
|
||||||
if (serverPort < 0)
|
Settings.OnLogEvent(logMessage);
|
||||||
throw new ArgumentException("Server port must be zero or greater.");
|
}
|
||||||
|
|
||||||
settings.SysLogServers = new List<SyslogServer>
|
public void Reset()
|
||||||
{
|
{
|
||||||
new SyslogServer(serverIp, serverPort)
|
Settings.ResetLogEvent();
|
||||||
};
|
}
|
||||||
|
|
||||||
Settings = settings;
|
private void LogHelper_OnException(object sender, ErrorMessage e)
|
||||||
SetupLogManager();
|
{
|
||||||
}
|
OnException?.Invoke(sender, e);
|
||||||
|
}
|
||||||
|
|
||||||
public LogManager(LoggerSettings settings)
|
private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true)
|
||||||
{
|
{
|
||||||
Settings = settings;
|
if (settings == null)
|
||||||
SetupFileLogger(settings, null, true);
|
settings = Settings;
|
||||||
SetupLogManager();
|
|
||||||
LogHelper.OnException += LogHelper_OnException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LogHelper_OnException(object sender, ErrorMessage e)
|
if (!settings.EnableFileLogging)
|
||||||
{
|
return;
|
||||||
OnException?.Invoke(sender, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true)
|
if (logFolder != null)
|
||||||
{
|
settings.FileLoggerOptions.LogDirectory = logFolder;
|
||||||
if (settings == null)
|
|
||||||
settings = Settings;
|
|
||||||
|
|
||||||
if (!settings.EnableFileLogging)
|
if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix))
|
||||||
return;
|
settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
if (logFolder != null)
|
private void SetupLogManager()
|
||||||
settings.FileLoggerOptions.LogDirectory = logFolder;
|
{
|
||||||
|
AppDomain.CurrentDomain.ProcessExit += ProcessExit;
|
||||||
|
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||||
|
_logDate = CurrentDateTme;
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix))
|
private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||||
settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty;
|
{
|
||||||
}
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
private void SetupLogManager()
|
private void ProcessExit(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
AppDomain.CurrentDomain.ProcessExit += ProcessExit;
|
Dispose();
|
||||||
Console.CancelKeyPress += Console_CancelKeyPress;
|
}
|
||||||
_logDate = CurrentDateTme;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
private Task StopLoggingAsync()
|
||||||
{
|
{
|
||||||
Dispose();
|
IsRunning = false;
|
||||||
}
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
private void ProcessExit(object sender, EventArgs e)
|
public void DeleteCurrentLogFile()
|
||||||
{
|
{
|
||||||
Dispose();
|
if (CurrentLogFile != null)
|
||||||
}
|
File.Delete(CurrentLogFile);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,192 +5,172 @@ using EonaCat.Logger.EonaCatCoreLogger.Models;
|
|||||||
using EonaCat.Logger.GrayLog;
|
using EonaCat.Logger.GrayLog;
|
||||||
using EonaCat.Logger.Syslog;
|
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.
|
public delegate void LogDelegate(EonaCatLogMessage message);
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
||||||
|
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>
|
/// <summary>
|
||||||
/// Logger settings.
|
/// Determines if we need to use the local time in the logging or UTC (default:false)
|
||||||
/// </summary>
|
/// </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;
|
get => _headerFormat;
|
||||||
public delegate void LogDelegate(EonaCatLogMessage message);
|
set
|
||||||
|
|
||||||
/// <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
|
if (string.IsNullOrEmpty(value)) _headerFormat = "";
|
||||||
{
|
else _headerFormat = value;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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;
|
namespace EonaCat.Logger.Splunk.Models;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace EonaCat.Logger.Splunk.Models
|
public class SplunkPayload
|
||||||
{
|
{
|
||||||
public class SplunkPayload
|
/// <summary>
|
||||||
{
|
/// Event data for splunk
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Event data for splunk
|
public string EventData { get; set; }
|
||||||
/// </summary>
|
|
||||||
public string EventData { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SourceType for splunk
|
/// SourceType for splunk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SourceType { get; set; }
|
public string SourceType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Host for splunk
|
/// Host for splunk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Host { get; set; }
|
public string Host { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,154 +1,129 @@
|
|||||||
using EonaCat.Json;
|
using System;
|
||||||
using EonaCat.Logger.Splunk.Models;
|
|
||||||
using System;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
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.
|
internal readonly object SendLock = new();
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Splunk Server.
|
/// Instantiate the object.
|
||||||
/// </summary>
|
/// </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 = splunkHecUrl;
|
||||||
/// SplunkHecUrl
|
SplunkHecToken = splunkHecToken;
|
||||||
/// </summary>
|
|
||||||
public string SplunkHecUrl
|
if (httpClientHandler == null) httpClientHandler = new HttpClientHandler();
|
||||||
|
|
||||||
|
SplunkClientHandler = httpClientHandler;
|
||||||
|
CreateHttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SplunkHecUrl
|
||||||
|
/// </summary>
|
||||||
|
public string SplunkHecUrl
|
||||||
|
{
|
||||||
|
get => _splunkHecUrl;
|
||||||
|
|
||||||
|
set
|
||||||
{
|
{
|
||||||
get
|
if (!string.IsNullOrWhiteSpace(_splunkHecUrl) && !_splunkHecUrl.ToLower().Contains("http"))
|
||||||
{
|
value = $"http://{value}";
|
||||||
return _splunkHecUrl;
|
_splunkHecUrl = value;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Sockets;
|
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.
|
private string _Hostname = "127.0.0.1";
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
private int _Port = 514;
|
||||||
|
|
||||||
|
internal UdpClient Udp;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Syslog server.
|
/// Instantiate the object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SyslogServer
|
public SyslogServer()
|
||||||
{
|
{
|
||||||
/// <summary>
|
}
|
||||||
/// Hostname.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public string Hostname
|
/// 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
|
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
|
||||||
{
|
_Hostname = value;
|
||||||
return _Hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
SetUdp();
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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.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; }
|
if (IsDisabled)
|
||||||
public static bool UseLocalTime { get; set; }
|
return;
|
||||||
private static LogManager LogManager;
|
|
||||||
public static string LogFolder => "Logs";
|
|
||||||
public static string CurrentLogFile => LogManager.CurrentLogFile;
|
|
||||||
public static bool IsDisabled { get; set; }
|
|
||||||
|
|
||||||
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)
|
stream = response.Body;
|
||||||
return;
|
|
||||||
|
|
||||||
LogManager.DeleteCurrentLogFile();
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
private static string ConvertToAbsolutePath(string path)
|
|
||||||
{
|
{
|
||||||
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)
|
if (acceptEncodingParts.Contains("br"))
|
||||||
{
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
string[] acceptEncodingParts = acceptEncoding.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
stream = response.Body;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Log(string message, ELogType logType = ELogType.INFO, bool writeToConsole = true)
|
await using (stream)
|
||||||
{
|
{
|
||||||
if (IsDisabled)
|
await oFS.CopyToAsync(stream).ConfigureAwait(false);
|
||||||
return;
|
|
||||||
|
|
||||||
LogManager.Write(message, logType, writeToConsole);
|
if (fS.Length > limit)
|
||||||
}
|
await stream.WriteAsync("\r\n####__EONACATLOGGER_TRUNCATED___####"u8.ToArray()).ConfigureAwait(false);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,27 +1,26 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
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)]
|
private readonly ILogger<ErrorModel> _logger;
|
||||||
[IgnoreAntiforgeryToken]
|
|
||||||
public class ErrorModel : PageModel
|
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)
|
public void OnGet()
|
||||||
{
|
{
|
||||||
_logger = logger;
|
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||||
}
|
|
||||||
|
|
||||||
public void OnGet()
|
|
||||||
{
|
|
||||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,17 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
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)
|
public void OnGet()
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnGet()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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)
|
public void OnGet()
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnGet()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,51 +1,51 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>@ViewData["Title"] - EonaCat.Logger.Web</title>
|
<title>@ViewData["Title"] - EonaCat.Logger.Web</title>
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
<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="~/css/site.css" asp-append-version="true"/>
|
||||||
<link rel="stylesheet" href="~/EonaCat.Logger.Web.styles.css" asp-append-version="true" />
|
<link rel="stylesheet" href="~/EonaCat.Logger.Web.styles.css" asp-append-version="true"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
<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">
|
|
||||||
<div class="container">
|
<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>
|
</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>
|
<footer class="border-top footer text-muted">
|
||||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
<div class="container">
|
||||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
© 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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -10,11 +10,11 @@ using EonaCat.Web.Tracer.Extensions;
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
FileLoggerOptions options = new FileLoggerOptions();
|
var options = new FileLoggerOptions();
|
||||||
options.MaxRolloverFiles = 5;
|
options.MaxRolloverFiles = 5;
|
||||||
options.FileSizeLimit = 1 * 1024 * 1024 / 4;
|
options.FileSizeLimit = 1 * 1024 * 1024 / 4;
|
||||||
options.UseLocalTime = true;
|
options.UseLocalTime = true;
|
||||||
builder.Logging.AddEonaCatFileLogger(fileLoggerOptions: options, filenamePrefix:"web");
|
builder.Logging.AddEonaCatFileLogger(fileLoggerOptions: options, filenamePrefix: "web");
|
||||||
|
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ rateOptions.AddDefaultConfiguration(config =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
RateLimiterMiddleware.OnLimitResponseCreated += RateLimiterMiddlewareOnLimitResponseCreatedAsync;
|
RateLimiterMiddleware.OnLimitResponseCreated += RateLimiterMiddlewareOnLimitResponseCreatedAsync;
|
||||||
|
|
||||||
async void RateLimiterMiddlewareOnLimitResponseCreatedAsync(object? sender, HttpContext httpContext)
|
async void RateLimiterMiddlewareOnLimitResponseCreatedAsync(object? sender, HttpContext httpContext)
|
||||||
{
|
{
|
||||||
await httpContext.Response.WriteAsync(" THIS IS MY CUSTOM RATE LIMIT MESSAGE").ConfigureAwait(false);
|
await httpContext.Response.WriteAsync(" THIS IS MY CUSTOM RATE LIMIT MESSAGE").ConfigureAwait(false);
|
||||||
@@ -61,7 +62,7 @@ builder.Services.AddMemoryCache();
|
|||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
Logger.UseLocalTime = true;
|
Logger.UseLocalTime = true;
|
||||||
Logger.MaxLogType = ELogType.TRACE;
|
Logger.MinLogType = ELogType.TRACE;
|
||||||
Logger.Configure();
|
Logger.Configure();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
@@ -83,6 +84,7 @@ if (!app.Environment.IsDevelopment())
|
|||||||
// await httpContext.Response.WriteAsync("THIS IS MY CUSTOM RATE LIMIT MESSAGE");
|
// await httpContext.Response.WriteAsync("THIS IS MY CUSTOM RATE LIMIT MESSAGE");
|
||||||
//}
|
//}
|
||||||
WebTracer.OnLog += WebTracer_OnLog;
|
WebTracer.OnLog += WebTracer_OnLog;
|
||||||
|
|
||||||
void WebTracer_OnLog(object? sender, string e)
|
void WebTracer_OnLog(object? sender, string e)
|
||||||
{
|
{
|
||||||
Console.WriteLine(e);
|
Console.WriteLine(e);
|
||||||
@@ -103,10 +105,10 @@ void RunLoggingExceptionTests()
|
|||||||
var loggerSettings = new LoggerSettings();
|
var loggerSettings = new LoggerSettings();
|
||||||
loggerSettings.FileLoggerOptions.UseLocalTime = true;
|
loggerSettings.FileLoggerOptions.UseLocalTime = true;
|
||||||
loggerSettings.UseLocalTime = true;
|
loggerSettings.UseLocalTime = true;
|
||||||
loggerSettings.MaxLogType = ELogType.TRACE;
|
loggerSettings.MinLogType = ELogType.INFO;
|
||||||
var logger = new LogManager(loggerSettings);
|
var logger = new LogManager(loggerSettings);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (var i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -117,19 +119,20 @@ void RunLoggingExceptionTests()
|
|||||||
logger.Write(exception);
|
logger.Write(exception);
|
||||||
Console.WriteLine($"Normal ExceptionLogged: {i}");
|
Console.WriteLine($"Normal ExceptionLogged: {i}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Delay(1);
|
Task.Delay(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(RunWebLoggerTests);
|
//Task.Run(RunWebLoggerTests);
|
||||||
Task.Run(RunWebLoggingTests);
|
//Task.Run(RunWebLoggingTests);
|
||||||
Task.Run(RunLoggingTests);
|
Task.Run(RunLoggingTests);
|
||||||
Task.Run(RunLoggingExceptionTests);
|
//Task.Run(RunLoggingExceptionTests);
|
||||||
Task.Run(RunWebLoggingExceptionTests);
|
//Task.Run(RunWebLoggingExceptionTests);
|
||||||
|
|
||||||
void RunWebLoggingExceptionTests()
|
void RunWebLoggingExceptionTests()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; i++)
|
for (var i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -145,18 +148,16 @@ void RunWebLoggingExceptionTests()
|
|||||||
app.Logger.LogInformation(exception, "INFORMATION");
|
app.Logger.LogInformation(exception, "INFORMATION");
|
||||||
Console.WriteLine($"WebExceptionLogged: {i}");
|
Console.WriteLine($"WebExceptionLogged: {i}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Delay(1);
|
Task.Delay(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunWebLoggingTests()
|
void RunWebLoggingTests()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(Logger.LogFolder))
|
if (!Directory.Exists(Logger.LogFolder)) Directory.CreateDirectory(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}");
|
app.Logger.LogInformation($"web-test {i}");
|
||||||
File.AppendAllText(Path.Combine(Logger.LogFolder, "test.log"), $"WebLogged: {i}{Environment.NewLine}");
|
File.AppendAllText(Path.Combine(Logger.LogFolder, "test.log"), $"WebLogged: {i}{Environment.NewLine}");
|
||||||
@@ -170,15 +171,15 @@ void RunLoggingTests()
|
|||||||
var loggerSettings = new LoggerSettings();
|
var loggerSettings = new LoggerSettings();
|
||||||
loggerSettings.UseLocalTime = true;
|
loggerSettings.UseLocalTime = true;
|
||||||
loggerSettings.FileLoggerOptions.UseLocalTime = true;
|
loggerSettings.FileLoggerOptions.UseLocalTime = true;
|
||||||
loggerSettings.MaxLogType = ELogType.TRACE;
|
loggerSettings.MinLogType = ELogType.INFO;
|
||||||
loggerSettings.FileLoggerOptions.FileSizeLimit = 1024 * 1024 * 1;
|
loggerSettings.FileLoggerOptions.FileSizeLimit = 1024 * 1024 * 1;
|
||||||
loggerSettings.FileLoggerOptions.FileNamePrefix = "AllTypes";
|
loggerSettings.FileLoggerOptions.FileNamePrefix = "AllTypes";
|
||||||
loggerSettings.FileLoggerOptions.MaxRolloverFiles = 5;
|
loggerSettings.FileLoggerOptions.MaxRolloverFiles = 5;
|
||||||
var logger = new LogManager(loggerSettings);
|
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} CRITICAL", ELogType.CRITICAL);
|
||||||
logger.Write($"test to file {i} DEBUG", ELogType.DEBUG);
|
logger.Write($"test to file {i} DEBUG", ELogType.DEBUG);
|
||||||
logger.Write($"test to file {i} ERROR", ELogType.ERROR);
|
logger.Write($"test to file {i} ERROR", ELogType.ERROR);
|
||||||
@@ -193,9 +194,9 @@ void RunLoggingTests()
|
|||||||
|
|
||||||
void RunWebLoggerTests()
|
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} CRITICAL", ELogType.CRITICAL);
|
||||||
Logger.Log($"test via logger {i} DEBUG", ELogType.DEBUG);
|
Logger.Log($"test via logger {i} DEBUG", ELogType.DEBUG);
|
||||||
Logger.Log($"test via logger {i} ERROR", ELogType.ERROR);
|
Logger.Log($"test via logger {i} ERROR", ELogType.ERROR);
|
||||||
|
|||||||
Reference in New Issue
Block a user