Pag-log in sa isang .Net microservice na kapaligiran sa pagsasanay

Pag-log in sa isang .Net microservice na kapaligiran sa pagsasanay

Ang pag-log ay isang napakahalagang tool ng developer, ngunit kapag lumilikha ng mga distributed system, ito ay nagiging isang bato na kailangang ilagay mismo sa pundasyon ng iyong aplikasyon, kung hindi, ang pagiging kumplikado ng pagbuo ng mga microservice ay mabilis na mamamatay.

Nagdagdag ng mahusay ang .Net Core 3 kakayahang magpadala ng konteksto ng ugnayan sa mga header ng HTTP, kaya kung ang iyong mga application ay gumagamit ng mga direktang HTTP na tawag para sa inter-service na komunikasyon, maaari mong samantalahin ang out-of-the-box na functionality na ito. Gayunpaman, kung ang iyong arkitektura ng backend ay nagsasangkot ng pakikipag-ugnayan sa pamamagitan ng isang broker ng mensahe (RabbitMQ, Kafka, atbp.), kailangan mo pa ring mag-alala tungkol sa pagpasa sa konteksto ng ugnayan sa pamamagitan ng mga mensaheng ito.

Sa artikulong ito kukuha kami ng isang simpleng web-api application at ayusin ang pag-log, na gagawin

  • panatilihin ang end-to-end na ugnayan sa pagitan ng mga log ng mga independiyenteng serbisyo upang madali mong makita ang lahat ng aktibidad na sanhi ng isang partikular na kahilingan mula sa kliyente

  • magkaroon ng isang entry point na may maginhawang pagsusuri, upang kahit na ang Suporta ay magagamit ang tool sa pag-log, kung saan maaaring gamitin ang mga tanong tulad ng "Nagkaroon ako ng error sa application na may ganito at ganoong request ID"

Una, kailangan naming magpasya sa isang provider ng pag-log para sa aming aplikasyon. Ang pangunahing kinakailangan para sa modernong pag-log ay istraktura, i.e. hindi tayo dapat magtrabaho sa mga flat text message, ngunit sa mga bagay. Salamat sa naturang mga log, madali kaming makakagawa ng mga view ng aming mga mensahe mula sa iba't ibang pananaw at makapagsagawa ng analytics.

Para sa aming aplikasyon gagamitin namin ang Serilog package, na may mahusay na suporta para sa structured logging at isang rich add-on system. Laktawan ko ang mga pangunahing hakbang ng pag-set up nito (maaari kang makahanap ng isang malaking bilang ng mga artikulo sa paksang ito) at gawin ang pagpapalagay na

  • Ang Serilog ay na-configure na at ang default na logger para sa iyong dependency injection provider

  • kasama sa pagsasaayos nito ang pagpapayaman ng mga mensahe na may mga katangian ng konteksto (Enrich.FromLogContext)

Ang susunod na hakbang ay ang piliin kung aling sentralisadong sistema ng koleksyon ng log ang magpapadala ng mga mensahe mula sa Serilog. Marahil ang pinakakaraniwang opsyon sa open source ngayon ay ang ELK stack (Elasticsearch, Logstash at Kibana), kaya kunin natin ito. Para magawa ito, gagamitin namin ang alok mula sa Logz.IO — pagkatapos magparehistro para sa isang libreng plano, nasa aming mga kamay ang lahat ng kapangyarihan ng search engine ng Lucene.

Ang kailangan lang nating gawin ay idagdag ang package sa ating proyekto Serilog.Sinks.Logzio

Install-Package Serilog.Sinks.Logzio

At idagdag ang kaukulang enricher sa configuration ng aming logger, na pinapakain ito ng access token

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

Sa pamamagitan ng paglulunsad ng application, magagawa naming obserbahan ang aming mga mensahe hindi lamang sa console, kundi pati na rin sa Kibana.

Pag-log in sa isang .Net microservice na kapaligiran sa pagsasanay

Interface

Pag-log in sa isang .Net microservice na kapaligiran sa pagsasanay

Sa isang application na uri ng serbisyo, maaari nating makilala ang dalawang pangunahing interface para sa pakikipag-ugnayan nito sa labas ng mundo, italaga natin ang mga ito bilang patayo at pahalang. Ang patayong interface ay isang web API kung saan dumarating ang mga tawag mula sa application ng kliyente. Ang Horizontal ay isang message broker na ginagamit upang makipagpalitan ng data sa iba pang panloob na serbisyo.

Isaalang-alang natin ang mga yugto ng pagpapakilala ng ugnayan sa bawat isa sa mga interface na ito.

Kaugnayan sa mga kahilingan sa HTTP

Upang makakuha ng mas maraming impormasyon hangga't maaari, kailangan naming bumuo ng isang pagkakakilanlan ng ugnayan hangga't maaari sa simula ng aktibidad, ibig sabihin. sa gateway o direkta sa kliyente (mobile o web). Dahil ngayon ay nakikipag-usap kami sa isang backend na application, ipapahiwatig lang namin dito ang kinakailangan para sa mandatoryong "X-Correlation-ID" na header sa lahat ng mga kahilingan sa web API.

Pagdaragdag ng isang pakete CorrelationID, na ang function ay kunin ang value mula sa header na kailangan namin

Install-Package CorrelationID

Idagdag natin ito sa pipeline ng pagpoproseso ng kahilingan

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

Ngayon ay gagamitin namin ito upang lumikha ng isang simpleng filter ng pagkilos:

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();
    }
}

At idagdag ito sa controller

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

}

Bilang resulta, magpapakita ang controller ng 400 Bad request para sa lahat ng mga kahilingan na walang header na may kaukulang identifier.

Pagkatapos naming magsimulang makatanggap ng isang identifier mula sa kliyente, dapat naming idagdag ito sa konteksto ng pag-log, gagawa kami ng isang framing layer para dito:

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);
        }
    }
}

Sa aming aplikasyon, ginagamit namin ang karaniwang ILogger mula sa pakete ng Microsoft.Extensions.Logging.Abstractions, kaya idaragdag namin ang halaga gamit ang isang simpleng extension dito.

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

Nagdaragdag kami ng layer sa pipeline ng pagpoproseso ng kahilingan at makuha ang ninanais na resulta.

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

Ngayon ang lahat ng aktibidad na nabuo sa pamamagitan ng mga kahilingan sa aming web API ay naglalaman ng isang pagkakakilanlan ng ugnayan kung saan madali silang maiugnay.

Pag-log in sa isang .Net microservice na kapaligiran sa pagsasanay

Kaugnayan sa mga mensahe ng broker

Ang susunod na hakbang ay i-set up ang paghahatid at pagtanggap ng pagkakakilanlan ng ugnayan sa pamamagitan ng broker ng mensahe. Sa aming halimbawa, gagamitin namin ang RabbitMQ, at kunin ang MassTransit framework bilang kliyente. Muli, laktawan natin ang paunang setup ng pagtatrabaho sa MassTransit at dumiretso sa pag-set up ng pag-log.

Upang magsimula, maaari naming isama ang mga log ng MassTransit mismo para dito magdaragdag kami ng isang pakete sa aming aplikasyon MassTransit.SerilogIntegration

Install-Package MassTransit.SerilogIntegration

Ngayon pagkatapos idagdag ang logger sa mga setting ng MassTransit, makikita natin ang mga log ng framework.

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

Hayaang tumugon ang aming application sa isang kahilingan sa POST sa pamamagitan ng pagpapadala ng kaganapan sa SomethingDoneMessage na may halagang "tapos na". Ang kontrata para sa naturang mensahe ay maaaring ilarawan bilang mga sumusunod:

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

Ang mga mensahe ng MassTransit ay mahalagang isang sobre kung saan ang mga mensahe ng broker ay nakapaloob. Ang sobre ay mukhang ganito:

{
  "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"
  }
}

Ang mensahe ay nagpapakita ng mga patlang ng serbisyo na kinakailangan para sa pagpapatakbo ng mismong balangkas, ngunit mayroon kaming kakayahang magdagdag ng sarili naming mga karagdagang katangian sa sobreng ito. Bukod dito, ang MassTransit ay may mga built-in na tool para sa pagtatrabaho sa ilang opsyonal na field, ang pinakakawili-wili ay ang correlation identifier na CorrelationId.

Idagdag natin ang interface ng CorrelatedBy sa kontrata ng mensahe:

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

Ipatupad natin ito at magtalaga ng value sa CorrelationId property kapag gumagawa ng mensahe:

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; }
}

Kung titingnan natin ang na-update na mensahe, makikita natin na ang pagkakakilanlan ng ugnayan ay naging hindi lamang bahagi ng ating mensahe, kundi bahagi rin ng sobre - ang identifier na ito ay gagamitin na rin ngayon sa lahat ng mga log ng MassTransit, na nangangahulugan na ito ay magiging mas madali. para harapin natin ang mga problema sa antas ng message broker.

{
  "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"
  }
}

Ang kailangan lang nating gawin ay i-configure ang pag-log ng mga katangian ng serbisyo ng mensahe para dito ay magdaragdag kami ng isang pakete sa proyekto Serilog.Enrichers.MassTransitMessage. Nagdaragdag ang package ng filter sa pipeline ng pagpoproseso ng mensahe ng MassTransit na nag-stack sa konteksto ng mensahe sa isang thread-safe stack. Binabasa ng Serilog ang konteksto mula sa stack at idinaragdag ang mga karagdagang katangian na ito sa aming mga log object.

Install-Package Serilog.Enrichers.MassTransitMessage

Sa MassTransit naglalagay kami ng filter

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

At sa pagsasaayos ng Serilog ay nagdaragdag kami ng isang enricher

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

Dahil ang application na tumatanggap ng mensahe mula sa RabbitMQ queue ay may access sa lahat ng mga katangian ng MassTransit envelope, maaari naming gamitin ang resultang pagkakakilanlan ng ugnayan sa loob ng gumagamit ng application, pati na rin ipasa ito sa kahabaan ng chain ng tawag.

Bilang resulta, ang aming mga log ay nagsimulang maglaman ng CorrelationId hindi lamang sa loob ng isang serbisyo, kundi pati na rin kapag nakikipag-ugnayan sa iba pang mga application.

Pag-log in sa isang .Net microservice na kapaligiran sa pagsasanay

Kaya, ang resultang sistema ng pag-log in sa mga .Net na application ay nagpapahintulot sa amin na iugnay ang mga log mula sa ganap na magkakaibang mga microservice nang walang anumang mga problema - kahit na ang mga gumagana sa pamamagitan ng isang broker ng mensahe. At sa tulong ng Elasticsearch, maaari naming mabilis at maginhawang pag-aralan ang mga log sa pamamagitan ng pagbuo ng mga dashboard na kailangan namin sa Kibana (isang halimbawa ay ipinapakita sa larawan para sa post).

Siyempre, hindi saklaw ng pag-log in sa form na ito ang mga kumplikadong pakikipag-ugnayan sa pagitan ng iyong mga serbisyo at iba't ibang mga panlabas na sistema, ngunit ang pagtatatag ng ganoong pagkakasunud-sunod sa pinakadulo simula ng pag-unlad ng proyekto ay isa sa mga bagay na magpapasalamat ka sa iyong sarili nang higit sa isang beses.

Maiintindihan mo ang source code ng nagresultang system sa proyekto: github.com/a-postx/YA.ServiceTemplate

Pinagmulan: www.habr.com

Bumili ng maaasahang pagho-host para sa mga site na may proteksyon ng DDoS, mga server ng VPS VDS 🔥 Bumili ng maaasahang website hosting na may proteksyon ng DDoS, VPS VDS servers | ProHoster