
Logging ass e ganz wichtegt Entwécklerinstrument, awer wann Dir verdeelt Systemer erstellt, gëtt et e Steen deen direkt an d'Fundament vun Ärer Applikatioun geluecht muss ginn, soss wäert d'Komplexitéit vun der Entwécklung vu Mikroservicer séier seng Maut huelen.
.Net Kär 3 huet eng grouss dobäi , Also wann Är Uwendungen direkt HTTP-Uriff fir Inter-Service Kommunikatioun benotzen, da kënnt Dir vun dëser Out-of-the-Box Funktionalitéit profitéieren. Wéi och ëmmer, wann Är Backend Architektur Interaktioun duerch e Message Broker (RabbitMQ, Kafka, etc.) involvéiert, da musst Dir Iech nach ëmmer Suergen iwwer d'Korrelatiounskontext duerch dës Messagen selwer passéieren.
An dësem Artikel wäerte mir eng einfach Web-API Applikatioun huelen an de Logbicher organiséieren, wat wäert
Enn-zu-Enn Korrelatioun tëscht Logbicher vun onofhängege Servicer behalen, sou datt Dir ganz einfach all Aktivitéite kuckt, déi duerch eng spezifesch Ufro vum Client verursaacht goufen
hunn en eenzegen Entréespunkt mat praktescher Analyse, sou datt souguer Support de Loggingsinstrument benotze kann, op déi Froen wéi "Ech hunn e Feeler an der Applikatioun mat esou an esou enger Ufro ID" benotzt kënne ginn
Als éischt musse mir iwwer e Logbicher fir eis Applikatioun entscheeden. D'Haaptfuerderung fir modern Logbicher ass Struktur, d.h. mir sollen net mat flaach SMSen schaffen, mä mat Objete. Dank esou Logbicher kënne mir ganz einfach Meenunge vun eise Messagen aus verschiddene Perspektiven bauen an Analyse maachen.
Fir eis Applikatioun benotze mir de Serilog Package, deen exzellent Ënnerstëtzung fir strukturéiert Logbicher an e räiche Add-on System huet. Ech sprangen d'Basis Schrëtt fir se opzestellen (Dir kënnt eng grouss Unzuel vun Artikelen iwwer dëst Thema fannen) a maachen d'Annahme datt
Serilog ass scho konfiguréiert an ass de Standardlogger fir Ären Ofhängegkeetsinjektiounsprovider
seng Konfiguratioun enthält d'Beräicherung vu Messagen mat Kontexteigenschaften (Enrich.FromLogContext)
De nächste Schrëtt ass ze wielen op wéi eng zentraliséiert Log Sammlung System Messagen vu Serilog ze schécken. Vläicht ass déi heefegst Open Source Optioun haut den ELK Stack (Elasticsearch, Logstash a Kibana), also loosst eis et huelen. Fir dëst ze maachen, wäerte mir d'Offer benotzen vun - nodeems Dir e gratis Plang ugemellt hutt, hu mir all d'Kraaft vun der Lucene Sichmotor an eisen Hänn.
Alles wat mir maache mussen ass de Package un eise Projet bäidroen
Install-Package Serilog.Sinks.Logzio
A füügt den entspriechende Enricher un d'Konfiguratioun vun eisem Logger, fiddert et en Zougangstoken
LoggerConfiguration loggerConfig = new LoggerConfiguration();
loggerConfig.WriteTo.Logzio(secrets.LogzioToken, 10, TimeSpan.FromSeconds(10), null, LogEventLevel.Debug);
Andeems Dir d'Applikatioun lancéiert, kënne mir eis Messagen net nëmmen an der Konsole beobachten, awer och a Kibana.

Interfaces

An enger Service-Typ Applikatioun kënne mir zwee Haaptschnëttplazen fir seng Interaktioun mat der Äussewelt ënnerscheeden, loosst eis se als vertikal an horizontal bezeechnen. Déi vertikal Interface ass eng Web API duerch déi Uriff vun der Clientapplikatioun ukommen. Horizontal ass e Message Broker dee benotzt gëtt fir Daten mat aneren internen Servicer auszetauschen.
Loosst eis d'Etappe vun der Aféierung vun Korrelatioun op jiddereng vun dësen Interfaces betruechten.
Korrelatioun an HTTP Ufroen
Fir sou vill wéi méiglech Informatiounen ze kréien, musse mir e Korrelatiounsidentifizéierer generéieren sou no wéi méiglech un den Start vun der Aktivitéit, d.h. op der Paart oder direkt um Client (mobil oder Web). Zënter haut hu mir mat enger Backend Applikatioun ze dinn, wäerte mir einfach d'Ufuerderung fir den obligatoresche "X-Correlation-ID" Header an all Ufro un d'Web API uginn.
Dobäizemaachen e Pak , deem seng Funktioun ass de Wäert vum Header ze huelen dee mir brauchen
Install-Package CorrelationID
Loosst eis et an d'Ufroveraarbechtungspipeline addéieren
public class Startup
{
public void Configure(IApplicationBuilder application)
{
application
.UseCorrelationId(new CorrelationIdOptions
{
Header = "X-Correlation-ID",
IncludeInResponse = false,
UpdateTraceIdentifier = false,
UseGuidForCorrelationId = false
});
}
}
Elo benotze mir et fir en einfachen Handlungsfilter ze kreéieren:
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();
}
}
A füügt et un de Controller
[Route("[controller]")]
[ApiController]
[ServiceFilter(typeof(ApiRequestFilter))]
public class CarsController : ControllerBase
{
}
Als Resultat weist de Controller 400 Schlecht Ufro fir all Ufroen ouni Header mat dem entspriechende Identifizéierer.
Nodeems mir ugefaang hunn en Identifizéierer vum Client ze kréien, musse mir et an de Logbuchkontext addéieren, mir maachen eng Framingschicht fir dëst:
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);
}
}
}
An eiser Applikatioun benotze mir de Standard ILogger vum Microsoft.Extensions.Logging.Abstractions Package, also addéiere mer de Wäert mat enger einfacher Erweiderung.
public static IDisposable BeginScopeWith(this ILogger logger, params (string key, object value)[] keys)
{
return logger.BeginScope(keys.ToDictionary(x => x.key, x => x.value));
}
Mir fügen eng Schicht un d'Ufroveraarbechtungspipeline bäi a kréien dat gewënschte Resultat.
public class Startup
{
public void Configure(IApplicationBuilder application)
{
application.UseMiddleware<CorrelationIdContextLogger>();
}
}
Elo enthalen all Aktivitéiten, déi duerch Ufroe fir eis Web API generéiert ginn, e Korrelatiounsidentifizéierer mat deem se einfach verlinkt kënne ginn.

Korrelatioun an Broker Messagen
De nächste Schrëtt ass d'Iwwerdroung an Empfang vum Korrelatiounsidentifizéierer duerch de Message Broker opzestellen. An eisem Beispill benotze mir RabbitMQ, an huelen de MassTransit Kader als Client. Nach eng Kéier, loosst eis den initialen Setup vun der Aarbecht mat MassTransit iwwersprangen a gitt direkt op d'Protokolléierung opzestellen.
Fir unzefänken kënne mir d'Logbicher vum MassTransit selwer enthalen; dofir addéiere mir e Package un eis Applikatioun
Install-Package MassTransit.SerilogIntegration
Elo nodeems Dir de Logger an d'MassTransit Astellunge bäigefüügt hutt, kënne mir de Framework Logbicher gesinn.
services
.AddSingleton(provider =>
{
return Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.UseSerilog();
});
});
Loosst eis Applikatioun op eng POST Ufro äntweren andeems Dir en SomethingDoneMessage Event mam Wäert "gemaach" schéckt. De Kontrakt fir esou e Message kann wéi follegt beschriwwe ginn:
namespace MbMessages
{
public interface ISomethingDoneMessageV1
{
string Value { get; }
}
}
MassTransit Messagen sinn am Fong eng Enveloppe an där Broker Messagen zougemaach sinn. D'Enveloppe gesäit sou aus:
{
"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"
}
}
De Message weist Servicefelder déi néideg sinn fir d'Operatioun vum Kader selwer, awer mir hunn d'Fähigkeit fir eis eegen zousätzlech Eegeschafte fir dës Enveloppe ze addéieren. Ausserdeem huet MassTransit agebaute Tools fir mat e puer fakultativ Felder ze schaffen, déi interessantst vun deenen ass de Korrelatiounsidentifizéierer CorrelationId.
Loosst eis d'CorrelatedBy Interface un de Message Kontrakt addéieren:
namespace MbMessages
{
public interface ISomethingDoneMessageV1 : CorrelatedBy<Guid>
{
string Value { get; }
}
}
Loosst eis et implementéieren an e Wäert un d'CorrelationId Eegeschafte zouginn wann Dir e Message erstellt:
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; }
}
Wa mir den aktualiséierten Message kucken, wäerte mir gesinn datt de Korrelatiounsidentifizéierer net nëmmen en Deel vun eisem Message ginn ass, awer och en Deel vun der Enveloppe - dësen Identifizéierer gëtt elo och an all MassTransit Logbicher benotzt, dat heescht datt et vill méi einfach gëtt fir eis mat Probleemer um Message Broker Niveau ze këmmeren.
{
"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"
}
}
Alles wat mir maache mussen ass d'Protokolléierung vun dëse Serviceeigenschaften vun der Noriicht konfiguréieren; dofir addéiere mir e Package zum Projet . De Package füügt e Filter un d'MassTransit Message Veraarbechtungspipeline bäi, déi de Message Kontext op e thread-séchere Stack stackelt. Serilog liest de Kontext aus dem Stack a füügt dës zousätzlech Eegeschafte fir eis Logobjekte bäi.
Install-Package Serilog.Enrichers.MassTransitMessage
Am MassTransit setzen mir e Filter an
services
.AddSingleton(provider =>
{
return Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.UseSerilog();
cfg.UseSerilogMessagePropertiesEnricher();
});
});
An an der Serilog Konfiguratioun addéiere mer en Enricher
Log.Logger = new LoggerConfiguration()
.Enrich.FromMassTransitMessage()
.CreateLogger();
Zënter der Applikatioun déi de Message vun der RabbitMQ Schlaang kritt huet Zougang zu all d'Eegeschafte vun der MassTransit Enveloppe, kënne mir de resultéierende Korrelatiounsidentifizéierer bannent der konsuméierender Applikatioun benotzen, wéi och weider laanscht d'Uruffkette weiderginn.
Als Resultat hunn eis Logbicher ugefaang CorrelationId net nëmmen an engem Service ze enthalen, awer och wann se mat aneren Uwendungen interagéieren.

Also, de resultéierende Logsystem an .Net Uwendungen erlaabt eis Logbicher vu komplett verschiddene Mikroservicer ouni Probleemer ze korreléieren - och déi, déi duerch e Message Broker funktionnéieren. A mat der Hëllef vun Elasticsearch kënne mir séier a bequem Logbicher analyséieren andeems Dir d'Dashboards baut déi mir an Kibana brauchen (e Beispill gëtt op der Foto fir de Post gewisen).
Natierlech wäert d'Aloggen an dëser Form net déi komplex Interaktiounen tëscht Äre Servicer a verschiddenen externen Systemer ofdecken, awer dës Uerdnung am Ufank vun der Entwécklung vum Projet opzebauen ass eng vun deene Saachen, fir déi Dir Iech méi wéi eemol Merci wäert soen.
Dir kënnt de Quellcode vum resultéierende System am Projet verstoen:
Source: will.com
