Удаление целых //-блоков со ссылками (Task/Ruling/этап/ТЗ/§/ дизайн-док/api-map/python/прототип) вместо построчного вырезания — без обрывков фраз; снят боилерплейт <param>/<returns>; то же в .proto.
105 lines
5.5 KiB
C#
105 lines
5.5 KiB
C#
using Serilog;
|
|
using Serilog.Events;
|
|
using Serilog.Formatting.Compact;
|
|
|
|
namespace Deal.Api.Logging;
|
|
|
|
internal static class DealLogging
|
|
{
|
|
// Env-ключ минимального уровня Serilog (Debug/Information/Warning/Error; дефолт Information).
|
|
private const string MinimumLevelEnvKey = "DEAL_LOG_LEVEL";
|
|
|
|
// Env-ключ каталога rolling-файлов (дефолт data/logs под ContentRoot).
|
|
private const string LogsDirectoryEnvKey = "DEAL_LOGS_DIR";
|
|
|
|
// Каталог логов по умолчанию (относительно ContentRoot): core — внутри volume /app/data.
|
|
private const string DefaultLogsSubdirectory = "data/logs";
|
|
|
|
// Шаблон имени rolling-файла (Serilog добавляет дату перед расширением): deal-core-20260908.json.
|
|
private const string LogFileNameTemplate = "deal-{0}-.json";
|
|
|
|
// Сколько rolling-файлов хранится (суток); старшие удаляются Serilog автоматически.
|
|
private const int RetainedFileCount = 30;
|
|
|
|
// Текстовая разметка консоли в Development (цвета — дефолтной темой Serilog).
|
|
private const string DevelopmentConsoleTemplate =
|
|
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}";
|
|
|
|
// Категория EF Core: команды SQL логируются на Warning+ (шум запросов не попадает в Loki).
|
|
private const string EntityFrameworkCoreCategory = "Microsoft.EntityFrameworkCore";
|
|
|
|
// Категория Grpc.AspNetCore: не ниже Information (внутренние Debug-события вызовов не дублируют access-лог).
|
|
private const string GrpcCategory = "Grpc";
|
|
|
|
// Дефолтный уровень при пустом/невалидном env DEAL_LOG_LEVEL.
|
|
private const LogEventLevel DefaultMinimumLevel = LogEventLevel.Information;
|
|
|
|
/// <summary>
|
|
/// Подключает Serilog к хосту
|
|
/// </summary>
|
|
/// <param name="builder">Билдер WebApplication процесса (до Build).</param>
|
|
/// <param name="processName">Имя процесса для имени файла-лога (core/telegram/ai/ml).</param>
|
|
public static void Configure(WebApplicationBuilder builder, string processName)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(builder);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(processName);
|
|
builder.Host.UseSerilog((context, loggerConfiguration) =>
|
|
Apply(loggerConfiguration, context.HostingEnvironment, context.Configuration, processName));
|
|
}
|
|
|
|
// Собирает LoggerConfiguration процесса: уровень/фильтры, rolling-файл, консоль.
|
|
// loggerConfiguration: Конфигурация Serilog (до CreateLogger).
|
|
// environment: Окружение хоста (Development — текстовая консоль).
|
|
// configuration: Конфигурация хоста (env DEAL_LOG_*).
|
|
// processName: Имя процесса (суффикс имени rolling-файла).
|
|
private static void Apply(
|
|
LoggerConfiguration loggerConfiguration,
|
|
IHostEnvironment environment,
|
|
IConfiguration configuration,
|
|
string processName)
|
|
{
|
|
loggerConfiguration
|
|
.MinimumLevel.Is(ParseMinimumLevel(configuration[MinimumLevelEnvKey]))
|
|
.MinimumLevel.Override(EntityFrameworkCoreCategory, LogEventLevel.Warning)
|
|
.MinimumLevel.Override(GrpcCategory, LogEventLevel.Information)
|
|
.Enrich.FromLogContext();
|
|
|
|
string logsDirectory = ResolveLogsDirectory(environment.ContentRootPath, configuration[LogsDirectoryEnvKey]);
|
|
Directory.CreateDirectory(logsDirectory);
|
|
string logFilePath = Path.Combine(
|
|
logsDirectory,
|
|
string.Format(LogFileNameTemplate, processName));
|
|
loggerConfiguration.WriteTo.File(
|
|
new CompactJsonFormatter(),
|
|
logFilePath,
|
|
rollingInterval: RollingInterval.Day,
|
|
retainedFileCountLimit: RetainedFileCount);
|
|
|
|
if (environment.IsDevelopment())
|
|
{
|
|
loggerConfiguration.WriteTo.Console(outputTemplate: DevelopmentConsoleTemplate);
|
|
}
|
|
else
|
|
{
|
|
loggerConfiguration.WriteTo.Console(new CompactJsonFormatter());
|
|
}
|
|
}
|
|
|
|
// Каталог rolling-файлов: env DEAL_LOGS_DIR либо data/logs под ContentRoot процесса.
|
|
// contentRootPath: ContentRoot хоста (/app в контейнере).
|
|
// configuredDirectory: Значение env DEAL_LOGS_DIR (null/пусто — дефолт).
|
|
// Возвращает: Абсолютный путь каталога логов.
|
|
private static string ResolveLogsDirectory(string contentRootPath, string? configuredDirectory)
|
|
=> string.IsNullOrWhiteSpace(configuredDirectory)
|
|
? Path.Combine(contentRootPath, DefaultLogsSubdirectory)
|
|
: configuredDirectory.Trim();
|
|
|
|
// Разбирает env DEAL_LOG_LEVEL; пустое/невалидное значение — DefaultMinimumLevel.
|
|
// rawValue: Сырое значение env.
|
|
// Возвращает: Уровень Serilog.
|
|
private static LogEventLevel ParseMinimumLevel(string? rawValue)
|
|
=> Enum.TryParse(rawValue, ignoreCase: true, out LogEventLevel parsedLevel)
|
|
? parsedLevel
|
|
: DefaultMinimumLevel;
|
|
}
|