Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 39

Configuring Service Defaults for Health Checks and OpenTelemetry

Recommended resources

Olá! Bem-vindo de volta.

Na lição anterior, você construiu sua primeira aplicação distribuída com .NET Aspire, definindo um App Host e adicionando projetos de serviço a ele. Você deve ter notado que o template criou um projeto chamado ServiceDefaults, mas nós o deixamos de lado. Hoje, vamos colocar esse projeto sob os holofotes.

O objetivo desta lição é configurar os Service Defaults para habilitar health checks padronizados e a integração com OpenTelemetry. Entender este projeto é fundamental, pois ele é o coração da filosofia "cloud-ready" do Aspire, fornecendo resiliência e observabilidade de forma consistente a todos os seus serviços com o mínimo de esforço.

1. O que são os Service Defaults?

Em uma arquitetura de microsserviços, muitas preocupações são transversais (cross-cutting concerns): como os serviços emitem logs? Como eles expõem seu estado de saúde? Como coletamos métricas e traces? Sem uma abordagem padronizada, cada serviço pode acabar com sua própria implementação, levando a inconsistências e a uma grande quantidade de código repetitivo (boilerplate).

O projeto ServiceDefaults do Aspire resolve exatamente isso. Ele é uma biblioteca de classes compartilhada que contém um conjunto opinativo, mas customizável, de configurações para:

  • Observabilidade (OpenTelemetry): Habilita logs, métricas e tracing distribuído.
  • Health Checks: Expõe endpoints padronizados para que sistemas de orquestração possam monitorar a saúde do serviço.
  • Resiliência (Polly): Configura políticas de retry e circuit breaker para chamadas HTTP (abordaremos em uma lição futura).
  • Service Discovery: Permite que os serviços se encontrem na rede (também em uma lição futura).

Ao adicionar uma única linha, builder.AddServiceDefaults(), ao Program.cs de um serviço, você o "inscreve" nesse conjunto de convenções.

Service defaults | Aspire

Para uma visão geral e oficial, leia a introdução da documentação do .NET Aspire sobre Service Defaults. Ela estabelece o propósito e os componentes principais que vamos explorar.

Leia as seções "Explore the service defaults project" e "Provided extension methods". Foque em entender o papel do projeto e as tarefas que o método AddServiceDefaults realiza. Isso vai nos dar a base para o resto da lição.


2. Habilitando Health Checks Padronizados

Health checks são endpoints simples que informam a um orquestrador (como o Aspire localmente, ou Kubernetes em produção) se uma aplicação está funcionando corretamente. Existem dois tipos principais:

  • Liveness (/alive): "A aplicação está rodando?". Se este endpoint falhar, significa que a aplicação travou e precisa ser reiniciada.
  • Readiness (/health): "A aplicação está pronta para receber tráfego?". Este endpoint verifica não apenas se a aplicação está rodando, mas também se suas dependências (banco de dados, outros serviços) estão acessíveis. Se falhar, o orquestrador para de enviar tráfego para a instância, mas não a reinicia, dando tempo para que ela se recupere.

Como o Aspire implementa Health Checks

Vamos olhar o código. No seu projeto ServiceDefaults, abra o arquivo Extensions.cs. Você encontrará dois métodos importantes: AddDefaultHealthChecks e MapDefaultEndpoints.

  1. AddDefaultHealthChecks:

    public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) 
        where TBuilder : IHostApplicationBuilder
    {
        builder.Services.AddHealthChecks()
            // Add a default liveness check to ensure app is responsive
            .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
        return builder;
    }
    

    Este método registra o serviço de health checks do ASP.NET Core e adiciona um primeiro check chamado "self". Note a tag ["live"], que o identifica como um check de liveness.

  2. MapDefaultEndpoints:

    public static WebApplication MapDefaultEndpoints(this WebApplication app)
    {
        // ...
        app.MapHealthChecks("/health");
        app.MapHealthChecks("/alive", new HealthCheckOptions
        {
            Predicate = r => r.Tags.Contains("live")
        });
        // ...
    }
    

    Este método mapeia os endpoints HTTP. /health executa todos os health checks (readiness), enquanto /alive executa apenas aqueles com a tag "live" (liveness).

O mais poderoso é que, quando você adiciona componentes Aspire (como um banco de dados ou cache), eles automaticamente adicionam seus próprios health checks, que são incluídos no endpoint /health.

Health Checks in ASP.NET Core with External Services
Esta imagem ilustra como o `AddHealthChecks()` do ASP.NET Core pode ser estendido para verificar dependências como PostgreSQL, Redis e RabbitMQ. O Aspire automatiza esse processo para você através dos seus componentes de integração.

Aplicação Prática

Vamos aplicar isso ao projeto MyAspireApp.ApiService que você criou na lição anterior.

  1. Verifique se o seu MyAspireApp.ApiService/Program.cs tem as seguintes linhas:
    // No topo do arquivo
    builder.AddServiceDefaults();
    
    // ... antes de app.Run()
    app.MapDefaultEndpoints();
    
  2. Inicie a aplicação Aspire a partir do diretório do AppHost:
    cd MyAspireApp.AppHost
    dotnet run
    
  3. Quando o dashboard abrir, encontre o endpoint do seu apiservice. Se ele estiver rodando, por exemplo, em https://localhost:7001, abra novas abas e navegue para:
    • https://localhost:7001/alive -> Você verá a resposta "Healthy".
    • https://localhost:7001/health -> Você também verá "Healthy".

No dashboard, o status do seu serviço apiservice deve aparecer como "Running".

.NET Aspire Tutorial For Beginners

O vídeo a seguir, do canal Julio Casal, demonstra de forma brilhante o poder dos health checks no Aspire. Ele para intencionalmente uma dependência (um banco de dados) e mostra como o status de saúde da API muda automaticamente.

Assista ao trecho de 46:12 a 59:14. Preste atenção especial em como o status da API no dashboard muda de 'Running' para 'Unhealthy' quando a dependência é interrompida. Isso ilustra perfeitamente o propósito do check de 'readiness'. Você verá também como os endpoints /alive e /health respondem de maneira diferente.


3. Habilitando Observabilidade com OpenTelemetry

Observabilidade é a capacidade de entender o estado interno de um sistema a partir de seus outputs externos. OpenTelemetry (OTel) é um padrão aberto para instrumentar, gerar, coletar e exportar dados de telemetria, que se baseiam em três pilares:

  • Logs: Eventos discretos que aconteceram, úteis para depuração.
  • Traces: Representam o fluxo de uma única requisição através de múltiplos serviços (tracing distribuído). Essencial para identificar gargalos e entender interações.
  • Metrics: Medições numéricas agregadas ao longo do tempo (ex: número de requisições por segundo, uso de CPU).

A configuração manual do OpenTelemetry pode ser complexa. O Aspire Service Defaults torna isso trivial.

Como o Aspire implementa OpenTelemetry

No mesmo arquivo Extensions.cs, você encontrará o método ConfigureOpenTelemetry.

public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) 
    where TBuilder : IHostApplicationBuilder
{
    builder.Logging.AddOpenTelemetry(...); // 1. Conecta os logs ao OTel

    builder.Services.AddOpenTelemetry()
        .WithMetrics(metrics => ...)      // 2. Configura as métricas
        .WithTracing(tracing => ...);     // 3. Configura os traces

    builder.AddOpenTelemetryExporters();  // 4. Configura para onde enviar os dados
    
    return builder;
}

A parte mais importante para o desenvolvimento local é o método AddOpenTelemetryExporters. Ele verifica por uma variável de ambiente, OTEL_EXPORTER_OTLP_ENDPOINT. O App Host do Aspire define automaticamente essa variável para cada serviço, apontando para um endpoint no próprio Aspire Dashboard. É assim que toda a telemetria flui para o dashboard sem nenhuma configuração manual.

Aplicação Prática: Explorando a Observabilidade

Como AddServiceDefaults já chama ConfigureOpenTelemetry, tudo está pronto.

  1. Com sua aplicação rodando, acesse um endpoint da sua apiservice algumas vezes (por exemplo, https://localhost:7001/weatherforecast).
  2. Vá para o Aspire Dashboard e clique na linha do seu apiservice.
  3. Explore as abas à esquerda:
    • Structured Logs: Você verá os logs da sua aplicação, não como texto puro, mas como dados estruturados. Você pode filtrar por propriedades, como RequestId, para ver todos os logs de uma única requisição.
    • Traces: Aqui está a mágica do tracing distribuído. Você verá cada chamada HTTP que você fez. Ao clicar em uma, você verá uma "cascata" (waterfall) mostrando a duração de cada etapa interna da sua requisição. Se seu serviço chamasse outro, você veria essa chamada como parte da mesma trace.
    • Metrics: Explore as métricas que o Aspire coleta por padrão. Você pode ver gráficos para aspnetcore_requests_total (total de requisições) ou http_client_request_duration_seconds (duração de chamadas HTTP feitas pelo seu serviço).

Code to cloud with .NET Aspire - David Fowler & Damian Edwards

David Fowler e Damian Edwards, os criadores do Aspire, demonstram essa experiência 'turnkey' de observabilidade. Ver a explicação deles sobre o porquê e como isso foi construído é extremamente valioso para entender os princípios de design.

Assista ao trecho de 39:23 a 46:36. Eles adicionam o ServiceDefaults, executam a aplicação e exploram os logs estruturados e, mais importante, as traces distribuídas no dashboard. Eles explicam como isso permite uma visão profunda do que está acontecendo durante o desenvolvimento.

Teste seu entendimento!

Você está depurando um problema de performance. Uma requisição a um endpoint está demorando mais do que o esperado. Qual dos três pilares da observabilidade (Logs, Traces, Metrics) no Aspire Dashboard seria o mais útil para começar sua investigação e por quê?

Show answer

A aba Traces seria o melhor lugar para começar. Uma trace distribuída mostraria uma visualização em cascata (waterfall) de todas as operações que compõem a requisição (ex: processamento da API, chamadas a banco de dados, chamadas a outros serviços). Isso permitiria que você identificasse visualmente qual operação específica está consumindo a maior parte do tempo e, portanto, é o gargalo de performance.


Conclusão

Nesta lição, você desvendou o projeto ServiceDefaults e viu como ele é essencial para construir aplicações robustas com .NET Aspire. Com uma única linha de código, você obteve um sistema padronizado de health checks e uma infraestrutura completa de observabilidade.

Principais Pontos:

  • O projeto ServiceDefaults centraliza configurações transversais para garantir consistência entre os serviços.
  • Health Checks (/alive e /health) são cruciais para a resiliência e são habilitados pelos métodos AddDefaultHealthChecks e MapDefaultEndpoints.
  • OpenTelemetry fornece os três pilares da observabilidade: Logs, Traces e Métricas.
  • AddServiceDefaults() habilita tudo isso, e o App Host configura automaticamente os serviços para enviarem telemetria para o Aspire Dashboard, proporcionando uma experiência de desenvolvimento local rica e perspicaz.

Na próxima lição, faremos um tour mais aprofundado pelo Aspire Dashboard. Agora que você sabe como os dados chegam lá, vamos nos concentrar em como usar o dashboard de forma eficaz para monitorar, diagnosticar e depurar suas aplicações distribuídas.