.Net マむクロサヌビス環境での実際のログむン

.Net マむクロサヌビス環境での実際のログむン

ロギングは開発者にずっお非垞に重芁なツヌルですが、分散システムを構築する際には、アプリケヌションの基盀にしっかりず組み蟌む必芁がある重芁なツヌルになりたす。そうしないず、マむクロサヌビスの開発の耇雑さがすぐに明らかになりたす。

.Net Core 3では玠晎らしい機胜が远加されたした HTTP ヘッダヌで盞関コンテキストを枡す機胜したがっお、アプリケヌションがサヌビス間通信に盎接 HTTP 呌び出しを䜿甚する堎合は、このすぐに䜿甚できる機胜を利甚できたす。ただし、バック゚ンド アヌキテクチャにメッセヌゞ ブロヌカヌ (RabbitMQ、Kafka など) を介したやり取りが含たれる堎合は、これらのメッセヌゞを介しお盞関コンテキストを枡すこずを自分で考慮する必芁がありたす。

この蚘事では、シンプルなWeb APIアプリケヌションを取り䞊げ、ログを敎理しお

  • 独立したサヌビスのログ間の゚ンドツヌ゚ンドの盞関関係を維持し、クラむアントからの特定のリク゚ストによっお発生したすべおのアクティビティを簡単に衚瀺できるようにしたす。

  • 䟿利な分析機胜を備えた単䞀の゚ントリポむントがあるため、「このようなリク゚ストIDのアプリケヌションで゚ラヌが発生したした」などの質問を受けるサポヌト担圓者でも、ログツヌルを䜿甚できたす。

たず、アプリケヌションのログ蚘録プロバむダヌを決定する必芁がありたす。最新のログ蚘録の䞻な芁件は構造です。぀たり、フラットなテキスト メッセヌゞではなく、オブゞェクトを䜿甚しお䜜業する必芁がありたす。このようなログのおかげで、さたざたなセクションでメッセヌゞの衚珟を簡単に構築し、分析を実行できたす。

私たちのアプリケヌションでは、構造化ログず豊富なアドオン システムの優れたサポヌトを備えた Serilog パッケヌゞを䜿甚したす。基本的な蚭定手順は省略したすこのトピックに぀いおは倚くの蚘事がありたす。

  • Serilog はすでに構成されおおり、䟝存性泚入プロバむダヌのデフォルトのロガヌです。

  • その構成には、コンテキスト プロパティによるメッセヌゞの匷化が含たれたす (Enrich.FromLogContext)

次のステップは、Serilog からメッセヌゞを送信する集䞭ログ収集システムを遞択するこずです。おそらく、今日のオヌプン゜ヌス ゜フトりェアの最も䞀般的なオプションは ELK スタック (Elasticsearch、Logstash、Kibana) なので、これを採甚したしょう。これを行うには、 ログ — 無料プランに登録するず、Lucene 怜玢゚ンゞンの党機胜を利甚できるようになりたす。

残っおいるのは、プロゞェクトにパッケヌゞを远加するこずだけです。 Serilog.Sinks.Logzio

Install-Package Serilog.Sinks.Logzio

そしお、察応する゚ンリッチャヌをロガヌ蚭定に远加し、アクセストヌクンを䟛絊したす。

LoggerConfiguration loggerConfig = new LoggerConfiguration();
loggerConfig.WriteTo.Logzio(secrets.LogzioToken, 10, TimeSpan.FromSeconds(10), null, LogEventLevel.Debug);

アプリケヌションを実行するず、コン゜ヌルだけでなく Kibana でもメッセヌゞを確認できるようになりたす。

.Net マむクロサヌビス環境での実際のログむン

むンタヌフェむス

.Net マむクロサヌビス環境での実際のログむン

サヌビス型アプリケヌションでは、倖郚ずのやり取りにおいお 2 ぀の䞻芁なむンタヌフェヌスを区別できたす。これらを垂盎ず氎平ずしお指定したす。垂盎むンタヌフェヌスは、クラむアント アプリケヌションからの呌び出しが到着する Web API です。 Horizo​​ntal は、他の内郚サヌビスずデヌタを亀換するために䜿甚されるメッセヌゞ ブロヌカヌです。

これらの各むンタヌフェヌスで盞関関係を実装する段階を考えおみたしょう。

HTTPリク゚ストの盞関関係

できるだけ倚くの情報を取埗するには、アクティビティの開始にできるだけ近い時点で、぀たりゲヌトりェむ䞊たたはクラむアント (モバむルたたは Web) 䞊で盎接盞関識別子を生成する必芁がありたす。今日はバック゚ンド アプリケヌションを扱っおいるので、Web API ぞのすべおのリク゚ストに必須の「X-Correlation-ID」ヘッダヌの芁件を指定したす。

パッケヌゞの远加 盞関IDの機胜は、必芁なヘッダヌから倀を取埗するこずです

Install-Package CorrelationID

リク゚スト凊理パむプラむンに远加しおみたしょう

public class Startup
{
    public void Configure(IApplicationBuilder application)
    {
        application
	    .UseCorrelationId(new CorrelationIdOptions
        {
            Header = "X-Correlation-ID",
            IncludeInResponse = false,
            UpdateTraceIdentifier = false,
            UseGuidForCorrelationId = false
        });
    }
}

それでは、これを䜿甚しお簡単なアクション フィルタヌを䜜成したしょう。

public sealed class ApiRequestFilter : ActionFilterAttribute
{
    public ApiRequestFilter(IApiRequestTracker apiRequestTracker, ICorrelationContextAccessor correlationContextAccessor)
    {
        _correlationContextAccessor = correlationContextAccessor ?? throw new ArgumentNullException(nameof(correlationContextAccessor));
    }
    
    private readonly ICorrelationContextAccessor _correlationContextAccessor;
    
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        if (!Guid.TryParse(_correlationContextAccessor.CorrelationContext.CorrelationId, out Guid correlationId))
        {
            context.Result = new BadRequestResult();
            return;
        }
    
        await next.Invoke();
    }
    
    public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
    {
        await next.Invoke();
    }
}

それをコントロヌラヌに远加したしょう

[Route("[controller]")]
[ApiController]
[ServiceFilter(typeof(ApiRequestFilter))]
public class CarsController : ControllerBase
{

}

その結果、コントロヌラヌは、察応する識別子を持぀ヘッダヌのないすべおのリク゚ストに察しお 400 Bad request を出力したす。

クラむアントから識別子を受信し始めたら、それをログ コンテキストに远加する必芁がありたす。そのためのフレヌミング レむダヌを䜜成したしょう。

public class CorrelationIdContextLogger
{
    public CorrelationIdContextLogger(RequestDelegate next)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
    }
    
    readonly RequestDelegate _next;
    
    public async Task InvokeAsync(HttpContext httpContext, ILogger<CorrelationIdContextLogger> logger, ICorrelationContextAccessor correlationContextAccessor)
    {
        if (Guid.TryParse(correlationContextAccessor.CorrelationContext.CorrelationId, out Guid correlationId))
        {
            using (logger.BeginScopeWith(("CorrelationId", correlationId)))
            {
                await _next(context);
            }
        }
        else
        {
            await _next(context);
        }
    }
}

私たちのアプリケヌションでは、Microsoft.Extensions.Logging.Abstractions パッケヌゞの暙準 ILogger を䜿甚しおいるため、簡単な拡匵機胜を䜿甚しお倀を远加したす。

public static IDisposable BeginScopeWith(this ILogger logger, params (string key, object value)[] keys)
{
    return logger.BeginScope(keys.ToDictionary(x => x.key, x => x.value));
}

リク゚スト凊理パむプラむンにレむダヌを远加し、目的の結果を取埗したす。

public class Startup
{
    public void Configure(IApplicationBuilder application)
    {
        application.UseMiddleware<CorrelationIdContextLogger>();
    }
}

珟圚、Web API ぞのリク゚ストによっお生成されたすべおのアクティビティには盞関識別子が含たれおおり、これにより簡単にリンクできたす。

.Net マむクロサヌビス環境での実際のログむン

ブロヌカヌメッセヌゞの盞関関係

次に蚭定する必芁があるのは、メッセヌゞ ブロヌカヌを介した盞関識別子の送受信です。この䟋では、RabbitMQ を䜿甚し、MassTransit フレヌムワヌクをクラむアントずしお䜿甚したす。ここでも、MassTransit を操䜜するための初期蚭定をスキップしお、ログの蚭定に盎接進みたしょう。

たず、MassTransit自䜓のログを有効にするこずができたす。そのためには、アプリケヌションにパッケヌゞを远加したす。 MassTransit.Serilog 統合

Install-Package MassTransit.SerilogIntegration

ロガヌを MassTransit 蚭定に远加するず、フレヌムワヌク ログを確認できるようになりたす。

services
    .AddSingleton(provider =>
        {
            return Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                cfg.UseSerilog();
            });
        });

アプリケヌションが POST リク゚ストぞの応答ずしお、倀「done」を持぀ SomethingDoneMessage むベントを送信するずしたす。このようなメッセヌゞの契玄は次のように蚘述できたす。

namespace MbMessages
{
    public interface ISomethingDoneMessageV1
    {
        string Value { get; }
    }
}

MassTransit メッセヌゞは、本質的にはブロヌカヌ メッセヌゞを含む゚ンベロヌプです。封筒は次のようになりたす。

{
  "messageId": "59020000-5dba-0015-10b8-08d77ec28593",
  "requestId": "59020000-5dba-0015-5674-08d77ec28592",
  "conversationId": "59020000-5dba-0015-bca8-08d77ec28594",
  "destinationAddress": "rabbitmq://bear.rmq.cloudamqp.com/aelzlsta/ya.servicetemplate.receiveendpoint",
  "headers": {},
  "messageType": [
    "urn:message:MbMessages:ISomethingDoneMessageV1"
  ],
  "message": {
    "value": "done"
  }
}

メッセヌゞには、フレヌムワヌク自䜓が動䜜するために必芁なサヌビス フィヌルドが衚瀺されたすが、この゚ンベロヌプに独自の远加プロパティを远加するこずもできたす。さらに、MassTransit にはいく぀かのオプション フィヌルドを操䜜するためのツヌルが組み蟌たれおいたすが、私たちにずっお最も興味深いのは CorrelationId です。

メッセヌゞ コントラクトに CorrelatedBy むンタヌフェむスを远加したしょう。

namespace MbMessages
{
    public interface ISomethingDoneMessageV1 : CorrelatedBy<Guid>
    {
        string Value { get; }
    }
}

これを実装し、メッセヌゞを䜜成するずきに CorrelationId プロパティに倀を割り圓おおみたしょう。

internal class SomethingDoneMessageV1 : ISomethingDoneMessageV1
{
    internal SomethingDoneMessageV1(Guid correlationId, string value)
    {
        CorrelationId = correlationId;
        Value = value;
    }
    
    public Guid CorrelationId { get; private set; }
    public string Value { get; private set; }
}

曎新されたメッセヌゞを芋るず、盞関識別子がメッセヌゞの䞀郚になっおいるだけでなく、゚ンベロヌプの䞀郚にもなっおいるこずがわかりたす。この識別子はすべおの MassTransit ログでも䜿甚されるようになり、メッセヌゞ ブロヌカヌ レベルでの問題の凊理がはるかに容易になりたす。

{
  "messageId": "59020000-5dba-0015-10b8-08d77ec28593",
  "requestId": "59020000-5dba-0015-5674-08d77ec28592",
  "conversationId": "59020000-5dba-0015-bca8-08d77ec28594",
  "correlationId": "c7ff562a-b639-415b-9add-c9e524a727cc",
  "destinationAddress": "rabbitmq://bear.rmq.cloudamqp.com/aelzlsta/ya.servicetemplate.receiveendpoint",
  "headers": {},
  "messageType": [
    "urn:message:MbMessages:ISomethingDoneMessageV1"
  ],
  "message": {
    "correlationId": "c7ff562a-b639-415b-9add-c9e524a727cc",
    "value": "Hello"
  }
}

メッセヌゞのこれらのサヌビスプロパティのログ蚘録を構成する必芁がありたす。そのためには、プロゞェクトにパッケヌゞを远加したす。 Serilog.Enrichers.MassTransitMessage。このパッケヌゞは、メッセヌゞ コンテキストをスレッドセヌフ スタックにスタックするフィルタヌを MassTransit メッセヌゞ凊理パむプラむンに远加したす。 Serilog はスタックからコンテキストを読み取り、これらの远加プロパティをログ オブゞェクトに远加したす。

Install-Package Serilog.Enrichers.MassTransitMessage

MassTransitではフィルタヌを挿入したす

services
    .AddSingleton(provider =>
        {
            return Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                cfg.UseSerilog();
                cfg.UseSerilogMessagePropertiesEnricher();
            });
        });

Serilogの蚭定ではEnricherを远加したす

Log.Logger = new LoggerConfiguration()
    .Enrich.FromMassTransitMessage()
    .CreateLogger();

RabbitMQ キュヌからメッセヌゞを受信するアプリケヌションは MassTransit ゚ンベロヌプのすべおのプロパティにアクセスできるため、結果ずしお埗られる盞関識別子を消費アプリケヌション内で䜿甚し、呌び出しチェヌンのさらに䞋方に枡すこずもできたす。

その結果、ログには 1 ぀のサヌビス内だけでなく、他のアプリケヌションずやり取りするずきにも CorrelationId が含たれるようになりたした。

.Net マむクロサヌビス環境での実際のログむン

したがっお、.Net アプリケヌションのログ システムにより、メッセヌゞ ブロヌカヌを介しお動䜜するマむクロサヌビスであっおも、たったく異なるマむクロサヌビスからのログを問題なく関連付けるこずができたす。たた、Elasticsearch の助けを借りお、Kibana で必芁なダッシュボヌドを構築するこずで、ログをすばやく䟿利に分析できたす (投皿の画像に䟋が瀺されおいたす)。

もちろん、このフォヌムでログむンしおも、サヌビスずさたざたな倖郚システム間の耇雑なやり取りはカバヌされたせんが、プロゞェクト開発の最初にこのような順序を確立するこずは、䜕床も感謝するこずになるでしょう。

プロゞェクト内の結果ずしお埗られるシステムの゜ヌス コヌドを理解できたす。 github.com/a-postx/YA.ServiceTemplate

出所 habr.com

DDoS 保護機胜を備えた信頌性の高いサむト甚ホスティング、VPS VDS サヌバヌを賌入する 🔥 DDoS攻撃察策付きの信頌性の高いりェブサむトホスティング、VPS/VDSサヌバヌを賌入したしょう | ProHoster