ASP.NET HealthCheck
Health checks são o recurso do ASP.NET Core que expõe, em um endpoint configurável, o estado atual da aplicação e das suas dependências. Neste artigo, você vai implementá-los em uma Web API com .NET 5, verificar um SQL Server, montar um dashboard visual e criar a sua própria verificação.
O que são health checks
Os health checks nada mais são do que um middleware que fornece um endpoint configurável. Ao receber uma requisição, ele responde com o estado de saúde da aplicação.
O recurso está disponível desde a versão 2.2 do ASP.NET Core. Além disso, o pacote Microsoft.AspNetCore.Diagnostics.HealthChecks já vem referenciado implicitamente em aplicações ASP.NET Core.
Segundo a documentação oficial de health checks, orquestradores de contêineres e balanceadores de carga usam esses endpoints como sondas. Por exemplo, um orquestrador pode reiniciar um contêiner que falha na verificação.
Por que monitorar aplicações distribuídas
Trabalhar com um parque de aplicações distribuídas é desafiador e exige atenção, principalmente no monitoramento. Antigamente, era comum criar um endpoint que apenas devolvia o status 200 para indicar que a aplicação estava no ar.
Com o avanço dos microsserviços, essa tarefa se tornou indispensável. Afinal, uma API pode responder normalmente enquanto o banco de dados que ela usa está fora do ar.
Por isso, uma boa verificação de saúde olha além do próprio processo. Ela também testa bancos, filas e serviços externos dos quais a aplicação depende.
Criando o projeto no .NET 5
Neste exemplo, usamos uma Web API simples com o .NET 5. Para criar o projeto, escolha a pasta de destino e execute o comando abaixo.
dotnet new webapi --framework net5.0
Em seguida, abra o projeto no seu editor ou IDE favorito. O template já vem com o Startup.cs, que concentra a configuração de serviços e do pipeline.
Registrando os health checks no Startup
No Startup.cs, adicionamos o AddHealthChecks() sobre a interface IServiceCollection, dentro do método ConfigureServices. Dessa forma, os serviços de health checks ficam disponíveis no container.
public void ConfigureServices(IServiceCollection services)
{
// Registra os serviços de health checks
services.AddHealthChecks();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HealthCheck", Version = "v1" });
});
}
Agora configuramos o middleware e a rota da verificação. Para isso, chamamos UseHealthChecks("/health") no método Configure, que recebe um IApplicationBuilder.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "HealthCheck v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
// Ativa o middleware e define o caminho da verificação
app.UseHealthChecks("/health");
}
O caminho do endpoint é livre e pode seguir o padrão do seu time. Neste exemplo, usamos /health, mas a documentação da Microsoft costuma usar /healthz.
Testando o endpoint /health
Agora executamos a aplicação com o comando abaixo.
dotnet run
Com a aplicação rodando, enviamos uma requisição pelo Postman para https://localhost:5001/health. O resultado aparece na imagem a seguir.
Repare que a resposta traz o status 200 e a mensagem Healthy. Ou seja, a aplicação conseguiu responder no endpoint e, portanto, está saudável.
Status possíveis de um health check
Cada health check devolve um valor da enumeração HealthStatus. Existem três estados possíveis: Healthy, Degraded e Unhealthy.
Por padrão, o middleware converte esses estados em códigos HTTP. Healthy e Degraded retornam 200, enquanto Unhealthy retorna 503 Service Unavailable.
Quando há várias verificações registradas, o pior resultado define o estado geral. Assim, basta uma dependência falhar para o endpoint inteiro reportar o problema.
Se precisar mudar esse mapeamento, a classe HealthCheckOptions oferece a propriedade ResultStatusCodes. Com ela, você define qual código HTTP cada estado deve retornar.
Verificando a integridade do SQL Server
Além da saúde da própria aplicação, podemos estender a verificação para outros serviços. Neste exemplo, vamos checar uma instância do SQL Server.
Para isso, instalamos o pacote AspNetCore.HealthChecks.SqlServer com o comando abaixo.
dotnet add package AspNetCore.HealthChecks.SqlServer
Esse pacote faz parte do projeto open source AspNetCore.Diagnostics.HealthChecks, mantido pela comunidade Xabaril. Vale lembrar que a Microsoft não mantém nem oferece suporte a essa biblioteca.
Além do SQL Server, o projeto oferece pacotes para diversos bancos e serviços. Há opções para PostgreSQL, MySQL, MongoDB, Redis e RabbitMQ, entre outros.
Depois da instalação, adicionamos o método AddSqlServer logo após a chamada do AddHealthChecks().
public void ConfigureServices(IServiceCollection services)
{
// Configura a verificação do SQL Server
services.AddHealthChecks()
.AddSqlServer(
connectionString: Mapping.SqlConnectionString.GetConnectionString(),
name: "SQL Server instance");
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HealthCheck", Version = "v1" });
});
}
Neste exemplo, a classe estática Mapping.SqlConnectionString devolve a string de conexão abaixo. Em produção, no entanto, prefira ler esse valor da configuração da aplicação.
Server=.\SQLExpress;Database=master;Integrated Security=True
Por trás dos panos, a verificação executa um SELECT 1 contra o banco. Se a consulta funcionar, o SQL Server está saudável.
Ao rodar a aplicação, o resultado continua igual ao da imagem anterior. Porém, se desativarmos a instância do SQL Server para simular uma indisponibilidade, a resposta muda.
Dessa vez, o retorno traz a mensagem Unhealthy e o status 503. Assim, qualquer ferramenta de monitoramento consegue detectar a falha automaticamente.
Dashboard com HealthChecks UI
Outra funcionalidade interessante dos health checks é a interface gráfica. Com ela, você visualiza eventos e históricos em uma espécie de dashboard.
Instalando os pacotes
Para isso, instalamos três pacotes na aplicação: AspNetCore.HealthChecks.UI, AspNetCore.HealthChecks.UI.Client e AspNetCore.HealthChecks.UI.InMemory.Storage.
Você pode instalá-los pelo gerenciador do NuGet ou pela linha de comando.
dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspNetCore.HealthChecks.UI.Client
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
Configurando os serviços da interface
Com os pacotes instalados, registramos os serviços AddHealthChecksUI() e AddInMemoryStorage() no Startup.cs.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddCors();
services.AddHealthChecks()
.AddSqlServer(
connectionString: Mapping.SqlConnectionString.GetConnectionString(),
name: "SQL Server instance");
// Configura a interface gráfica e o armazenamento do histórico
services.AddHealthChecksUI(options =>
{
options.SetEvaluationTimeInSeconds(5);
options.MaximumHistoryEntriesPerEndpoint(10);
options.AddHealthCheckEndpoint("API with health checks", "/health");
})
.AddInMemoryStorage(); // Adiciona o banco em memória
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HealthCheck", Version = "v1" });
});
}
Vale destacar as opções que passamos na configuração:
SetEvaluationTimeInSecondsdefine o intervalo entre as verificações.MaximumHistoryEntriesPerEndpointdefine o número máximo de registros no histórico.AddHealthCheckEndpointdefine qual endpoint de health checks o dashboard vai consultar.
Por fim, o método AddInMemoryStorage configura um banco em memória para guardar o histórico das verificações. Em ambientes reais, o projeto também oferece armazenamento em bancos como SQL Server e PostgreSQL.
Habilitando o dashboard no pipeline
No método Configure, primeiro passamos um HealthCheckOptions para o UseHealthChecks. Essa classe permite configurar os delegates Predicate e ResponseWriter.
Depois, chamamos o UseHealthChecksUI, que de fato habilita a interface gráfica.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "HealthCheck v1"));
}
app.UseCors(x => x.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = p => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseHealthChecksUI(options => { options.UIPath = "/dashboard"; });
}
O Predicate igual a true executa todas as verificações registradas. Enquanto isso, o UIResponseWriter.WriteHealthCheckUIResponse gera a resposta em JSON detalhado, no formato que o dashboard entende.
Pronto, agora basta rodar a aplicação e acessar https://localhost:5001/dashboard.
Personalizando o visual do dashboard
Os recursos não param por aí. Também podemos personalizar o dashboard com um arquivo CSS e deixá-lo com a cara da nossa aplicação.
Para isso, criamos um arquivo de estilo na raiz do projeto, chamado aqui de balta-style.css. O conteúdo redefine as variáveis de cor e o logo da interface.
:root {
--bgMain: #1f1f1f; /* cor de fundo */
--fcBase: #fff; /* cor da fonte */
--bcMenuActive: #fffb00; /* cor da borda do item de menu ativo */
--primaryColor: #8625d2; /* cor primária */
--secondaryColor: #9343d1; /* cor secundária */
--bgMenuActive: #7a0bcf6e; /* cor de fundo do item de menu ativo */
--bgButton: #63199b; /* cor de fundo dos botões */
--logoImageUrl: url('https://raw.githubusercontent.com/diegostan/healthchecks-aspnet5/main/images/baltaLogo.png'); /* URL do logo */
--bgAside: var(--primaryColor); /* cor de fundo da barra lateral */
--bgTable: #2f2f2f; /* cor de fundo da tabela */
--bgTableSecondary: #63199b; /* cor secundária da tabela */
}
A lista completa de variáveis está na página de estilos e branding do HealthChecks UI. Neste exemplo, usamos as cores e o logo do balta.io.
Agora adicionamos a opção AddCustomStylesheet, passando o caminho do CSS. O UseHealthChecksUI fica assim.
app.UseHealthChecksUI(options =>
{
options.UIPath = "/dashboard";
options.AddCustomStylesheet("balta-style.css");
});
Ao rodar a aplicação novamente, o dashboard já aparece com o novo visual.
Criando seu próprio health check com IHealthCheck
Existe uma vasta lista de provedores prontos para bancos e serviços. Mesmo assim, podemos criar verificações próprias e ganhar mais flexibilidade com os health checks.
Para isso, basta implementar a interface IHealthCheck. Vamos criar a classe CustomHealthChecks.cs como exemplo.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace HealthCheck
{
public class CustomHealthChecks : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
return Task.FromResult(new HealthCheckResult(
status: HealthStatus.Unhealthy,
description: "API is unhealthy"));
}
}
}
Vamos aos pontos importantes do código:
- A interface
IHealthCheckdefine o métodoCheckHealthAsync. - Esse método precisa retornar um objeto
HealthCheckResult. - O
HealthCheckResultinforma o estado do serviço verificado.
A propriedade status do HealthCheckResult indica a saúde do serviço. Como este é apenas um exemplo, fixamos o valor em Unhealthy, ou seja, o serviço aparece como doente.
Em um cenário real, a lógica dentro do CheckHealthAsync testaria algo concreto. Por exemplo, ela poderia verificar o espaço em disco, a resposta de uma API externa ou o tamanho de uma fila.
Agora registramos a verificação no AddHealthChecks() com o método AddCheck.
services.AddHealthChecks()
.AddSqlServer(
connectionString: Mapping.SqlConnectionString.GetConnectionString(),
name: "SQL Server instance")
.AddCheck<CustomHealthChecks>("Custom health check"); // Registra a verificação customizada
Ao rodar a aplicação, a nova verificação aparece no dashboard com o estado definido.
Health checks com Entity Framework Core
Se a aplicação usa o Entity Framework Core, existe uma alternativa oficial ao pacote da comunidade. Basta instalar o Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.
dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
Depois, registramos a verificação com o método AddDbContextCheck, informando o DbContext da aplicação.
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Mapping.SqlConnectionString.GetConnectionString()));
services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>();
Por padrão, essa verificação chama o método CanConnectAsync do EF Core. Ou seja, ela confirma se a aplicação consegue abrir uma conexão com o banco configurado no contexto.
Caso precise de um teste mais específico, as sobrecargas do AddDbContextCheck permitem trocar a operação executada. Por exemplo, você pode consultar uma tabela essencial em vez de apenas abrir a conexão.
Consumindo o JSON dos health checks
Até aqui, usamos os health checks para verificar serviços e montamos um dashboard amigável. Além disso, também podemos consumir diretamente o JSON publicado no endpoint.
Com esse JSON, é possível criar painéis unificados que mostram o estado de várias aplicações ao mesmo tempo. Ao acessar o endpoint /health, configurado no início do artigo, obtemos o resultado abaixo.
Dessa forma, você pode integrar o endpoint a qualquer ferramenta de monitoramento ou automação. Na prática, o JSON traz o estado geral, a duração e o resultado de cada verificação.
Se preferir enviar os resultados em vez de esperar consultas, o ASP.NET Core oferece a interface IHealthCheckPublisher. Quando você a registra no container, o sistema executa as verificações periodicamente e chama o método PublishAsync com o relatório.
Com isso, fica simples encaminhar o estado das verificações para uma ferramenta de observabilidade. As opções Delay e Period controlam quando a primeira execução acontece e o intervalo entre as seguintes.
UseHealthChecks ou MapHealthChecks
Neste artigo, usamos o UseHealthChecks, que registra o middleware diretamente no pipeline. Porém, o ASP.NET Core também oferece o MapHealthChecks, que registra os health checks como um endpoint de roteamento.
Segundo a Microsoft, o UseHealthChecks encerra o pipeline assim que a requisição corresponde ao caminho. Isso evita trabalho desnecessário, como logs e outros middlewares.
Por outro lado, o MapHealthChecks permite usar middlewares que conhecem o endpoint, como a autorização. Assim, você pode proteger o caminho da verificação com uma política específica.
Para entender melhor como o pipeline funciona, vale ler o artigo sobre middleware no ASP.NET. Ele explica a ordem de execução e como criar o seu próprio componente.
Liveness e readiness
Em ambientes como o Kubernetes, é comum separar duas perguntas diferentes. A sonda de liveness responde se a aplicação travou e precisa reiniciar.
Já a sonda de readiness responde se a aplicação está pronta para receber tráfego. Por exemplo, uma API pode estar viva, mas ainda aguardando a conexão com o banco.
Para separar as duas, você registra as verificações com tags e filtra cada endpoint pelo Predicate. Dessa forma, o orquestrador decide com precisão quando reiniciar e quando apenas segurar o tráfego.
Fora do Kubernetes, o próprio Docker oferece a instrução HEALTHCHECK. Com ela, o contêiner chama periodicamente o endpoint de health checks e marca a si mesmo como saudável ou não.
HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit 1
Vale notar que as imagens Linux do .NET não trazem o curl por padrão. Portanto, instale o pacote no Dockerfile ou use o wget em imagens baseadas em Alpine.
Conclusão sobre health checks
Neste artigo, vimos o que são os health checks e como configurar essa ferramenta tão útil em uma aplicação ASP.NET 5. Também verificamos o SQL Server, montamos um dashboard, personalizamos o visual e criamos uma verificação própria.
Com poucos ajustes, sua API deixa de responder apenas se está no ar. Em vez disso, ela passa a informar com clareza a saúde de cada dependência.
O código-fonte completo deste exemplo está disponível no repositório ExemploHealthChecks no GitHub.
FAQ
Qual a diferença entre Degraded e Unhealthy?
Degraded indica que o serviço funciona com limitações e, por padrão, retorna 200. Já Unhealthy indica falha e retorna 503.
Preciso instalar algum pacote para usar health checks?
Não para o uso básico, porque o pacote principal já vem referenciado no ASP.NET Core. Pacotes extras só são necessários para verificar bancos, filas ou exibir o dashboard.
O pacote AspNetCore.HealthChecks.SqlServer é da Microsoft?
Não. Ele faz parte do projeto open source da comunidade Xabaril, que a Microsoft não mantém.
Posso proteger o endpoint de health checks com autenticação?
Sim. Com o MapHealthChecks, você aplica uma política de autorização ao endpoint, como em qualquer outra rota.
O histórico do dashboard sobrevive a um reinício?
Não com o AddInMemoryStorage, porque os dados ficam apenas na memória. Para manter o histórico, use um provedor de armazenamento persistente.