Logimine mikroteenuste keskkonnas .Net praktikas

Logimine mikroteenuste keskkonnas .Net praktikas

Logimine on arendaja jaoks vĂ€ga oluline tööriist, kuid hajussĂŒsteemide loomisel muutub see vundamendikiviks, mis tuleb paika panna kohe rakenduse alusesse, vastasel juhul annab mikroteenuste arenduse keerukus end vĂ€ga kiiresti tunda.

.Net Core 3-s lisandus suurepĂ€rane vĂ”imalus edastada korrelatsioonikonteksti HTTP-pĂ€istes, seega kui teie rakendused kasutavad teenustevaheliseks suhtluseks otseseid HTTP-kutseid, saate kasutada seda valmislahendust. Kui teie backend’i arhitektuur eeldab aga suhtlust sĂ”numivahendaja kaudu (RabbitMQ, Kafka jne), peate endiselt ise hoolitsema korrelatsioonikonteksti edastamise eest nende sĂ”numite kaudu.

Selles artiklis vÔtame lihtsa Web API rakenduse ja seadistame logimise, mis

  • sĂ€ilitab sĂ”ltumatute teenuste logide vahel lĂ€biva korrelatsiooni nii, et oleks lihtne nĂ€ha kĂ”iki tegevusi, mille konkreetne kliendipĂ€ring kĂ€ivitas

  • vĂ”imaldab luua ĂŒhe keskse sisenemispunkti koos mugava analĂŒĂŒsiga, et logimistööriista saaks kasutada isegi tugi, kellele jĂ”uavad kĂŒsimused stiilis „mul viskas rakenduses veateate sellise pĂ€ringu ID-ga”

Esiteks peame oma rakenduses valima logimislahenduse pakkuja. Kaasaegse logimise peamine nĂ”ue on struktureeritus, st me ei tööta lamedate tekstsĂ”numitega, vaid objektidega. TĂ€nu sellistele logidele saame hĂ”lpsalt luua oma sĂ”numitest erinevaid vaateid ja teha analĂŒĂŒsi.

Oma rakenduses kasutame paketti Serilog, millel on suurepĂ€rane struktureeritud logimise tugi ja rikkalik laienduste ökosĂŒsteem. JĂ€tan selle pĂ”hiseadistuse etapid vahele (sellel teemal leiab palju artikleid) ja eeldan, et

  • Serilog on juba seadistatud ja on teie sĂ”ltuvussĂŒstimise pakkuja vaikimisi logger

  • selle konfiguratsioonis on lubatud sĂ”numite rikastamine konteksti omadustega (Enrich.FromLogContext)

JĂ€rgmine samm on valida, millisesse tsentraliseeritud logikogumissĂŒsteemi Serilogi sĂ”numid saata. TĂ”enĂ€oliselt on tĂ€napĂ€eval kĂ”ige levinum avatud lĂ€htekoodiga lahendus ELK stack (Elasticsearch, Logstash ja Kibana), seega kasutame seda. Selleks vĂ”tame appi lahenduse ettevĂ”ttelt Logz.IO — pĂ€rast tasuta paketi registreerimist on meie kĂ€sutuses kogu Lucene'i otsingumootori vĂ”imekus.

Meil jÀÀb ĂŒle lisada oma projekti pakett Serilog.Sinks.Logzio

Install-Package Serilog.Sinks.Logzio

Ja lisada meie logeri konfiguratsiooni vastav enricher, andes sellele ette juurdepÀÀsutokeni

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

PÀrast rakenduse kÀivitamist nÀeme oma sÔnumeid mitte ainult konsoolis, vaid ka Kibanas.

Logimine mikroteenuste keskkonnas .Net praktikas

Liidesed

Logimine mikroteenuste keskkonnas .Net praktikas

TeenusetĂŒĂŒpi rakendusel saab eristada kahte peamist liidest vĂ€lismaailmaga suhtlemiseks, nimetagem neid vertikaalseks ja horisontaalseks. Vertikaalne liides on Web API, mille kaudu saabuvad kliendirakenduse pĂ€ringud. Horisontaalne liides on sĂ”numivahendaja, mida kasutatakse andmevahetuseks teiste sisemiste teenustega.

Vaatleme korrelatsiooni juurutamise etappe kummagi liidese puhul.

Korrelatsioon HTTP-pÀringutes

Et saada vĂ”imalikult palju teavet, tuleb korrelatsiooniidentifikaator genereerida vĂ”imalikult tegevuse alguse lĂ€hedal, s.t. lĂŒĂŒsis vĂ”i otse kliendi poolel (mobiili- vĂ”i veebirakenduses). Kuna tegeleme tĂ€na backend-rakendusega, siis piirduleme nĂ”udega, et kĂ”igil Web API pĂ€ringutel peab olema kohustuslik pĂ€is „X-Correlation-ID”.

Lisame paketi CorrelationID, mille ĂŒlesanne on lugeda vÀÀrtus meie jaoks vajalikust pĂ€isest

Install-Package CorrelationID

Lisame selle pÀringutöötluse konveierisse

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

NĂŒĂŒd teeme selle abil lihtsa action-filtri:

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

Ja lisame selle kontrollerisse

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

}

Selle tulemusel hakkab kontroller tagastama kÔigile pÀringutele ilma vastava identifikaatoriga pÀiseta vastuse 400 Bad Request.

PĂ€rast seda, kui hakkasime kliendilt identifikaatorit saama, peame selle lisama logimiskonteksti. Selleks teeme eraldi vahekihi:

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

Meie rakenduses kasutame standardset ILoggerit paketist Microsoft.Extensions.Logging.Abstractions, seega lisame vÀÀrtuse selle jaoks loodud lihtsa laienduse abil.

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

Lisame vahekihi pÀringutöötluse konveierisse ja saame soovitud tulemuse.

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

NĂŒĂŒd sisaldavad kĂ”ik tegevused, mille kĂ€ivitavad pĂ€ringud meie Web API-le, korrelatsiooniidentifikaatorit, mille alusel saab need hĂ”lpsasti omavahel siduda.

Logimine mikroteenuste keskkonnas .Net praktikas

Korrelatsioon sÔnumibrokeri teadetes

JÀrgmise sammuna peame seadistama korrelatsiooniidentifikaatori edastamise ja vastuvÔtmise sÔnumivahendaja kaudu. Meie nÀites kasutame RabbitMQ-d ning kliendina vÔtame kasutusele MassTransit frameworki. JÀtame taas MassTransiti esmase seadistuse vahele ja liigume kohe logimise seadistamise juurde.

Alustuseks vĂ”ime sisse lĂŒlitada MassTransiti enda logid; selleks lisame oma rakendusse paketi MassTransit.SerilogIntegration

Install-Package MassTransit.SerilogIntegration

PÀrast loggeri lisamist MassTransiti seadistusse nÀeme frameworki logisid.

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

Oletame, et meie rakendus saadab POST-pĂ€ringu tulemusena sĂŒndmuse SomethingDoneMessage vÀÀrtusega „done“. Sellise sĂ”numi lepingu saab kirjeldada jĂ€rgmiselt:

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

MassTransiti sĂ”numid on sisuliselt ĂŒmbris, mille sisse on paigutatud vahendaja sĂ”numid. See ĂŒmbris nĂ€eb vĂ€lja umbes selline:

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

SĂ”numis on nĂ€ha teenindusvĂ€lju, mida framework ise oma tööks vajab, kuid meil on vĂ”imalik sellesse ĂŒmbrisesse lisada ka oma tĂ€iendavaid omadusi. Lisaks on MassTransitis olemas sisseehitatud vahendid mĂ”ne valikulise vĂ€lja kasutamiseks, millest meid huvitab kĂ”ige rohkem korrelatsiooniidentifikaator CorrelationId.

Lisame sÔnumi lepingule liidese CorrelatedBy:

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

Rakendame selle ja mÀÀrame sÔnumi loomisel atribuudile CorrelationId vÀÀrtuse:

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

Kui vaatame uuendatud teadet, nĂ€eme, et korrelatsiooniidentifikaatorist on saanud mitte ainult meie sĂ”numi, vaid ka ĂŒmbriku osa — seda identifikaatorit kasutatakse nĂŒĂŒd ka kĂ”igis MassTransit'i logides, mis tĂ€hendab, et sĂ”numivahendaja tasemel probleemide analĂŒĂŒsimine muutub meie jaoks mĂ€rksa lihtsamaks.

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

NĂŒĂŒd jÀÀb ĂŒle seadistada nende sĂ”numi teenusatribuutide logimine, selleks lisame projekti paketi Serilog.Enrichers.MassTransitMessage. Pakett lisab MassTransit'i sĂ”numitöötluse konveierisse filtri, mis paigutab sĂ”numi konteksti lĂ”imeturvalisse pinu. Serilog loeb konteksti pinust ja lisab need tĂ€iendavad omadused meie logiobjektidele.

Install-Package Serilog.Enrichers.MassTransitMessage

MassTransit'is lisame filtri

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

Ja Serilogi konfiguratsiooni lisame enricheri

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

Kuna rakendusel, mis vĂ”tab sĂ”numi RabbitMQ jĂ€rjekorrast vastu, on juurdepÀÀs kĂ”igile MassTransit'i ĂŒmbriku omadustele, saame saadud korrelatsiooniidentifikaatorit kasutada tarbijarakenduse sees ning edastada seda edasi kogu kutsungiahela ulatuses.

Selle tulemusel sisaldavad meie logid nĂŒĂŒd CorrelationId-d mitte ainult ĂŒhe teenuse piires, vaid ka suhtluses teiste rakendustega.

Logimine mikroteenuste keskkonnas .Net praktikas

KokkuvĂ”ttes vĂ”imaldab selline .Net-rakenduste logimissĂŒsteem meil ilma suuremate raskusteta korreleerida logisid tĂ€iesti erinevatest mikroteenustest — isegi neist, mis töötavad sĂ”numivahendaja kaudu. Elasticsearchi abil saame logisid kiiresti ja mugavalt analĂŒĂŒsida, luues Kibanas vajalikud armatuurlauad (nĂ€ide on toodud postituse pildil).

Muidugi ei kata sellisel kujul logimine teie teenuste ja erinevate vĂ€liste sĂŒsteemide keerukamaid koostööstsenaariume, kuid sellise korra loomine juba projekti arenduse alguses on ĂŒks neist sammudest, mille eest tĂ€nate end hiljem korduvalt.

Valmis sĂŒsteemi lĂ€htekoodiga saate tutvuda selles projektis: github.com/a-postx/YA.ServiceTemplate

Allikas: habr.com

Osta usaldusvÀÀrne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid - ProHoster