Мы ищем способ ПОДКЛЮЧИТЬ Request-Id
(или Correlation-Id
) к нескольким запросам API, как показано на рисунке ниже:
Идея состоит в том, чтобы иметь один единственный идентификатор для отслеживания конкретного запроса через несколько API в наших журналах. Мы используем стандартный ILogger
, который поставляется с .NET Core 2.1.
Что мы пробовали до сих пор
Мы попытались использовать Request-Id
в заголовке запроса, который успешно регистрируется, но мы не можем получить значение, чтобы добавить его в последующий HTTP-запрос к другим API.
Мы заметили, что есть также CorrelationId
, который регистрируется. Однако мы не можем понять, как его обновить.
Кроме того, мы заметили, что на TraceIdentity
доступен HttpContextAccessor
, который потенциально может решить нашу задачу. Но мы не знаем, как использовать это для решения нашей проблемы.
Мы не можем использовать Application Insights и хотим полагаться на собственную инфраструктуру ведения журналов. Мы не можем найти ничего особенного в документации.
Есть ли готовое решение, которое мы можем использовать, не предлагая собственное решение?
Идентификатор запроса, который регистрируется, - это TraceIdentifier.
Привет, @Jehof, как уже упоминалось, мы не смогли найти способ получить request-id
в последующих запросах. Не могли бы вы дать нам образец кода для достижения этой цели?
Я использовал Serilog для входа в ядро dotnet и нажимал свойства LogContext с помощью промежуточного программного обеспечения -
CorrelationMiddleware.cs
public class CorrelationMiddleware
{
readonly RequestDelegate _next;
public CorrelationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.Request.Headers.TryGetValue(Headers.Correlation, out StringValues correlationId);
using (LogContext.PushProperty("Correlation-Id", correlationId.FirstOrDefault()))
{
await _next.Invoke(context);
}
}
}
Зарегистрируйте это в своем методе настройки Startup.cs:
app.UseMiddleware<CorrelationLogger>();
Затем при исходящем http-вызове вы можете добавить заголовок в HttpRequestMessage, добавив
request.Headers.Add(Headers.Correlation, GetHeader("Correlation-Id"));
При поиске в журналах мы можем выполнить поиск по идентификатору корреляции и просмотреть полный список всех API ...
Привет, @David, мы, безусловно, можем использовать этот подход, и это запасной вариант. Но мы хотели избежать этого, поскольку у сервисов API могли быть разные владельцы команд. Вместо создания собственного «настраиваемого» промежуточного программного обеспечения мы искали что-то из коробки, что может быть единообразным для разных команд, и больше «стандартного», выходящего из официальный ASP.NET Core SDK. Надеюсь, я понимаю.
Не уверен, связано ли это напрямую, но мы сталкиваемся с той же проблемой при сопоставлении запросов между API и службами в проектах Service Fabric. В итоге мы установили и получили correlationId из CallContext (внутри System.Runtime.Remoting.Messaging). Случайно только что написал что-то об этом здесь. Пожалуйста, посмотрите, поможет ли это.
Я задал тот же вопрос @davidfowl в Твиттере. Он ответил:
No there's nothing out of the box for this. There's an end to end that works with app insights but it isn't very fleshed out. You might consider just using the same middleware across teams. if There's a work item to address this in 3.0 https://github.com/aspnet/Hosting/issues/1350
Итак, из того, что сейчас кажется, что пользовательское промежуточное ПО - единственный путь вперед. Это может измениться в будущем выпуске.
Обновлять
В итоге мы создали специальное промежуточное ПО, как предлагал @DavidMcEleney. Однако, помимо этого, мы добавили CorrelationId
в свойство AsyncLocal
. Это помогло нам получить доступ к CorrelationId
в любом месте кода, если / когда это необходимо. Вот код получения / установки CorrelationId
:
using System;
using System.Threading;
public static class CorrelationContext
{
private static readonly AsyncLocal<string> CorrelationId = new AsyncLocal<string>();
public static void SetCorrelationId(string correlationId)
{
if (string.IsNullOrWhiteSpace(correlationId))
{
throw new ArgumentException(nameof(correlationId), "Correlation id cannot be null or empty");
}
if (!string.IsNullOrWhiteSpace(CorrelationId.Value))
{
throw new InvalidOperationException("Correlation id is already set");
}
CorrelationId.Value = correlationId;
}
public static string GetCorrelationId()
{
return CorrelationId.Value;
}
}
Для использования в CorrelationMiddleware.cs
public class CorrelationMiddleware
{
private readonly RequestDelegate _next;
public CorrelationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.Request.Headers.TryGetValue("Correlation-Id-Header", out var correlationIds);
var correlationId = correlationIds.FirstOrDefault() ?? Guid.NewGuid().ToString();
CorrelationContext.SetCorrelationId(correlationId);
using (LogContext.PushProperty("Correlation-Id", correlationId))
{
await _next.Invoke(context);
}
}
}
Если нам понадобится доступ к CorrelationId
в любом месте кода позже, мы просто вызываем: CorrelationContext.GetCorrelationId();
Ага, я это видел. Спасибо еще раз.
пожалуйста, также проверьте другую бесплатную библиотеку: github.com/albumprinter/CorrelationTracking/wiki
Привет @AnkitVijay Я знаю, что этому вопросу один год, но мне было интересно: почему вы решили создать статическую функцию, а не регистрировать службу для каждого запроса, сохраняя значение? Он был бы доступен для всех других сервисов через DI и, как мне кажется, можно было бы избежать всей сложности AsyncLocal
.
почему вы не можете получить значение заголовка Request-Id? Если для входящего запроса установлен заголовок Request-Id, ядро asp.net использует его значение как CorrelationId в сообщениях журнала. это делается автоматически.