У меня есть собственный 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
Форматирование даты, DateTime, DateTimeOffset в ILogger ⇐ C#
Место общения программистов C#
-
Anonymous
1728919414
Anonymous
У меня есть собственный ILoggerProvider. Реализация не важна. Моя проблема заключается в том, что любые параметры Date, DateTime, DateTimeOffset, которые передаются в ILogger, преобразуются внутренним классом Microsoft.Logging.Extensions.LogValuesFormatter в строку с использованием [b]InvariantCulture[/b] вместо [b]. >Текущая культура[/b] приложения. Мы находимся в Великобритании и используем дд/ММ/гггг, а не ММ/дд/гггг (инвариант). Даже гггг-ММ-дд гораздо предпочтительнее, чем формат, ориентированный на США.
Проблема проявляется в методе 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;
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79086350/formatting-of-date-datetime-datetimeoffset-in-ilogger[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия