Criando um dashboard de healthcheck personalizado para suas WebAPIs em .NET 6

Guilherme Schiavone
Tradeback
Published in
7 min readJun 9, 2022

Seja bem-vindo!

O que são Health Checks?

Hoje vou comentar sobre uma necessidade que vim explorando nos últimos tempos e que acabou gerando insights bem legais na solução. Me refiro a prática de healthchecks nas aplicações e os recursos que essas consomem.

Normalmente, realizar healthchecks é uma boa prática nas diferentes aplicações que escrevemos no dia a dia, onde nos permite avaliar a saúde das aplicações e os recursos que essas consomem. Por exemplo, imagine que haja o desejo de avaliar a conectividade com o database em uma API, para verificar se a instância está disponível e se a aplicação está conseguindo estabelecer uma conexão bem-sucedida com essa base.

Poderíamos, claro, debugar o código testando a conectividade com recursos específicos em diferentes ambientes ou analisar a instância criada, entretanto, isso normalmente é custoso em termos de tempo. Para isso, existe o healthcheck, sendo um recurso que permite que com a chamada a um único endpoint na API consigamos avaliar a conectividade com essa base de dados e outros muitos serviços que essa API possa estar se integrando.

Apresentando o problema

Estando o conceito de healthcheck apresentado, podemos partir para nosso estudo de caso.

Recentemente, houve uma necessidade de apresentarmos os diferentes estados de integridade de nossas API´s que são consumidas por diferentes clientes, para que estes acompanhassem em tempo real a disponibilidade dos diferentes serviços.

Claro, poderíamos construir uma solução completa para proporcionar essa métrica, criando um SPA e uma API que disponibilizasse uma conexão por websocket para atualizar esse dado em tempo real. Entretanto, isso seria relativamente custoso, considerando toda infra que deveria ser criada e tudo que deveria ser codificado.

Realizando mais estudos, notei que havia uma solução de UI para healthchecks já out of the box para o .NET e resolvi implementar.

No .NET 6 e suas versões anteriores (a partir do .NET Core 2.2), existe um recurso de healthcheck que podemos implementar de forma muito fácil em nossas aplicações, simplesmente configurando um endpoint para essa métrica e definindo quais checks serão realizados (existem checks já out of the box abstraídos e podemos também criar checks personalizados).

Porém, também existe uma lib que faz parte desse mesmo módulo, que nos permite a criação de um dashboard de forma automatizada para a disponibilização desses resultados. Essa é a AspNetCore.HealthChecks.UI.

Com ela, podemos criar checks personalizados que chamam as rotas /health (ou qualquer outra em que os checks estejam configurados) dos nossos diferentes serviços e centralizar em uma única API e dashboard esses resultados de integridade e degradação, dessa forma, solucionando nossa demanda.

Implementando o dashboard

Bem, podemos parar com a enrolação e começar a codar! :)

Primeiramente, criei um novo projeto e solução utilizando o template de WebApi com .NET 6 pelo Visual Studio. (ocultarei essa parte, pois não é o foco do artigo)

Agora, precisamos configurar nossas injeções de dependência em nosso container de serviços. Para isso, costumo criar uma classe orquestradora chamada “Startup”.

Criei uma segunda classe para centralizar a injeção de dependências personalizadas, deixando a Startup somente como orquestradora desse processo.

Depois de registrar os serviços de healthcheck e os checks personalizados que faço na linha:

services.RegisterHealthChecks(Configuration);

Adiciono a injeção da UI de HealthChecks (servirá para a geração do dashboard de forma automatizada) e faço algumas configurações nele:

  • Primeiro, defino qual será o intervalo em que os healthchecks serão atualizados em segundos, isso é, de quanto tempo em quanto tempo serão feitos esses healthchecks. No caso, settei para uma hora (3600 segundos). Isso é feito na linha:

options.SetEvaluationTimeInSeconds(3600);

  • O dashboard também tem um mecanismo de histórico para apresentar quais foram os estados de integridade ao longo do tempo, onde podemos dizer qual o tamanho máximo desse histórico que será apresentado. No caso, settei como 5 na linha:

options.MaximumHistoryEntriesPerEndpoint(5);

  • Configurei quais endpoints serão consultados para disponibilizar esses resultados, no caso, da própria API de healthcheck (aqui, passamos o endpoint em que os checks foram configurados). Isso é feito na linha:

options.AddHealthCheckEndpoint(“API´s”, “/health-api”);

Digo qual o nome do endpoint que será apresentado e qual path relativo será feito o request.

Por fim, defino em qual lugar será armazenado esses resultados de integridade que serão apresentados no dashboard (serve principalmente para o histórico e é obrigatório definir). Nesse cenário, estou definindo que serão armazenados em um banco em memória mesmo:

.AddInMemoryStorage();

Entretanto, a lib nos dá a oportunidade de armazenar em diferentes bases de dados que nos garantem mais integridade, confidencialidade e longividade (MySql, SqlServer, etc…)

Aqui, estamos adicionando os serviços dependentes para o healthcheck e injetando os checks personalizados com seus respetivos nomes. Podemos utilizar também os checks abstraídos da lib como o do SqlServer (AddSqlServer()), do Redis (AddRedis()), entre muitos outros. Você pode consultar outros tipos de checks já prontos no repo do módulo aqui.

Nesse caso, estou registrando dois checks em API´s, sendo uma chamada Manager e outra chamada Promo. Como vou chamar a rota de health delas, também estou injetando um HttpClient usando a estratégia do HttpClientFactory tipado.

Esse método de extensão é chamado na linha da classe Startup:

services.RegisterHealthChecks(Configuration);

Implementação dos checks personalizados:

Para os dois checks, como ambos possuem uma estrutura muito próxima em termos de lógica, onde consultaremos as rotas de /health das respectivas API´s, resolvi criar uma base class para ter essa operação virtual de checks:

Para criar um check personalizado, basta criar uma classe que implemente a interface IHealthCheck e definir um corpo para o método CheckHealthCheckAsync. Nesse caso, chamo a API específica e leio o conteúdo da resposta. Se o status code não possuir sucesso (!= 200) ou a API retornar Unhealthy, é settado que aquele serviço não está integro.

Além desses dois estados, é possível definir um terceiro estado chamado Degraded, em que podemos especificar que está íntegro o retorno, entretanto, tem algo afetando o tempo de resposta.

Voltando a Startup:

Estando nossos checks e UI configurados, agora só precisamos adicionar algumas dependências ao ApplicationBuilder.

Depois das configurações padrões, configuramos o endpoint de health check que chamará nossos checks personalizados. Nele, passamos duas props. Isso é feito nas linhas:

app.UseHealthChecks(“/health-api”, new HealthCheckOptions

{

ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,

Predicate = (registration) => registration.Tags.Contains(“api”)

});

A primeira é o ResponseWriter, onde podemos definir um serializador personalizado que será executado sempre que a rota for chamada (é muito útil quando queremos definir um contrato personalizado para esses endpoints de healthcheck), nesse caso, estou passando o default que define o contrato para a UI consumir.

A segunda que podemos passar é o Predicate, em que podemos dizer quais checks irão para aquele endpoint. É muito útil quando queremos definir diferentes endpoints de healthcheck para diferentes recursos. Nesse caso, estou passando que o predicado são todos aqueles checks que tiverem a tag “api”

Agora, dentro da configuração dos endpoints das controllers, configuramos a UI para a geração dos dashboards. Isso é feito nas linhas:

endpoints.MapHealthChecksUI(options =>

{

options.UIPath = “/dashboard”;

options.UseRelativeApiPath = true;

options.UseRelativeResourcesPath = true;

options.AddCustomStylesheet(Path.Combine(“Infra”, “Style”, “Dashboard.css”));

});

Na prop UIPath, especificamos o Path relativo que o nosso dashboard será renderizado. É um endpoint na API.

Nas props UseRelativeApiPath e UseRelativeResourcesPath, dizemos que queremos usar os paths relativos na hora que configuramos os endpoints na UI. Dessa forma, não precisamos passar a URL absoluta da API.

O método AddCustomStylesheet é uma adição recente. Nele, podemos passar uma arquivo CSS personalizado para customizar a UI que será gerada. Neste caso, passo um CSS que criei de forma personalizada usando as cores e logo da Tradeback.

CSS personalizado

Resultado final:

Se desejarmos, podemos colocar mais detalhes na descrição (algo como a exception message por exemplo, entretanto, como isso irá para o cliente final, não faria sentido expor detalhes de implementação interna que estão encapsulados).

Conclusão

Dessa forma, conseguimos criar uma aplicação que centraliza a verificação de integridade de nossas aplicações e podemos disponibilizar para nossos clientes. Podemos pensar que é uma visão mega simplificada do que a Microsoft faz com o Azure Status. Além disso, foi implementado de uma forma que torna fácil a escalabilidade (basta criar novos checks e registrá-los) e a manutenabilidade.

É importante ressaltar que podemos customizar esse dashboard de diferentes formas. Apresentei aqui somente a verificação de integridade de duas APIs, mas podemos verificar N outros serviços e configurar N outros endpoints de HealthCheck. Cada registro expansível dessa tabela é um endpoint e cada expansão é um check.

O objetivo era apresentar esse estudo de caso, conseguindo também expor conceitualmente o healthcheck e como sua apresentação pode ser implementada de forma simples usando o .NET 6. Espero que consiga tirar insights para suas próprias demandas de negócio.

Bem, fico por aqui. Espero que tenha gostado e caso o faça, peço que dê alguns claps para valorizar o artigo.

Em uma próxima oportunidade, explicarei do que se trata aquela aba WebHooks no Dashboard, sendo uma ferramenta que permite notificar outros serviços por WebHook sobre a alteração dos status de health.

Até a próxima! :)

Link para o repositório: https://github.com/GFernandesS/health-check-spec

--

--

Guilherme Schiavone
Tradeback

Amante da tecnologia, de games, filmes trash e desenvolvedor. O que você pode me ensinar hoje?