Em aplicações distribuídas, uma das coisas mais importantes que devemos ter, é rastreabilidade, principalmente de erros, e uma prática muito comum e eficiente, é trabalharmos com correlation id’s, para diferentes requisições ou workers.
Para implementarmos esse conceito, iremos utilizar como padrão, um exemplo de API, que pode ser encontrada em: otaviolarrosa/DotnetApi (github.com).
Primeiro, iremos utilizar os seguintes pacotes:
- Serilog
- Serilog.AspNetCore
- Serilog.Enrichers.CorrelationId
Conforme exemplificado no arquivo .csproj da aplicação
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.9" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
<PackageReference Include="Serilog.Enrichers.CorrelationId" Version="3.0.1" />
<PackageReference Include="Serilog.Enrichers.Memory" Version="1.0.4" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\StreamNet\StreamNet\StreamNet.csproj" />
<ProjectReference Include="..\Application\Application.csproj" />
<ProjectReference Include="..\Consumers\Consumers.csproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
<ProjectReference Include="..\Producers\Producers.csproj" />
</ItemGroup>
</Project>
Após isso, também é necessário configurar no startup do projeto, alguns pontos para utilizarmos o Serilog, tais como:
- Utilização do Serilog com o Enricher do CorrelationId
- Utilização do Serilog, para utilizar o CorrelationId que vem do chamador da requisição, caso queira uma rastreabilidade integrada
- Formatação de um template para facilitar a leitura dos logs
using Application.Extensions;
using Consumers;
using Infrastructure.Extensions;
using Producers;
using Serilog;
using StreamNet;
var builder = WebApplication.CreateBuilder(args);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.WithCorrelationId() //Aqui, pedimos para o Serilog utilizar a feature de correlation Id.
.Enrich.WithCorrelationIdHeader() //Aqui, pedimos para utilizar o correlation Id, de quem solicitou a chamada na requisição HTTP, para que o trace fique ainda mais rico, com os logs do frontend.
.WriteTo.Console(outputTemplate: "[{CorrelationId} - {Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}") //Aqui, formatamos um template para formatação dos logs padronizados, à fim de facilitar a leitura.
.CreateLogger();
builder.Host.UseSerilog(); //Adicionamos o Serilog dentro do Host do nosso serviço
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton(builder.Logging);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.RegisterApplicationDependencies(builder.Configuration);
builder.Services.RegisterInfrastructureDependencies(builder.Configuration);
builder.Services.AddProducers();
builder.Services.AddConsumers();
builder.Services.AddTopicManagement();
var app = builder.Build();
app.RunMigrations();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
Após isso, temos apenas que utilizar a interface ILogger da abstração Microsoft.Extensions.Logging, como na controller abaixo:
using Application.UseCases.User.CreateUser.Input;
using Application.UseCases.User.RequestCreateUser;
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly IRequestCreateUserUseCase _requestCreateUserUseCase;
private readonly ILogger<UserController> _logger;
public UserController(IRequestCreateUserUseCase requestCreateUserUseCase, ILogger<UserController> logger)
{
_requestCreateUserUseCase = requestCreateUserUseCase;
_logger = logger;
}
[HttpPost]
[Route("/user")]
public async Task<IActionResult> CreateUser([FromBody]RequestCreateUserInput userInput)
{
try
{
_logger.LogInformation("Starting request {method} with params {@input}", nameof(CreateUser), userInput);
await _requestCreateUserUseCase.ExecuteAsync(userInput);
_logger.LogInformation("Ended request {method} with params {@input}", nameof(CreateUser), userInput);
return Ok();
}
catch (Exception ex)
{
_logger.LogError(ex, "A unexpected error occurred in {method} with params {@input}", nameof(CreateUser), userInput);
return StatusCode((int)HttpStatusCode.InternalServerError, "A unexpected error occurred in CreateUser.");
}
}
}
}
E assim, temos como resultado, um template bem simples de ler, e com o correlationId :)
Top comments (0)