
ãã®ã³ã°ã¯éçºè ã«ãšã£ãŠéåžžã«éèŠãªããŒã«ã§ããã忣ã·ã¹ãã ãæ§ç¯ããéã«ã¯ãã¢ããªã±ãŒã·ã§ã³ã®åºç€ã«ãã£ãããšçµã¿èŸŒãå¿ èŠãããéèŠãªããŒã«ã«ãªããŸããããããªããšããã€ã¯ããµãŒãã¹ã®éçºã®è€éããããã«æããã«ãªããŸãã
.Net Core 3ã§ã¯çŽ æŽãããæ©èœã远å ãããŸãã ãããã£ãŠãã¢ããªã±ãŒã·ã§ã³ããµãŒãã¹ééä¿¡ã«çŽæ¥ HTTP åŒã³åºãã䜿çšããå Žåã¯ããã®ããã«äœ¿çšã§ããæ©èœãå©çšã§ããŸãããã ããããã¯ãšã³ã ã¢ãŒããã¯ãã£ã«ã¡ãã»ãŒãž ãããŒã«ãŒ (RabbitMQãKafka ãªã©) ãä»ããããåããå«ãŸããå Žåã¯ããããã®ã¡ãã»ãŒãžãä»ããŠçžé¢ã³ã³ããã¹ããæž¡ãããšãèªåã§èæ ®ããå¿ èŠããããŸãã
ãã®èšäºã§ã¯ãã·ã³ãã«ãªWeb APIã¢ããªã±ãŒã·ã§ã³ãåãäžãããã°ãæŽçããŠ
ç¬ç«ãããµãŒãã¹ã®ãã°éã®ãšã³ãããŒãšã³ãã®çžé¢é¢ä¿ãç¶æããã¯ã©ã€ã¢ã³ãããã®ç¹å®ã®ãªã¯ãšã¹ãã«ãã£ãŠçºçãããã¹ãŠã®ã¢ã¯ãã£ããã£ãç°¡åã«è¡šç€ºã§ããããã«ããŸãã
䟿å©ãªåææ©èœãåããåäžã®ãšã³ããªãã€ã³ãããããããããã®ãããªãªã¯ãšã¹ãIDã®ã¢ããªã±ãŒã·ã§ã³ã§ãšã©ãŒãçºçããŸããããªã©ã®è³ªåãåãããµããŒãæ åœè ã§ãããã°ããŒã«ã䜿çšã§ããŸãã
ãŸããã¢ããªã±ãŒã·ã§ã³ã®ãã°èšé²ãããã€ããŒã決å®ããå¿ èŠããããŸããææ°ã®ãã°èšé²ã®äž»ãªèŠä»¶ã¯æ§é ã§ããã€ãŸãããã©ãããªããã¹ã ã¡ãã»ãŒãžã§ã¯ãªãããªããžã§ã¯ãã䜿çšããŠäœæ¥ããå¿ èŠããããŸãããã®ãããªãã°ã®ãããã§ãããŸããŸãªã»ã¯ã·ã§ã³ã§ã¡ãã»ãŒãžã®è¡šçŸãç°¡åã«æ§ç¯ããåæãå®è¡ã§ããŸãã
ç§ãã¡ã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãæ§é åãã°ãšè±å¯ãªã¢ããªã³ ã·ã¹ãã ã®åªãããµããŒããåãã Serilog ããã±ãŒãžã䜿çšããŸããåºæ¬çãªèšå®æé ã¯çç¥ããŸãïŒãã®ãããã¯ã«ã€ããŠã¯å€ãã®èšäºããããŸãïŒã
Serilog ã¯ãã§ã«æ§æãããŠãããäŸåæ§æ³šå ¥ãããã€ããŒã®ããã©ã«ãã®ãã¬ãŒã§ãã
ãã®æ§æã«ã¯ãã³ã³ããã¹ã ããããã£ã«ããã¡ãã»ãŒãžã®åŒ·åãå«ãŸããŸã (Enrich.FromLogContext)
次ã®ã¹ãããã¯ãSerilog ããã¡ãã»ãŒãžãéä¿¡ããéäžãã°åéã·ã¹ãã ãéžæããããšã§ããããããã仿¥ã®ãªãŒãã³ãœãŒã¹ ãœãããŠã§ã¢ã®æãäžè¬çãªãªãã·ã§ã³ã¯ ELK ã¹ã¿ã㯠(ElasticsearchãLogstashãKibana) ãªã®ã§ããããæ¡çšããŸãããããããè¡ãã«ã¯ã â ç¡æãã©ã³ã«ç»é²ãããšãLucene æ€çŽ¢ãšã³ãžã³ã®å šæ©èœãå©çšã§ããããã«ãªããŸãã
æ®ã£ãŠããã®ã¯ããããžã§ã¯ãã«ããã±ãŒãžã远å ããããšã ãã§ãã
Install-Package Serilog.Sinks.Logzio
ãããŠã察å¿ãããšã³ãªããã£ãŒããã¬ãŒèšå®ã«è¿œå ããã¢ã¯ã»ã¹ããŒã¯ã³ãäŸçµŠããŸãã
LoggerConfiguration loggerConfig = new LoggerConfiguration();
loggerConfig.WriteTo.Logzio(secrets.LogzioToken, 10, TimeSpan.FromSeconds(10), null, LogEventLevel.Debug);
ã¢ããªã±ãŒã·ã§ã³ãå®è¡ãããšãã³ã³ãœãŒã«ã ãã§ãªã Kibana ã§ãã¡ãã»ãŒãžã確èªã§ããããã«ãªããŸãã

ã€ã³ã¿ãŒãã§ã€ã¹

ãµãŒãã¹åã¢ããªã±ãŒã·ã§ã³ã§ã¯ãå€éšãšã®ããåãã«ãã㊠2 ã€ã®äž»èŠãªã€ã³ã¿ãŒãã§ãŒã¹ãåºå¥ã§ããŸããããããåçŽãšæ°Žå¹³ãšããŠæå®ããŸããåçŽã€ã³ã¿ãŒãã§ãŒã¹ã¯ãã¯ã©ã€ã¢ã³ã ã¢ããªã±ãŒã·ã§ã³ããã®åŒã³åºããå°çãã Web API ã§ãã Horizoâântal ã¯ãä»ã®å éšãµãŒãã¹ãšããŒã¿ã亀æããããã«äœ¿çšãããã¡ãã»ãŒãž ãããŒã«ãŒã§ãã
ãããã®åã€ã³ã¿ãŒãã§ãŒã¹ã§çžé¢é¢ä¿ãå®è£ ããæ®µéãèããŠã¿ãŸãããã
HTTPãªã¯ãšã¹ãã®çžé¢é¢ä¿
ã§ããã ãå€ãã®æ å ±ãååŸããã«ã¯ãã¢ã¯ãã£ããã£ã®éå§ã«ã§ããã ãè¿ãæç¹ã§ãã€ãŸãã²ãŒããŠã§ã€äžãŸãã¯ã¯ã©ã€ã¢ã³ã (ã¢ãã€ã«ãŸã㯠Web) äžã§çŽæ¥çžé¢èå¥åãçæããå¿ èŠããããŸãã仿¥ã¯ããã¯ãšã³ã ã¢ããªã±ãŒã·ã§ã³ãæ±ã£ãŠããã®ã§ãWeb API ãžã®ãã¹ãŠã®ãªã¯ãšã¹ãã«å¿ é ã®ãX-Correlation-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 ãžã®ãªã¯ãšã¹ãã«ãã£ãŠçæããããã¹ãŠã®ã¢ã¯ãã£ããã£ã«ã¯çžé¢èå¥åãå«ãŸããŠãããããã«ããç°¡åã«ãªã³ã¯ã§ããŸãã

ãããŒã«ãŒã¡ãã»ãŒãžã®çžé¢é¢ä¿
次ã«èšå®ããå¿ èŠãããã®ã¯ãã¡ãã»ãŒãž ãããŒã«ãŒãä»ããçžé¢èå¥åã®éåä¿¡ã§ãããã®äŸã§ã¯ãRabbitMQ ã䜿çšããMassTransit ãã¬ãŒã ã¯ãŒã¯ãã¯ã©ã€ã¢ã³ããšããŠäœ¿çšããŸããããã§ããMassTransit ãæäœããããã®åæèšå®ãã¹ãããããŠããã°ã®èšå®ã«çŽæ¥é²ã¿ãŸãããã
ãŸããMassTransitèªäœã®ãã°ãæå¹ã«ããããšãã§ããŸãããã®ããã«ã¯ãã¢ããªã±ãŒã·ã§ã³ã«ããã±ãŒãžã远å ããŸãã
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"
}
}
ã¡ãã»ãŒãžã®ãããã®ãµãŒãã¹ããããã£ã®ãã°èšé²ãæ§æããå¿ èŠããããŸãããã®ããã«ã¯ããããžã§ã¯ãã«ããã±ãŒãžã远å ããŸãã ããã®ããã±ãŒãžã¯ãã¡ãã»ãŒãž ã³ã³ããã¹ããã¹ã¬ããã»ãŒã ã¹ã¿ãã¯ã«ã¹ã¿ãã¯ãããã£ã«ã¿ãŒã 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 ã¢ããªã±ãŒã·ã§ã³ã®ãã° ã·ã¹ãã ã«ãããã¡ãã»ãŒãž ãããŒã«ãŒãä»ããŠåäœãããã€ã¯ããµãŒãã¹ã§ãã£ãŠãããŸã£ããç°ãªããã€ã¯ããµãŒãã¹ããã®ãã°ãåé¡ãªãé¢é£ä»ããããšãã§ããŸãããŸããElasticsearch ã®å©ããåããŠãKibana ã§å¿ èŠãªããã·ã¥ããŒããæ§ç¯ããããšã§ããã°ããã°ãã䟿å©ã«åæã§ããŸã (æçš¿ã®ç»åã«äŸã瀺ãããŠããŸã)ã
ãã¡ããããã®ãã©ãŒã ã§ãã°ã€ã³ããŠãããµãŒãã¹ãšããŸããŸãªå€éšã·ã¹ãã éã®è€éãªããåãã¯ã«ããŒãããŸãããããããžã§ã¯ãéçºã®æåã«ãã®ãããªé åºã確ç«ããããšã¯ãäœåºŠãæè¬ããããšã«ãªãã§ãããã
ãããžã§ã¯ãå ã®çµæãšããŠåŸãããã·ã¹ãã ã®ãœãŒã¹ ã³ãŒããçè§£ã§ããŸãã
åºæïŒ habr.com
