Добавил correlation id
Some checks failed
Create and publish a Docker image / Publish image (push) Failing after 1m10s

This commit is contained in:
2025-11-21 16:03:14 +03:00
parent 8ba1aea46a
commit daf3639038
5 changed files with 58 additions and 7 deletions

View File

@@ -11,7 +11,7 @@ namespace SfeduSchedule.Controllers;
[ApiController] [ApiController]
[Route("api/proxy")] [Route("api/proxy")]
[EnableRateLimiting("throttle")] [EnableRateLimiting("throttle")]
public class ProxyController(ModeusService modeusService, ILogger<ScheduleController> logger) : ControllerBase public class ProxyController(ModeusService modeusService, ILogger<ProxyController> logger) : ControllerBase
{ {
/// <summary> /// <summary>
/// Получить расписание по пользовательскому запросу. /// Получить расписание по пользовательскому запросу.

View File

@@ -0,0 +1,34 @@
using System.Diagnostics;
namespace SfeduSchedule.Middleware;
/// <summary>
/// Middleware для добавления и обработки Correlation ID в HTTP запросах.
/// Нужно для трассировки запросов.
/// </summary>
public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger<CorrelationIdMiddleware> 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);
}
}

View File

@@ -1,4 +1,5 @@
using System.Net; using System.Net;
using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Threading.RateLimiting; using System.Threading.RateLimiting;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
@@ -15,10 +16,13 @@ using SfeduSchedule.Auth;
using SfeduSchedule.Jobs; using SfeduSchedule.Jobs;
using SfeduSchedule.Middleware; using SfeduSchedule.Middleware;
using SfeduSchedule.Services; using SfeduSchedule.Services;
using SfeduSchedule.Logging;
using X.Extensions.Logging.Telegram.Extensions; using X.Extensions.Logging.Telegram.Extensions;
using Microsoft.Extensions.Logging.Console;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
#region Работа с конфигурацией
var configuration = builder.Configuration; var configuration = builder.Configuration;
var preinstalledJwtToken = configuration["TOKEN"]; 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 permitLimit = int.TryParse(configuration["PERMIT_LIMIT"], out var parsedPermitLimit) ? parsedPermitLimit : 40;
var timeLimit = int.TryParse(configuration["TIME_LIMIT"], out var parsedTimeLimit) ? parsedTimeLimit : 10; var timeLimit = int.TryParse(configuration["TIME_LIMIT"], out var parsedTimeLimit) ? parsedTimeLimit : 10;
#endregion
#region Работа с папкой данных
// создать папку data если не существует // создать папку data если не существует
var dataDirectory = Path.Combine(AppContext.BaseDirectory, "data"); var dataDirectory = Path.Combine(AppContext.BaseDirectory, "data");
if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory); if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory);
GlobalConsts.JwtFilePath = Path.Combine(dataDirectory, "jwt.txt"); GlobalConsts.JwtFilePath = Path.Combine(dataDirectory, "jwt.txt");
var pluginsPath = Path.Combine(dataDirectory, "Plugins"); var pluginsPath = Path.Combine(dataDirectory, "Plugins");
#endregion
#region Работа с логированием
builder.Logging.ClearProviders(); 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); builder.Logging.AddFilter("Quartz", LogLevel.Warning);
if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken)) if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken))
builder.Logging.AddTelegram(options => builder.Logging.AddTelegram(options =>
@@ -61,6 +75,7 @@ if (!string.IsNullOrEmpty(tgChatId) && !string.IsNullOrEmpty(tgToken))
{ "Quartz", LogLevel.Warning } { "Quartz", LogLevel.Warning }
}; };
}); });
#endregion
// Включаем MVC контроллеры // Включаем MVC контроллеры
var mvcBuilder = builder.Services.AddControllers(); var mvcBuilder = builder.Services.AddControllers();
@@ -70,7 +85,7 @@ builder.Services.AddHttpClient("modeus", client =>
}); });
builder.Services.AddSingleton<ModeusHttpClient>(); builder.Services.AddSingleton<ModeusHttpClient>();
builder.Services.AddScoped<ModeusService>(); builder.Services.AddScoped<ModeusService>();
builder.Services.AddHttpClient<ModeusHttpClient>("authClient"); builder.Services.AddHttpClient("authClient");
builder.Services.AddAuthentication() builder.Services.AddAuthentication()
.AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>( .AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>(
@@ -226,6 +241,9 @@ var logger = app.Services.GetRequiredService<ILogger<Program>>();
// Используем настройки из DI (Configure<ForwardedHeadersOptions>) // Используем настройки из DI (Configure<ForwardedHeadersOptions>)
app.UseForwardedHeaders(); app.UseForwardedHeaders();
// Корреляция логов по запросам
app.UseMiddleware<CorrelationIdMiddleware>();
if (string.IsNullOrEmpty(preinstalledJwtToken)) if (string.IsNullOrEmpty(preinstalledJwtToken))
{ {
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>(); var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();

View File

@@ -121,8 +121,7 @@ public class ModeusHttpClient
catch catch
{ {
_logger.LogWarning( _logger.LogWarning(
"GetGuidAsync: Не удалось получить идентификатор пользователя, {FullName}, json: {Json}", fullName, "GetGuidAsync: не удалось получить идентификатор пользователя. FullName={FullName}", fullName);
json);
return null; return null;
} }

View File

@@ -30,7 +30,7 @@ public class ModeusService
var schedule = await GetScheduleAsync(msr); var schedule = await GetScheduleAsync(msr);
if (schedule == null) 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"); throw new Exception("Schedule is null");
} }
@@ -189,7 +189,7 @@ public class ModeusService
var serializer = new CalendarSerializer(); var serializer = new CalendarSerializer();
var serializedCalendar = serializer.SerializeToString(calendar); var serializedCalendar = serializer.SerializeToString(calendar);
_logger.LogInformation("GetIcsAsync: Serialized calendar created. Length: {Length}", _logger.LogInformation("GetIcsAsync: serialized calendar created. Length: {Length}",
serializedCalendar?.Length ?? 0); serializedCalendar?.Length ?? 0);
return serializedCalendar; return serializedCalendar;
} }