forked from amkovkov/GranuSightSoftware2
feat: add logs formatting
This commit is contained in:
@@ -0,0 +1,111 @@
|
|||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
|
||||||
|
namespace GSS2.Test;
|
||||||
|
|
||||||
|
public sealed class ConsoleFormatter : Microsoft.Extensions.Logging.Console.ConsoleFormatter
|
||||||
|
{
|
||||||
|
private static class Ansi
|
||||||
|
{
|
||||||
|
public const string Reset = "\x1b[0m";
|
||||||
|
|
||||||
|
public static string Color(ConsoleColor color) => color switch
|
||||||
|
{
|
||||||
|
ConsoleColor.Black => "\x1b[30m",
|
||||||
|
ConsoleColor.DarkRed => "\x1b[31m",
|
||||||
|
ConsoleColor.DarkGreen => "\x1b[32m",
|
||||||
|
ConsoleColor.DarkYellow => "\x1b[33m",
|
||||||
|
ConsoleColor.DarkBlue => "\x1b[34m",
|
||||||
|
ConsoleColor.DarkMagenta => "\x1b[35m",
|
||||||
|
ConsoleColor.DarkCyan => "\x1b[36m",
|
||||||
|
ConsoleColor.Gray => "\x1b[37m",
|
||||||
|
|
||||||
|
ConsoleColor.Red => "\x1b[91m",
|
||||||
|
ConsoleColor.Green => "\x1b[92m",
|
||||||
|
ConsoleColor.Yellow => "\x1b[93m",
|
||||||
|
ConsoleColor.Blue => "\x1b[94m",
|
||||||
|
ConsoleColor.Magenta => "\x1b[95m",
|
||||||
|
ConsoleColor.Cyan => "\x1b[96m",
|
||||||
|
ConsoleColor.White => "\x1b[97m",
|
||||||
|
|
||||||
|
_ => Reset
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConsoleFormatter() : base(nameof(ConsoleFormatter))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public override void Write<TState>(in LogEntry<TState> logEntry, IExternalScopeProvider? scopeProvider, TextWriter textWriter)
|
||||||
|
{
|
||||||
|
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||||
|
var level = logEntry.LogLevel switch
|
||||||
|
{
|
||||||
|
LogLevel.Trace => "TRACE",
|
||||||
|
LogLevel.Debug => "DEBUG",
|
||||||
|
LogLevel.Information => "INFO",
|
||||||
|
LogLevel.Warning => "WARN",
|
||||||
|
LogLevel.Error => "ERROR",
|
||||||
|
LogLevel.Critical => "CRIT",
|
||||||
|
_ => "NONE",
|
||||||
|
};
|
||||||
|
var category = logEntry.Category;
|
||||||
|
var eventId = logEntry.EventId.Id;
|
||||||
|
var eventName = logEntry.EventId.Name;
|
||||||
|
var message = logEntry.Formatter?.Invoke(logEntry.State, logEntry.Exception);
|
||||||
|
|
||||||
|
if (Console.IsOutputRedirected)
|
||||||
|
{
|
||||||
|
textWriter.WriteLine($"[{timestamp}] [{level}] [{category}] {message}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var levelColor = Ansi.Color(GetLogLevelColor(logEntry.LogLevel));
|
||||||
|
var categoryColor = Ansi.Color(ConsoleColor.Blue);
|
||||||
|
|
||||||
|
textWriter.Write('[');
|
||||||
|
textWriter.Write(timestamp);
|
||||||
|
textWriter.Write("] [");
|
||||||
|
textWriter.Write(levelColor);
|
||||||
|
textWriter.Write(level);
|
||||||
|
textWriter.Write(Ansi.Reset);
|
||||||
|
textWriter.Write("] [");
|
||||||
|
textWriter.Write(categoryColor);
|
||||||
|
textWriter.Write(category);
|
||||||
|
textWriter.Write(Ansi.Reset);
|
||||||
|
textWriter.Write("] [");
|
||||||
|
textWriter.Write(eventId);
|
||||||
|
textWriter.Write("] [");
|
||||||
|
textWriter.Write(eventName);
|
||||||
|
textWriter.Write("] ");
|
||||||
|
|
||||||
|
textWriter.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logEntry.Exception != null)
|
||||||
|
{
|
||||||
|
if (!Console.IsOutputRedirected)
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkRed;
|
||||||
|
|
||||||
|
textWriter.WriteLine(logEntry.Exception);
|
||||||
|
|
||||||
|
if (!Console.IsOutputRedirected)
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteColored(TextWriter writer, string text, ConsoleColor color)
|
||||||
|
{
|
||||||
|
var originalColor = Console.ForegroundColor;
|
||||||
|
Console.ForegroundColor = color;
|
||||||
|
writer.Write(text);
|
||||||
|
Console.ForegroundColor = originalColor;
|
||||||
|
}
|
||||||
|
private static ConsoleColor GetLogLevelColor(LogLevel level) => level switch
|
||||||
|
{
|
||||||
|
LogLevel.Trace => ConsoleColor.DarkGray,
|
||||||
|
LogLevel.Debug => ConsoleColor.Gray,
|
||||||
|
LogLevel.Warning => ConsoleColor.Yellow,
|
||||||
|
LogLevel.Error => ConsoleColor.Red,
|
||||||
|
LogLevel.Critical => ConsoleColor.DarkRed,
|
||||||
|
_ => Console.ForegroundColor
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
namespace GSS2.Test;
|
||||||
|
|
||||||
|
public sealed class FileLogger : ILogger
|
||||||
|
{
|
||||||
|
private readonly string _category;
|
||||||
|
private readonly string _filePath;
|
||||||
|
private static readonly object _lock = new();
|
||||||
|
|
||||||
|
public FileLogger(string category, string filePath)
|
||||||
|
{
|
||||||
|
_category = category;
|
||||||
|
_filePath = filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS8633 // Nullability in constraints for type parameter doesn't match the constraints for type parameter in implicitly implemented interface method'.
|
||||||
|
public IDisposable? BeginScope<TState>(TState state) => null;
|
||||||
|
#pragma warning restore CS8633 // Nullability in constraints for type parameter doesn't match the constraints for type parameter in implicitly implemented interface method'.
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel) => true;
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||||
|
{
|
||||||
|
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||||
|
var level = logLevel switch
|
||||||
|
{
|
||||||
|
LogLevel.Trace => "TRACE",
|
||||||
|
LogLevel.Debug => "DEBUG",
|
||||||
|
LogLevel.Information => "INFO",
|
||||||
|
LogLevel.Warning => "WARN",
|
||||||
|
LogLevel.Error => "ERROR",
|
||||||
|
LogLevel.Critical => "CRIT",
|
||||||
|
_ => "NONE",
|
||||||
|
};
|
||||||
|
var message = formatter(state, exception);
|
||||||
|
|
||||||
|
var line = $"[{timestamp}] [{level}] [{_category}] [{eventId.Id}] [{eventId.Name}] {message}";
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
File.AppendAllText(_filePath, line + Environment.NewLine);
|
||||||
|
|
||||||
|
if (exception != null)
|
||||||
|
File.AppendAllText(_filePath, exception + Environment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace GSS2.Test;
|
||||||
|
|
||||||
|
public sealed class FileLoggerProvider : ILoggerProvider
|
||||||
|
{
|
||||||
|
private readonly string _filePath;
|
||||||
|
|
||||||
|
public FileLoggerProvider(string filePath) => _filePath = filePath;
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName) => new FileLogger(categoryName, _filePath);
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
using LibCameraSharp;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace GSS2.Test;
|
||||||
|
|
||||||
|
public sealed class LibCameraLogSink : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly ILogger _loggerCamera;
|
||||||
|
private readonly ILogger _loggerRPI;
|
||||||
|
private readonly ILogger _loggerDeviceEnumerator;
|
||||||
|
private readonly ILogger _loggerIPAProxy;
|
||||||
|
private readonly ILogger _loggerWRAPPER;
|
||||||
|
private bool _disposedValue;
|
||||||
|
|
||||||
|
public LibCameraLogSink(ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_logger = loggerFactory.CreateLogger("LibCamera");
|
||||||
|
_loggerCamera = loggerFactory.CreateLogger("LibCamera-Camera");
|
||||||
|
_loggerRPI = loggerFactory.CreateLogger("LibCamera-RPI");
|
||||||
|
_loggerDeviceEnumerator = loggerFactory.CreateLogger("LibCamera-DeviceEnumerator");
|
||||||
|
_loggerIPAProxy = loggerFactory.CreateLogger("LibCamera-IPAProxy");
|
||||||
|
_loggerWRAPPER = loggerFactory.CreateLogger("LibCamera-WRAPPER");
|
||||||
|
Log.Initialize();
|
||||||
|
Log.SetLevel("Camera", Log.LogLevelEnum.DEBUG);
|
||||||
|
Log.SetLevel("RPI", Log.LogLevelEnum.DEBUG);
|
||||||
|
Log.SetLevel("DeviceEnumerator", Log.LogLevelEnum.DEBUG);
|
||||||
|
Log.SetLevel("IPAProxy", Log.LogLevelEnum.DEBUG);
|
||||||
|
Log.SetLevel("WRAPPER", Log.LogLevelEnum.DEBUG);
|
||||||
|
Log.LogReceived += OnLogReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLogReceived(object? sender, Log.LogEventArgs e)
|
||||||
|
{
|
||||||
|
if (_disposedValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var level = MapLogLevel(e.Level);
|
||||||
|
|
||||||
|
var logger = e.Category switch
|
||||||
|
{
|
||||||
|
"Camera" => _loggerCamera,
|
||||||
|
"RPI" => _loggerRPI,
|
||||||
|
"DeviceEnumerator" => _loggerDeviceEnumerator,
|
||||||
|
"IPAProxy" => _loggerIPAProxy,
|
||||||
|
"WRAPPER" => _loggerWRAPPER,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (logger is not null && logger.IsEnabled(level))
|
||||||
|
logger.Log(level, new EventId(0, $"{e.ThreadId} {e.File}"), e, null, static (state, _) => $"{state.Message}");
|
||||||
|
else if (logger is null && _logger.IsEnabled(level))
|
||||||
|
_logger.Log(level, new EventId(0, $"{e.ThreadId} {e.File}"), e, null, static (state, _) => $"[{state.Category}] {state.Message}");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
private static LogLevel MapLogLevel(Log.LogLevelEnum level) => level switch
|
||||||
|
{
|
||||||
|
Log.LogLevelEnum.DEBUG => LogLevel.Debug,
|
||||||
|
Log.LogLevelEnum.INFO => LogLevel.Information,
|
||||||
|
Log.LogLevelEnum.WARN => LogLevel.Warning,
|
||||||
|
Log.LogLevelEnum.ERROR => LogLevel.Error,
|
||||||
|
Log.LogLevelEnum.FATAL => LogLevel.Critical,
|
||||||
|
_ => LogLevel.None
|
||||||
|
};
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
Log.LogReceived -= OnLogReceived;
|
||||||
|
}
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using GSS2.Core;
|
using GSS2.Core;
|
||||||
using GSS2.Core.Analysis.AmineContent.Database;
|
using GSS2.Core.Analysis.AmineContent.Database;
|
||||||
|
using Microsoft.Extensions.Logging.Console;
|
||||||
|
|
||||||
namespace GSS2.Test;
|
namespace GSS2.Test;
|
||||||
|
|
||||||
@@ -10,6 +11,12 @@ public class Program
|
|||||||
{
|
{
|
||||||
var builder = Host.CreateApplicationBuilder(args);
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
|
|
||||||
|
builder.Logging.ClearProviders();
|
||||||
|
builder.Logging.AddConsole(options => options.FormatterName = nameof(ConsoleFormatter));
|
||||||
|
builder.Logging.AddConsoleFormatter<ConsoleFormatter, ConsoleFormatterOptions>();
|
||||||
|
builder.Logging.AddProvider(new FileLoggerProvider("app.log"));
|
||||||
|
builder.Services.AddSingleton<LibCameraLogSink>();
|
||||||
|
|
||||||
builder.Services.AddAmineContentCalibrationContext(builder.Configuration);
|
builder.Services.AddAmineContentCalibrationContext(builder.Configuration);
|
||||||
builder.Services.AddAmineContentResultsContext(builder.Configuration);
|
builder.Services.AddAmineContentResultsContext(builder.Configuration);
|
||||||
|
|
||||||
@@ -21,6 +28,7 @@ public class Program
|
|||||||
builder.Services.AddHostedService<Worker>();
|
builder.Services.AddHostedService<Worker>();
|
||||||
|
|
||||||
var host = builder.Build();
|
var host = builder.Build();
|
||||||
|
host.Services.GetRequiredService<LibCameraLogSink>();
|
||||||
|
|
||||||
using (var scope = host.Services.CreateScope())
|
using (var scope = host.Services.CreateScope())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,18 @@
|
|||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
"Microsoft.Hosting.Lifetime": "Information",
|
||||||
|
"Microsoft.EntityFrameworkCore.Migrations": "Warning",
|
||||||
|
"Microsoft.EntityFrameworkCore.Database.Command": "Warning",
|
||||||
|
"GSS2.Core.Hardware.LightsService": "Information",
|
||||||
|
"GSS2.Core.Hardware.IlluminatorService":"Information",
|
||||||
|
"GSS2.Core.Hardware.CameraService":"Information",
|
||||||
|
"LibCamera": "Information",
|
||||||
|
"LibCamera-Camera": "Information",
|
||||||
|
"LibCamera-RPI": "Information",
|
||||||
|
"LibCamera-DeviceEnumerator": "Information",
|
||||||
|
"LibCamera-IPAProxy": "Information",
|
||||||
|
"LibCamera-WRAPPER": "Information"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
|
|||||||
Reference in New Issue
Block a user