DEV Community

Otávio Larrosa
Otávio Larrosa

Posted on

Trabalhando com correlationId em .Net + Serilog

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:

  1. Serilog
  2. Serilog.AspNetCore
  3. 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>
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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.");
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

E assim, temos como resultado, um template bem simples de ler, e com o correlationId :)

correlationIdTemplate

Top comments (0)