diff --git a/SfeduSchedule/Controllers/ProxyController.cs b/SfeduSchedule/Controllers/ProxyController.cs index 598f80e..c4f4b75 100644 --- a/SfeduSchedule/Controllers/ProxyController.cs +++ b/SfeduSchedule/Controllers/ProxyController.cs @@ -11,7 +11,7 @@ namespace SfeduSchedule.Controllers; [ApiController] [Route("api/proxy")] [EnableRateLimiting("throttle")] -public class ProxyController(ModeusService modeusService, ILogger logger) : ControllerBase +public class ProxyController(ModeusService modeusService, ILogger logger) : ControllerBase { /// /// Получить расписание по пользовательскому запросу. diff --git a/SfeduSchedule/Middleware/CorrelationIdMiddleware.cs b/SfeduSchedule/Middleware/CorrelationIdMiddleware.cs new file mode 100644 index 0000000..b4c9031 --- /dev/null +++ b/SfeduSchedule/Middleware/CorrelationIdMiddleware.cs @@ -0,0 +1,34 @@ +using System.Diagnostics; + +namespace SfeduSchedule.Middleware; + +/// +/// Middleware для добавления и обработки Correlation ID в HTTP запросах. +/// Нужно для трассировки запросов. +/// +public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger logger) +{ + private const string HeaderName = "X-Correlation-ID"; + + public async Task InvokeAsync(HttpContext context) + { + // 1. Берём из заголовка, если клиент прислал + if (!context.Request.Headers.TryGetValue(HeaderName, out var correlationId) || + string.IsNullOrWhiteSpace(correlationId)) + { + // 2. Иначе используем Activity TraceId или TraceIdentifier + var activityId = Activity.Current?.TraceId.ToString(); + correlationId = !string.IsNullOrEmpty(activityId) + ? activityId + : context.TraceIdentifier; + } + + // Положим в Items, чтобы можно было достать из сервисов + // context.Items[HeaderName] = correlationId.ToString(); + + // 3. Прокинем в ответ + context.Response.Headers[HeaderName] = correlationId.ToString(); + + await next(context); + } +} diff --git a/SfeduSchedule/Program.cs b/SfeduSchedule/Program.cs index a9d7fb4..ecf9c74 100644 --- a/SfeduSchedule/Program.cs +++ b/SfeduSchedule/Program.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Diagnostics; using System.Reflection; using System.Threading.RateLimiting; using Microsoft.AspNetCore.Authentication; @@ -15,10 +16,13 @@ using SfeduSchedule.Auth; using SfeduSchedule.Jobs; using SfeduSchedule.Middleware; using SfeduSchedule.Services; +using SfeduSchedule.Logging; using X.Extensions.Logging.Telegram.Extensions; +using Microsoft.Extensions.Logging.Console; using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; var builder = WebApplication.CreateBuilder(args); +#region Работа с конфигурацией var configuration = builder.Configuration; var preinstalledJwtToken = configuration["TOKEN"]; @@ -36,15 +40,25 @@ if (string.IsNullOrEmpty(configuration["MODEUS_URL"])) var permitLimit = int.TryParse(configuration["PERMIT_LIMIT"], out var parsedPermitLimit) ? parsedPermitLimit : 40; var timeLimit = int.TryParse(configuration["TIME_LIMIT"], out var parsedTimeLimit) ? parsedTimeLimit : 10; +#endregion + +#region Работа с папкой данных // создать папку data если не существует var dataDirectory = Path.Combine(AppContext.BaseDirectory, "data"); if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory); GlobalConsts.JwtFilePath = Path.Combine(dataDirectory, "jwt.txt"); var pluginsPath = Path.Combine(dataDirectory, "Plugins"); +#endregion +#region Работа с логированием builder.Logging.ClearProviders(); -builder.Logging.AddConsole(); +// Configure the console logger to include logging scopes so TraceId from the CorrelationIdMiddleware is visible +builder.Logging.AddSimpleConsole(options => +{ + options.SingleLine = true; + options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff "; +}); builder.Logging.AddFilter("Quartz", LogLevel.Warning); if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken)) builder.Logging.AddTelegram(options => @@ -61,6 +75,7 @@ if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken)) { "Quartz", LogLevel.Warning } }; }); +#endregion // Включаем MVC контроллеры var mvcBuilder = builder.Services.AddControllers(); @@ -70,7 +85,7 @@ builder.Services.AddHttpClient("modeus", client => }); builder.Services.AddSingleton(); builder.Services.AddScoped(); -builder.Services.AddHttpClient("authClient"); +builder.Services.AddHttpClient("authClient"); builder.Services.AddAuthentication() .AddScheme( @@ -226,6 +241,9 @@ var logger = app.Services.GetRequiredService>(); // Используем настройки из DI (Configure) app.UseForwardedHeaders(); +// Корреляция логов по запросам +app.UseMiddleware(); + if (string.IsNullOrEmpty(preinstalledJwtToken)) { var schedulerFactory = app.Services.GetRequiredService(); diff --git a/SfeduSchedule/Services/ModeusHttpClient.cs b/SfeduSchedule/Services/ModeusHttpClient.cs index 4fa8a4d..f2096d4 100644 --- a/SfeduSchedule/Services/ModeusHttpClient.cs +++ b/SfeduSchedule/Services/ModeusHttpClient.cs @@ -121,8 +121,7 @@ public class ModeusHttpClient catch { _logger.LogWarning( - "GetGuidAsync: Не удалось получить идентификатор пользователя, {FullName}, json: {Json}", fullName, - json); + "GetGuidAsync: не удалось получить идентификатор пользователя. FullName={FullName}", fullName); return null; } diff --git a/SfeduSchedule/Services/ModeusService.cs b/SfeduSchedule/Services/ModeusService.cs index 8910d9a..27411b4 100644 --- a/SfeduSchedule/Services/ModeusService.cs +++ b/SfeduSchedule/Services/ModeusService.cs @@ -30,7 +30,7 @@ public class ModeusService var schedule = await GetScheduleAsync(msr); if (schedule == null) { - _logger.LogError("GetScheduleJsonAsync: Schedule is null. Request: {@msr}", msr); + _logger.LogError("GetScheduleJsonAsync: schedule is null. {@Request}", msr); throw new Exception("Schedule is null"); } @@ -189,7 +189,7 @@ public class ModeusService var serializer = new CalendarSerializer(); var serializedCalendar = serializer.SerializeToString(calendar); - _logger.LogInformation("GetIcsAsync: Serialized calendar created. Length: {Length}", + _logger.LogInformation("GetIcsAsync: serialized calendar created. Length: {Length}", serializedCalendar?.Length ?? 0); return serializedCalendar; }