Files
Deal/src/core/Deal.Api/Logging/DealLogging.cs
T
Rustam Khalimov 79c931d88e Почистить комментарии от ссылок на ТЗ и обрывков
Удаление целых //-блоков со ссылками (Task/Ruling/этап/ТЗ/§/
дизайн-док/api-map/python/прототип) вместо построчного вырезания —
без обрывков фраз; снят боилерплейт <param>/<returns>; то же в
.proto.
2026-09-11 13:49:26 +03:00

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;
}