DevOps

Notificações, Logs e Monitoramento do Pipeline Já leu

13 min de leitura

Notificações, Logs e Monitoramento do Pipeline
Um pipeline de CI/CD que falha sem avisar ninguém é pior do que não ter pipeline. A automação cria uma falsa sensação de segurança: o time assume que, se nada foi dito, tudo correu bem. Quando a falha finalmente é descob

Um pipeline de CI/CD que falha sem avisar ninguém é pior do que não ter pipeline. A automação cria uma falsa sensação de segurança: o time assume que, se nada foi dito, tudo correu bem. Quando a falha finalmente é descoberta — geralmente por um cliente reclamando de algo que não funciona — o tempo de resposta é muito maior do que seria se o alerta tivesse chegado imediatamente.

Monitoramento de pipeline não é um detalhe de acabamento. É parte integral do sistema de entrega. Um bom sistema de notificações garante que a pessoa certa recebe a informação certa no momento certo, sem criar ruído que leve a equipe a ignorar os alertas.

Este artigo cobre três dimensões complementares: como estruturar notificações úteis, como interpretar e centralizar logs de pipeline, e como monitorar a saúde do pipeline como um sistema em si.


O Problema do Alerta Genérico

Antes de configurar qualquer notificação, vale refletir sobre o que torna um alerta útil versus um alerta que se torna ruído.

Um alerta genérico do tipo "pipeline falhou" obriga o receptor a abrir o GitHub, encontrar o workflow, identificar qual job falhou, ler os logs e então entender o que aconteceu. Se esse processo acontece dezenas de vezes por semana, a equipe começa a ignorar os alertas.

Um alerta útil responde imediatamente a quatro perguntas: o quê falhou, onde falhou, quem causou a falha e o que fazer a seguir. Com essa informação disponível diretamente na notificação, o tempo de resposta cai drasticamente e a equipe mantém o hábito de agir sobre os alertas.


Notificações no Slack

O Slack é a ferramenta de comunicação mais comum em equipes de tecnologia. A integração com GitHub Actions pode ser feita de diferentes formas, com diferentes níveis de riqueza de informação.

A forma mais simples usa webhooks do Slack diretamente:

- name: Notifica falha no Slack
  if: failure()
  uses: slackapi/slack-github-action@v1.26.0
  with:
    payload: |
      {
        "blocks": [
          {
            "type": "header",
            "text": {
              "type": "plain_text",
              "text": "❌ Pipeline Falhou"
            }
          },
          {
            "type": "section",
            "fields": [
              {
                "type": "mrkdwn",
                "text": "*Repositório:*\n${{ github.repository }}"
              },
              {
                "type": "mrkdwn",
                "text": "*Branch:*\n${{ github.ref_name }}"
              },
              {
                "type": "mrkdwn",
                "text": "*Job Falhou:*\n${{ github.job }}"
              },
              {
                "type": "mrkdwn",
                "text": "*Autor do Commit:*\n${{ github.actor }}"
              }
            ]
          },
          {
            "type": "section",

            "text": {
              "type": "mrkdwn",
              "text": "*Mensagem do Commit:*\n${{ github.event.head_commit.message }}"
            }
          },
          {
            "type": "actions",
            "elements": [
              {
                "type": "button",
                "text": {
                  "type": "plain_text",
                  "text": "Ver Pipeline"
                },
                "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
              }
            ]
          }
        ]
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
    SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

Para notificações mais sofisticadas que incluem o diff de cobertura de testes, o tamanho do PR ou métricas de build, usa-se a API do Slack diretamente com um token de bot:

- name: Notifica resultado completo do pipeline
  if: always()
  uses: slackapi/slack-github-action@v1.26.0
  with:
    channel-id: 'deploys'
    slack-message: |
      ${{ job.status == 'success' && '✅' || '❌' }} *${{ github.repository }}*
      *Status:* ${{ job.status }}
      *Branch:* `${{ github.ref_name }}`
      *Commit:* `${{ github.sha }}`
      *Autor:* ${{ github.actor }}
      *Duração:* ${{ steps.duracao.outputs.tempo }}
      <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Ver detalhes>
  env:
    SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

Notificações por Email e Microsoft Teams

Para equipes que usam email como canal principal de comunicação:

- name: Envia notificação por email
  if: failure()
  uses: dawidd6/action-send-mail@v3
  with:
    server_address: smtp.gmail.com
    server_port: 465
    secure: true
    username: ${{ secrets.EMAIL_USERNAME }}
    password: ${{ secrets.EMAIL_PASSWORD }}
    subject: "❌ Pipeline falhou — ${{ github.repository }} (${{ github.ref_name }})"
    to: time-devops@empresa.com
    from: pipeline@empresa.com
    html_body: |
      <h2>Pipeline Falhou</h2>
      <table>
        <tr><td><strong>Repositório</strong></td><td>${{ github.repository }}</td></tr>
        <tr><td><strong>Branch</strong></td><td>${{ github.ref_name }}</td></tr>
        <tr><td><strong>Commit</strong></td><td>${{ github.sha }}</td></tr>
        <tr><td><strong>Autor</strong></td><td>${{ github.actor }}</td></tr>
        <tr><td><strong>Job</strong></td><td>${{ github.job }}</td></tr>
      </table>
      <p>
        <a href="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}">
          Ver detalhes do pipeline
        </a>
      </p>

Para Microsoft Teams, o formato usa Adaptive Cards:

- name: Notifica no Microsoft Teams
  if: failure()
  uses: jdcargile/ms-teams-notification@v1.4
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    ms-teams-webhook-uri: ${{ secrets.TEAMS_WEBHOOK_URL }}
    notification-summary: "Pipeline falhou em ${{ github.repository }}"
    notification-color: "FF0000"
    timezone: America/Sao_Paulo

Estratégia de Notificação por Contexto

Notificar sempre e para todos é tão ruim quanto não notificar. A estratégia mais eficiente diferencia o contexto:

jobs:
  testes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

      # Notifica o autor do commit diretamente quando os testes falham
      - name: Notifica autor sobre falha nos testes
        if: failure() && github.event_name == 'pull_request'
        uses: slackapi/slack-github-action@v1.26.0
        with:
          channel-id: ${{ secrets.SLACK_CHANNEL_DEVS }}
          slack-message: |
            <@${{ github.actor }}> seus testes falharam no PR #${{ github.event.pull_request.number }}.
            <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Ver logs>
        env:
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

  deploy-producao:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy
        run: ./scripts/deploy.sh

      # Deploy bem-sucedido — notifica canal geral
      - name: Anuncia deploy bem-sucedido
        if: success()
        uses: slackapi/slack-github-action@v1.26.0
        with:
          channel-id: ${{ secrets.SLACK_CHANNEL_GERAL }}
          slack-message: |
            🚀 *Deploy em produção concluído*
            Versão `${{ github.sha }}` está no ar.
            Autor: ${{ github.actor }}
        env:
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

      # Deploy falhou — acorda o plantão
      - name: Alerta crítico de falha em produção
        if: failure()
        uses: slackapi/slack-github-action@v1.26.0
        with:
          channel-id: ${{ secrets.SLACK_CHANNEL_ALERTAS }}
          slack-message: |
            🚨 *ALERTA: Deploy em produção FALHOU*
            <!channel> verificar imediatamente.
            Commit: `${{ github.sha }}`
            Autor: ${{ github.actor }}
            <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Ver pipeline>
        env:
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

Logs Estruturados no Pipeline

Logs são o principal instrumento de diagnóstico quando algo dá errado. A diferença entre logs úteis e logs inúteis é a estrutura e a consistência.

O GitHub Actions oferece comandos especiais chamados workflow commands que permitem estruturar a saída dos logs:

# Agrupa linhas relacionadas em uma seção recolhível
echo "::group::Instalando dependências"
npm ci
echo "::endgroup::"

# Marca uma linha como aviso — aparece destacado nos logs
echo "::warning::Cobertura de testes abaixo de 85%"

# Marca uma linha como erro — aparece em vermelho
echo "::error::Falha na conexão com o banco de dados"

# Adiciona uma anotação vinculada a um arquivo e linha específica
echo "::error file=src/auth.js,line=42::Token de autenticação não validado"

# Define uma variável disponível nos steps seguintes
echo "BUILD_VERSION=1.2.3" >> $GITHUB_ENV

# Define um output do step disponível nos jobs seguintes
echo "image-tag=sha-abc1234" >> $GITHUB_OUTPUT

# Mascara um valor para que não apareça nos logs
echo "::add-mask::$SENHA_SECRETA"

Exemplo de um script de deploy com logs bem estruturados:

#!/bin/bash
set -euo pipefail

# Funções de log com timestamp e nível
log_info() {
  echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [INFO] $*"
}

log_warn() {
  echo "::warning::[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [WARN] $*"
}

log_error() {
  echo "::error::[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [ERROR] $*"
}

log_group() {
  echo "::group::$1"
}

log_endgroup() {
  echo "::endgroup::"
}

# Uso no script de deploy
log_group "Verificando pré-requisitos"
  log_info "Verificando Docker..."
  docker version || { log_error "Docker não está acessível"; exit 1; }

  log_info "Verificando conectividade com o servidor..."
  nc -zv "$DEPLOY_HOST" 22 || { log_error "Servidor inacessível na porta 22"; exit 1; }
log_endgroup

log_group "Executando deploy"
  log_info "Imagem: $IMAGE"
  log_info "Servidor: $DEPLOY_HOST"

  docker pull "$IMAGE" && log_info "Imagem baixada com sucesso" || {
    log_error "Falha ao baixar imagem $IMAGE"
    exit 1
  }
log_endgroup

log_group "Verificando saúde pós-deploy"
  for i in $(seq 1 10); do
    STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
      "http://$DEPLOY_HOST:3000/health" || echo "000")

    if [ "$STATUS" = "200" ]; then
      log_info "Aplicação saudável após ${i}s"
      break
    fi

    if [ "$i" = "10" ]; then
      log_error "Aplicação não ficou saudável após 10s — status $STATUS"
      exit 1
    fi

    log_warn "Aguardando... tentativa $i/10 — status atual: $STATUS"
    sleep 1
  done
log_endgroup

Calculando e Reportando Métricas do Pipeline

Acompanhar métricas do pipeline ao longo do tempo permite identificar degradações de performance antes que se tornem problemas críticos.

Um step que calcula a duração do pipeline e a reporta:

jobs:
  pipeline:
    runs-on: ubuntu-latest

    steps:
      - name: Registra hora de início
        id: inicio
        run: echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT

      # ... steps do pipeline ...

      - name: Calcula e reporta métricas
        if: always()
        run: |
          INICIO=${{ steps.inicio.outputs.timestamp }}
          FIM=$(date +%s)
          DURACAO=$((FIM - INICIO))
          MINUTOS=$((DURACAO / 60))
          SEGUNDOS=$((DURACAO % 60))

          echo "::notice::Duração total do pipeline: ${MINUTOS}m ${SEGUNDOS}s"

          # Alerta se o pipeline demorou mais que 10 minutos
          if [ $DURACAO -gt 600 ]; then
            echo "::warning::Pipeline acima de 10 minutos — considere otimização"
          fi

          # Envia métrica para o Datadog (se disponível)
          if [ -n "${{ secrets.DD_API_KEY }}" ]; then
            curl -s -X POST "https://api.datadoghq.com/api/v2/series" \
              -H "Content-Type: application/json" \
              -H "DD-API-KEY: ${{ secrets.DD_API_KEY }}" \
              -d "{
                \"series\": [{
                  \"metric\": \"ci.pipeline.duration\",
                  \"points\": [[$(date +%s), $DURACAO]],
                  \"tags\": [
                    \"repo:${{ github.repository }}\",
                    \"branch:${{ github.ref_name }}\",
                    \"status:${{ job.status }}\"
                  ]
                }]
              }"
          fi

Monitoramento do Pipeline como Sistema

Além de monitorar execuções individuais, é valioso monitorar o pipeline como um sistema — identificar padrões de falha, jobs lentos e tendências ao longo do tempo.

O GitHub fornece uma API para consultar dados históricos de workflows:

#!/bin/bash
# scripts/analise-pipeline.sh
# Analisa as últimas 50 execuções do workflow de CI

REPO="meu-usuario/meu-repositorio"
WORKFLOW="ci.yml"
TOKEN="$GITHUB_TOKEN"

# Busca as últimas execuções
RUNS=$(curl -s \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/$REPO/actions/workflows/$WORKFLOW/runs?per_page=50")

# Calcula taxa de sucesso
TOTAL=$(echo "$RUNS" | jq '.workflow_runs | length')
SUCESSO=$(echo "$RUNS" | jq '[.workflow_runs[] | select(.conclusion == "success")] | length')
FALHA=$(echo "$RUNS" | jq '[.workflow_runs[] | select(.conclusion == "failure")] | length')
TAXA=$(echo "scale=1; $SUCESSO * 100 / $TOTAL" | bc)

echo "=== Análise do Pipeline: últimas $TOTAL execuções ==="
echo "Sucesso: $SUCESSO ($TAXA%)"
echo "Falha: $FALHA"

# Calcula duração média das execuções bem-sucedidas
MEDIA=$(echo "$RUNS" | jq '
  [.workflow_runs[]
   | select(.conclusion == "success")
   | (.updated_at | fromdateiso8601) - (.created_at | fromdateiso8601)
  ] | add / length | . / 60 | floor')

echo "Duração média (sucesso): ${MEDIA} minutos"

# Identifica os jobs que mais falham
echo ""
echo "=== Analisando jobs com maior taxa de falha ==="
for RUN_ID in $(echo "$RUNS" | jq -r '.workflow_runs[].id' | head -20); do
  curl -s \
    -H "Authorization: Bearer $TOKEN" \
    "https://api.github.com/repos/$REPO/actions/runs/$RUN_ID/jobs" \
    | jq -r '.jobs[] | select(.conclusion == "failure") | .name'
done | sort | uniq -c | sort -rn | head -10

Dashboard de Status com GitHub Badges

Uma forma simples e eficaz de tornar o status do pipeline visível para toda a equipe é adicionar badges ao README do repositório:

# Minha API

[![CI](https://github.com/usuario/minha-api/actions/workflows/ci.yml/badge.svg)](https://github.com/usuario/minha-api/actions/workflows/ci.yml)
[![Deploy](https://github.com/usuario/minha-api/actions/workflows/deploy.yml/badge.svg)](https://github.com/usuario/minha-api/actions/workflows/deploy.yml)
[![Cobertura](https://codecov.io/gh/usuario/minha-api/branch/main/graph/badge.svg)](https://codecov.io/gh/usuario/minha-api)

Esses badges atualizam automaticamente e são visíveis para qualquer pessoa que abrir o repositório — sem precisar entrar na aba de Actions.


Integrando com Ferramentas de Observabilidade

Para times com ferramentas de APM como Datadog ou New Relic, integrar os eventos de deploy ao sistema de observabilidade cria uma linha do tempo unificada que correlaciona deploys com mudanças de performance:

- name: Registra evento de deploy no Datadog
  if: success()
  run: |
    curl -s -X POST "https://api.datadoghq.com/api/v1/events" \
      -H "Content-Type: application/json" \
      -H "DD-API-KEY: ${{ secrets.DD_API_KEY }}" \
      -d '{
        "title": "Deploy em produção",
        "text": "Versão ${{ github.sha }} implantada por ${{ github.actor }}",
        "alert_type": "info",
        "tags": [
          "env:production",
          "service:minha-api",
          "version:${{ github.sha }}"
        ],
        "source_type_name": "github"
      }'

Com esse evento registrado, qualquer anomalia de performance que ocorra após um deploy fica visualmente correlacionada no dashboard do Datadog — simplificando enormemente a investigação de regressões.


Encerrando o Módulo 4

Com este artigo conclui-se o Módulo 4. Foram cobertos os fundamentos conceituais do CI/CD, a construção de pipelines completos com GitHub Actions, testes automatizados em suas diferentes camadas, gerenciamento seguro de secrets, deploy automático com zero downtime e rollback, e o monitoramento do próprio pipeline como sistema.

O Módulo 5 entra no território da Infraestrutura como Código — a disciplina que aplica os mesmos princípios de versionamento, revisão e automação que foram aplicados ao código da aplicação à própria infraestrutura que a sustenta.


Referências para Aprofundamento

Documentação oficial - GitHub Actions — Workflow Commands — Referência completa de todos os comandos especiais disponíveis nos runners do GitHub Actions para estruturar logs e outputs. - GitHub Actions — API de Workflows — Documentação da API REST do GitHub para consultar dados históricos de execuções de workflows.

Ferramentas de notificação - Slack Block Kit Builder — Ferramenta visual do Slack para criar e testar layouts de mensagens antes de implementá-los no pipeline. - slackapi/slack-github-action — GitHub — Repositório oficial da action do Slack com documentação de todos os modos de uso disponíveis. - dawidd6/action-send-mail — GitHub — Action para envio de emails com suporte a HTML, anexos e múltiplos destinatários.

Observabilidade e métricas - Datadog CI Visibility — Documentação do módulo de visibilidade de CI/CD do Datadog, que agrega métricas de múltiplos pipelines em dashboards unificados. - Codecov Documentation — Documentação do Codecov para integração de relatórios de cobertura de testes ao pipeline do GitHub Actions.

Comentários

Mais em DevOps

SSH: Conectando e Gerenciando Servidores Remotos
SSH: Conectando e Gerenciando Servidores Remotos

SSH &mdash;&nbsp;Secure Shell &mdash; &eacute; o protocolo pelo qual administ...

Publicando Imagens no Docker Hub e GitHub Container Registry
Publicando Imagens no Docker Hub e GitHub Container Registry

Uma imagem que existe apenas na máquina do desenvolvedor não serve a nenhum p...

Kubernetes em Produção: Segurança, GitOps e Deploys Avançados
Kubernetes em Produção: Segurança, GitOps e Deploys Avançados

Criar um cluster EKS e fazer uma aplicação rodar nele é relativamente simples...