Guia para observabilidade — logging estruturado, métricas, tracing distribuído, health checks e instrumentação. Use quando a tarefa envolver logging, métricas, OpenTelemetry, tracing, health checks ou monitoramento.
logger.LogInformation("User {UserId} created", userId)$"User {userId}" ❌ (usar placeholders)catch (Exception) { }Regra de ouro: Logging estruturado + health checks = 80% da observabilidade.
ILogger<T> via DI no construtor primáriologger.LogInformation("Message {Property}", value)appsettings.json: Information (prod), Debug (dev)AddHealthChecks().AddDbContextCheck<AppDbContext>()app.MapHealthChecks("/health")Cenário: Logging estruturado em UseCase + Health Checks
public class CreateUserUseCase(
IUserRepository repository,
ILogger<CreateUserUseCase> logger) : ICreateUserUseCase
{
public async Task<CreateUserResponseModel> ExecuteAsync(CreateUserInput input, CancellationToken ct = default)
{
logger.LogInformation("Creating user with email {Email}", input.Email);
try
{
if (await repository.ExistsAsync(input.Email, ct))
{
logger.LogWarning("Email {Email} already exists", input.Email);
throw new InvalidOperationException("Email já está em uso.");
}
var user = await repository.CreateAsync(input, ct);
logger.LogInformation("User {UserId} created successfully", user.Id);
return CreateUserPresenter.Present(user);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to create user with email {Email}", input.Email);
throw;
}
}
}
// Program.cs
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>("database")
.AddUrlGroup(new Uri("https://external-api.com/health"), "external-api");
var app = builder.Build();
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
var result = new
{
Status = report.Status.ToString(),
Checks = report.Entries.Select(e => new
{
Name = e.Key,
Status = e.Value.Status.ToString(),
Duration = e.Value.Duration.TotalMilliseconds,
Error = e.Value.Exception?.Message
}),
TotalDuration = report.TotalDuration.TotalMilliseconds
};
await context.Response.WriteAsJsonAsync(result);
}
});
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
}
}
}
Pontos-chave:
{Email}) permitem busca e análisePara hot paths (alto volume), use source generators (evita boxing/alocações):
public static partial class Log
{
[LoggerMessage(EventId = 1001, Level = LogLevel.Information, Message = "Creating user with email {Email}")]
public static partial void CreatingUser(ILogger logger, string email);
[LoggerMessage(EventId = 1002, Level = LogLevel.Information, Message = "User {UserId} created")]
public static partial void UserCreated(ILogger logger, Guid userId);
[LoggerMessage(EventId = 1003, Level = LogLevel.Error, Message = "Failed to create user {Email}")]
public static partial void UserCreationFailed(ILogger logger, string email, Exception exception);
}
// Uso
Log.CreatingUser(logger, input.Email);
var user = await repository.CreateAsync(input, ct);
Log.UserCreated(logger, user.Id);
Para sinks avançados (arquivos, cloud, Elasticsearch):
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
using Serilog;
// Program.cs
builder.Host.UseSerilog((context, config) =>
{
config
.ReadFrom.Configuration(context.Configuration)
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.WriteTo.Console()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day);
});
Para sistemas distribuídos (tracing completo):
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter());
| Nível | Quando Usar |
|---|---|
| Trace | Debug extremamente detalhado (raramente usado) |
| Debug | Informações de debug (apenas dev) |
| Information | Fluxo normal da aplicação (operações importantes) |
| Warning | Situações anormais mas recuperáveis (retry, fallback) |
| Error | Erros que impedem operação (exceções) |
| Critical | Falhas catastróficas (app crash, corrupção de dados) |
// ❌ NÃO fazer
logger.LogInformation("User {Email} logged in with password {Password}", email, password);
// ✅ Fazer
logger.LogInformation("User {Email} logged in successfully", email);