Форматирование даты, DateTime, DateTimeOffset в ILoggerC#

Место общения программистов C#
Ответить
Anonymous
 Форматирование даты, DateTime, DateTimeOffset в ILogger

Сообщение Anonymous »

У меня есть собственный ILoggerProvider. Реализация не важна. Моя проблема заключается в том, что любые параметры Date, DateTime, DateTimeOffset, которые передаются в ILogger, преобразуются внутренним классом Microsoft.Logging.Extensions.LogValuesFormatter в строку с использованием InvariantCulture вместо . >Текущая культура приложения. Мы находимся в Великобритании и используем дд/ММ/гггг, а не ММ/дд/гггг (инвариант). Даже гггг-ММ-дд гораздо предпочтительнее, чем формат, ориентированный на США.
Проблема проявляется в методе void Log приведенного ниже примера, где состояние TState — это внутренний класс Microsoft.Logging.Extensions.LogValuesFormatter, который формат не может взломать — только передайте его в предоставленный «форматер». Есть ли способ предоставить альтернативу классу LogValuesFormatter? Те провайдеры, которые ведут журналирование расширенных параметров, делают это, поэтому я смогу сделать это с помощью нашего собственного средства ведения журнала.
Мне было предложено посмотреть, как ILogger обращается к аргументам параметров в функции Log< TState>(LogLevel logLevel как решение, но все эти решения имеют серьезные недостатки: а) использует отражение (медленно и не работает с .net7+), б) приводит состояние к IReadOnlyList) (не работает, потому что класс является внутренним), в) сериализовать и десериализовать состояние в/из JSON (невероятно неэффективно и только доводит вас до определенного момента) или г) реализовать свой собственный метод расширения LogInformation (требуется перекодирование сотен журналов записи в различных приложениях нижнего уровня) и нарушает переносимость с помощью общих функций ILogger.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Text;

namespace XXXXX.Base;

///
/// Custom ILoggerProvider, writes logs to text files with the option to cycle the filename daily.
/// Daily File change is checked and handled by a call to CustomFileLoggerProvider.CheckTimeToChangeFile()
/// which for efficiency should be done via an occasional background worker or a Scoped Page Request (middleware)
///
[ProviderAlias("CustomFile")]
public sealed class CustomFileLoggerProvider : ILoggerProvider
{
private readonly ConcurrentDictionary _loggers = new(StringComparer.OrdinalIgnoreCase);
private CustomFileLoggerConfiguration _currentConfig;
private readonly IDisposable? _onChangeToken;
private readonly object _lock = new();
private StreamWriter? _logFileWriter;
private bool _stopLogging = false;

///
/// If messages come in prior to initialisation via the first request (middleware) then stores values here
///
private readonly List _initialCache = new(32);

public CustomFileLoggerProvider(IOptionsMonitor config)
{
_currentConfig = config.CurrentValue;
_onChangeToken = config.OnChange(config => _currentConfig = config);
}

public void Flush()
{
_logFileWriter?.Flush();
}

public void Write(string text)
{
if (_stopLogging)
return;

lock (_lock)
{
if (_logFileWriter is null)
{
OpenLogFile();
if (_logFileWriter is null)
{
if (_initialCache.Count < 100)
_initialCache.Add(text);
else
_stopLogging = true;
}
}
if (_logFileWriter is not null)
{
_logFileWriter.WriteLine(text);
_logFileWriter.Flush();
}
}
}

public ILogger CreateLogger(string categoryName) =>
_loggers.GetOrAdd(categoryName, name => new CustomFileLogger(categoryName, Write));

///
/// Opens a new log file. This should be called while under lock.
///
private bool OpenLogFile()
{
var filename = _currentConfig.LogFile;
if (filename is null)
return false;

try
{
_logFileWriter = new StreamWriter(filename, append: true, encoding: Encoding.UTF8);
_logFileWriter.WriteLine();
_logFileWriter.WriteLine();
_logFileWriter.WriteLine($"======== {DateTime.UtcNow:dd/MM/yyyy HH:mm:ss} ========");
_logFileWriter.Flush();
_stopLogging = false;
return true;
}
catch (IOException ex)
{
...
}
}

///
/// Closes the current log file. This should be called while under lock.
///
private void CloseLogFile(string reason)
{
if (_logFileWriter is not null)
{
if (!string.IsNullOrEmpty(reason))
_logFileWriter.WriteLine(reason);
_logFileWriter.WriteLine("======== END ========");
_logFileWriter.Flush();
_logFileWriter.Dispose();
_logFileWriter = null;
}
}

public void Dispose()
{
lock (_lock)
{
CloseLogFile("Closing Log File due to Provider being disposed (e.g. application shutdown)");
_loggers.Clear();
_onChangeToken?.Dispose();
}
}
}

///
/// Custom ILogger, writes logs to text files
///
public sealed class CustomFileLogger(string categoryName, Action writeFunc) : ILogger
{
private readonly string _categoryName = categoryName;
private readonly Action _writeFunc = writeFunc;

public IDisposable BeginScope(TState state) where TState : notnull => default!;

public bool IsEnabled(LogLevel logLevel) =>
// Ensure that only information level and higher logs are recorded
logLevel >= LogLevel.Information;

public void Log(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func formatter)
{
// Ensure that only information level and higher logs are recorded
if (!IsEnabled(logLevel))
return;

// Get the formatted log message
var message = formatter(state, exception);

//var now = DateTimeOffset.Now;

//Write log messages to text file
_writeFunc($"{LL(logLevel)} [{_categoryName}] {message}");
}

private static string LL(LogLevel l) => l switch
{
LogLevel.Information => "INF",
LogLevel.Trace => "TRC",
LogLevel.Debug => "DBG",
LogLevel.Warning => "WRN",
LogLevel.Error => "ERR",
LogLevel.Critical => "CRITICAL",
LogLevel.None => "",
_ => throw new ArgumentOutOfRangeException(nameof(l))
};
}

///
/// By convention, extension methods on ILoggingBuilder are used to register the custom provider
///
public static class CustomFileLoggerExtensions
{
public static ILoggingBuilder AddCustomFileLogger(this ILoggingBuilder builder)
{
builder.AddConfiguration();
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton());
LoggerProviderOptions.RegisterProviderOptions(builder.Services);
return builder;
}

public static ILoggingBuilder AddCustomFileLogger(this ILoggingBuilder builder, Action configure)
{
builder.AddCustomFileLogger();
builder.Services.Configure(configure);
return builder;
}
}


Подробнее здесь: https://stackoverflow.com/questions/790 ... in-ilogger
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»