Убрать неиспользуемые using по код-стайлу

Прогон dotnet format (IDE0005) по 4 решениям: удалены лишние using,
оставшиеся после миграции namespace (676 файлов).
This commit is contained in:
Rustam Khalimov
2026-09-11 13:30:57 +03:00
parent 5bfa92a4ac
commit e8b9fab860
675 changed files with 47248 additions and 49911 deletions
+123 -126
View File
@@ -1,126 +1,123 @@
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Events;
using Serilog.Formatting.Compact;
using Deal.Api.Services;
using Deal.Api.Dtos;
namespace Deal.Api.Logging;
// Serilog-конфигурация процесса core (Ruling 7/9, план Task 14).
// Все четыре процесса (core + telegram/ai/ml-сервисы) логируют через Serilog: консоль — JSON в
// prod-стиле (CompactJsonFormatter: одна JSON-строка на событие, поля @t/@mt/@l — парсинг
// Loki/Promtail) либо текст в Development; плюс rolling-файл data/logs/deal-<процесс>.json
// (под ContentRoot; у core каталог data смонтирован volume-ом, compose.prod). Уровень/каталог
// переопределяются env: DEAL_LOG_LEVEL, DEAL_LOGS_DIR.
// Конфигурация кодом, а не секцией appsettings: у трёх сервисов appsettings.json нет (весь конфиг —
// env, Ruling 13), поэтому единый для четырёх хостов код-набор с env-переопределениями дешевле и не
// расходится между процессами. Правило «секреты не логируются» (Ruling 13) соблюдается на уровне
// сообщений (в лог-конфигурации секретов нет; запрос-логи Task 14 логируют метод/путь/статус без
// query/заголовков/тел). OTel-метрики/трейсы и Prometheus в этапе 7 не добавляются (Ruling 7) —
// стек: Serilog-логи → docker-логи → Promtail → Loki → Grafana.
// Вызов — из Program.cs процесса (entry point): DealLogging.Configure(builder, "…") ДО
// builder.Build(). Интеграционные тесты сервисов поднимают хост через *ServiceHost.Create
// БЕЗ этого вызова (конфигурация логирования — забота production-точки входа), поэтому тесты не пишут
// файлы-логи и не меняют своё логирование.
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 к хосту (builder.Host.UseSerilog). Регистрация отложенная: конфигурация
/// логгера применяется при <c>builder.Build()</c>, когда среда/конфигурация (env) уже собраны.
/// </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())
{
// Dev: читаемый текст в консоли (Ruling 7: «dev можно текст»); файл — всегда JSON.
loggerConfiguration.WriteTo.Console(outputTemplate: DevelopmentConsoleTemplate);
}
else
{
// Prod-стиль: одна JSON-строка на событие — docker-логи собирает Promtail (Ruling 7/9).
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;
}
using Serilog;
using Serilog.Events;
using Serilog.Formatting.Compact;
namespace Deal.Api.Logging;
// Serilog-конфигурация процесса core (Ruling 7/9, план Task 14).
// Все четыре процесса (core + telegram/ai/ml-сервисы) логируют через Serilog: консоль — JSON в
// prod-стиле (CompactJsonFormatter: одна JSON-строка на событие, поля @t/@mt/@l — парсинг
// Loki/Promtail) либо текст в Development; плюс rolling-файл data/logs/deal-&lt;процесс&gt;.json
// (под ContentRoot; у core каталог data смонтирован volume-ом, compose.prod). Уровень/каталог
// переопределяются env: DEAL_LOG_LEVEL, DEAL_LOGS_DIR.
// Конфигурация кодом, а не секцией appsettings: у трёх сервисов appsettings.json нет (весь конфиг —
// env, Ruling 13), поэтому единый для четырёх хостов код-набор с env-переопределениями дешевле и не
// расходится между процессами. Правило «секреты не логируются» (Ruling 13) соблюдается на уровне
// сообщений (в лог-конфигурации секретов нет; запрос-логи Task 14 логируют метод/путь/статус без
// query/заголовков/тел). OTel-метрики/трейсы и Prometheus в этапе 7 не добавляются (Ruling 7) —
// стек: Serilog-логи → docker-логи → Promtail → Loki → Grafana.
// Вызов — из Program.cs процесса (entry point): DealLogging.Configure(builder, "…") ДО
// builder.Build(). Интеграционные тесты сервисов поднимают хост через *ServiceHost.Create
// БЕЗ этого вызова (конфигурация логирования — забота production-точки входа), поэтому тесты не пишут
// файлы-логи и не меняют своё логирование.
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 к хосту (builder.Host.UseSerilog). Регистрация отложенная: конфигурация
/// логгера применяется при <c>builder.Build()</c>, когда среда/конфигурация (env) уже собраны.
/// </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())
{
// Dev: читаемый текст в консоли (Ruling 7: «dev можно текст»); файл — всегда JSON.
loggerConfiguration.WriteTo.Console(outputTemplate: DevelopmentConsoleTemplate);
}
else
{
// Prod-стиль: одна JSON-строка на событие — docker-логи собирает Promtail (Ruling 7/9).
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;
}