Dashboard de Métricas Pessoais com Grafana e SQLite: Como Visualizei Minha Vida em Uma Tarde de Domingo
Eu olhei para o espelho na segunda-feira e percebi algo perturbador: não fazia ideia de quantas horas tinha dormido no fim de semana. Não sabia quantos commits tinha feito na semana anterior. Não lembrava quantas vezes tinha saido para caminhar em abril. Tudo era vibes, nenhum dado.
O problema de viver no automático é que você não sabe se está indo pra frente ou só girando no lugar. Ferramentas de produtividade prometem resolver isso, mas a maioria te dá gráficos bonitos sobre o trabalho corporativo — sprints, tickets, velocity. Ninguém te ajuda a medir se você está dormindo melhor, codando mais, ou saindo da cadeira o suficiente.
Foi num domingo chuvoso que decidi parar de reclamar e construir meu próprio dashboard de métricas pessoais. A stack? Grafana + SQLite. O tempo? uma tarde. O resultado? Mudou completamente meu relacionamento com dados — e com minha própria rotina.
Neste artigo, vou te mostrar exatamente como construí esse sistema, do zero ao dashboard funcional, incluindo os erros que cometi (porque foram muitos) e o código que realmente funciona.

Por Que Grafana + SQLite e Não Uma App Qualquer
Vamos ser honestos: existem 47 apps de habit tracker na Play Store. Eu testei 12 deles. O problema de todos?
- Seus dados moram no servidor de outra pessoa. Sem API aberta, sem export confiável, sem controle.
- Gráficos limitados ao que o designer achou bonito. Quer cruzar horas de sono com número de commits? Azar o seu.
- Lock-in brutal. Depois de 6 meses registrando tudo, migrar é pesadelo.
- Funciona enquanto a startup existe. Já vi apps excelentes morrerem de uma noite pra outra.
O Grafana é a ferramenta de visualização mais flexível que existe. O SQLite é o banco de dados mais confiável do mundo — um arquivo, sem servidor, sem configuração, sem ponto único de falha. Juntar os dois parece óbvio em retrospectiva, mas eu demorei 3 meses pra perceber que a resposta estava no meu homelab de hardware reciclado o tempo todo.
O Que Eu Decidi Medir (E Por Quê)
A regra numero 1 de métricas pessoais: não meça tudo. Métricas demais são tão inúteis quanto métricas de menos. Eu comecei com 5 dados que realmente importavam para mim:
- Horas de sono — tudo começa aqui. Código ruim = sono ruim.
- Commits por dia — proxy decente de produtividade em código.
- Minutos de exercício — porque sentar 14 horas por dia não é sustentável.
- Tempo de tela (pós-trabalho) — scroll infinito é o inimigo.
- Humor (1-5) — subjetivo, mas quando você cruza com sono, patrões aparecem.
Depois de duas semanas, adicionei mais dois: linhas de código escritas (sim, isso importa pra mim) e caféínicos consumidos (sim, isso também importa).
Arquitetura do Sistema — Simples Como Deve Ser
A infra é propositalmente minimalista:
~/metrics/
├── data/
│ └── personal.db # SQLite — um arquivo, baby
├── scripts/
│ ├── collect.sh # Coleta dados diários
│ ├── git-stats.sh # Extrai commits do git log
│ └── import-health.sh # Importa dados do Android
├── grafana/
│ └── provisioning/ # Dashboards automáticos
└── docker-compose.yml # Grafana em container
Tudo roda no meu servidor caseiro, dentro de uma rede isolada. Sem cloud, sem API terceira, sem custo mensal. O SQLite roda localmente, o Grafana roda em container Docker, e um script cron faz a coleta automática.
Passo 1: Criando o Banco SQLite
Esse é o coração do sistema. E é ridiculamente simples:
-- schema.sql
CREATE TABLE IF NOT EXISTS daily_metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL UNIQUE,
sleep_hours REAL,
commits INTEGER DEFAULT 0,
exercise_minutes INTEGER DEFAULT 0,
screen_time_minutes INTEGER,
mood INTEGER CHECK(mood BETWEEN 1 AND 5),
coffee_count INTEGER DEFAULT 0,
notes TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX idx_metrics_date ON daily_metrics(date);
-- View para médias semanais (usada pelo Grafana)
CREATE VIEW IF NOT EXISTS weekly_averages AS
SELECT
strftime('%Y-%W', date) as week,
ROUND(AVG(sleep_hours), 1) as avg_sleep,
ROUND(AVG(commits), 1) as avg_commits,
ROUND(AVG(exercise_minutes), 0) as avg_exercise,
ROUND(AVG(mood), 1) as avg_mood,
COUNT(*) as days_tracked
FROM daily_metrics
GROUP BY strftime('%Y-%W', date)
ORDER BY week DESC;
Um arquivo. Uma tabela. Um índice. Uma view. Isso é tudo que você precisa pra começar. Nada de migrations complexas, nada de ORM, nada de boilerplate. SQLite não brinca de enterprise — ele resolve.
# Inicializar o banco
sqlite3 ~/metrics/data/personal.db < ~/metrics/schema.sql
# Verificar
sqlite3 ~/metrics/data/personal.db ".tables"
# daily_metrics weekly_averages
O Erro Que Me Custou 2 Horas
Eu inicialmente coloquei date DATE no schema. Funciona? Sim. Mas quando você precisa fazer queries como "últimos 7 dias" ou "média semanal", o tipo TEXT com formato ISO (YYYY-MM-DD) é muito mais flexível no SQLite. As funções de data do SQLite (strftime, date(), datetime()) funcionam melhor com strings. Lição aprendida da forma difícil.

Passo 2: Script de Coleta Diária
O script de coleta roda via cron todo dia às 23:45. Ele coleta o que dá pra automatizar e me pergunta o resto:
#!/usr/bin/env bash
# ~/metrics/scripts/collect.sh
set -euo pipefail
DB="$HOME/metrics/data/personal.db"
TODAY=$(date +%Y-%m-%d)
# Coletar commits do dia (todos os repos no workspace)
COMMITS=$(find ~/workspace -name ".git" -type d 2>/dev/null | \
while read -r gitdir; do
repo=$(dirname "$gitdir")
git -C "$repo" log --author="$(git config user.name)" \
--since="$TODAY 00:00:00" \
--until="$TODAY 23:59:59" \
--oneline 2>/dev/null | wc -l
done | awk '{s+=$1} END {print s}')
COMMITS=${COMMITS:-0}
# Input interativo para métricas manuais
read -rp "😴 Horas de sono [0]: " SLEEP
read -rp "🏋️ Minutos de exercício [0]: " EXERCISE
read -rp "📱 Tempo de tela (min) [0]: " SCREEN
read -rp "😊 Humor (1-5) [3]: " MOOD
read -rp "☕ Cafés hoje [0]: " COFFEE
read -rp "📝 Notas (opcional): " NOTES
SLEEP=${SLEEP:-0}
EXERCISE=${EXERCISE:-0}
SCREEN=${SCREEN:-0}
MOOD=${MOOD:-3}
COFFEE=${COFFEE:-0}
sqlite3 "$DB" "
INSERT INTO daily_metrics (date, sleep_hours, commits, exercise_minutes,
screen_time_minutes, mood, coffee_count, notes)
VALUES ('$TODAY', $SLEEP, $COMMITS, $EXERCISE, $SCREEN, $MOOD, $COFFEE, '$NOTES')
ON CONFLICT(date) DO UPDATE SET
sleep_hours = $SLEEP,
commits = $COMMITS,
exercise_minutes = $EXERCISE,
screen_time_minutes = $SCREEN,
mood = $MOOD,
coffee_count = $COFFEE,
notes = '$NOTES';
"
echo "✅ Métricas de $TODAY registradas!"
echo " 📊 $COMMITS commits | ${SLEEP}h sono | ${EXERCISE}min exercício"
O ON CONFLICT é fundamental — permite rodar o script múltiplas vezes sem duplicar dados. Eu aprendi isso depois de ter 3 entradas pra mesma data no primeiro dia e precisar limpar na mão.
Passo 3: Subindo o Grafana com SQLite Plugin
Aqui é onde a mágica acontece. O Grafana não suporta SQLite nativamente, mas existe um plugin excelente (e leve) que resolve isso:
# docker-compose.yml
version: "3.8"
services:
grafana:
image: grafana/grafana:latest
container_name: personal-metrics
ports:
- "3456:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=false
- GF_AUTH_BASIC_ENABLED=true
- GF_SECURITY_ADMIN_USER=olivetto
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASS:-changeme}
- GF_INSTALL_PLUGINS=frser-sqlite-datasource
volumes:
- grafana-storage:/var/lib/grafana
- ./data:/data:ro # SQLite DB read-only
- ./grafana/provisioning:/etc/grafana/provisioning
restart: unless-stopped
volumes:
grafana-storage:
# Subir o container
cd ~/metrics
docker compose up -d
# Verificar
docker compose logs -f grafana
# ... t=2026-05-03 ... msg="HTTP Server Listen" address=[::]:3000
O pulo do gato é o volume ./data:/data:ro — monta o diretório com o SQLite como read-only dentro do container. O Grafana lê o arquivo diretamente, sem necessidade de API intermediária, sem latência de rede.
Configurando o DataSource SQLite no Grafana
Depois do container subir, acesse http://localhost:3456 (ou o IP do seu servidor na rede local). Vá em Configuration → Data Sources → Add data source e selecione SQLite.
A configuração é uma linha: o caminho pro banco dentro do container, que é /data/personal.db. Salve e teste. Se der verde, você está no caminho certo.
Passo 4: Construindo o Dashboard
Aqui é onde eu perdi mais tempo — e onde você vai ganhar. Meu dashboard final tem 6 painéis:
- Sono vs Humor — gráfico de dispersão que confirmou o óbvio: durmo menos que 6h, humor despenca.
- Commits por Dia da Semana — barras empilhadas. Descobri que terça é meu dia mais produtivo. Sexta, nem tanto.
- Tendência de Exercício — linha temporal com média móvel de 7 dias.
- Tempo de Tela vs Sono — correlação inversa brutal. Mais de 120min de tela = quase sempre menos de 6h de sono.
- Resumo Semanal — stat panels com as médias da semana atual.
- Streak Tracker — quantos dias seguidos registrei dados. Gamificação básica que funciona.
Exemplo de query para o painel de Sono vs Humor:
-- Grafana SQLite query: Sono vs Humor (últimos 30 dias)
SELECT
date AS time,
sleep_hours AS 'Horas de Sono',
mood AS 'Humor (1-5)'
FROM daily_metrics
WHERE date >= date('now', '-30 days')
ORDER BY date ASC;
E o streak tracker, que é a query que mais me orgulha:
-- Streak de dias consecutivos com registro
WITH RECURSIVE streak AS (
SELECT date, 1 AS length
FROM daily_metrics
WHERE date = (SELECT MAX(date) FROM daily_metrics)
UNION ALL
SELECT d.date, s.length + 1
FROM daily_metrics d
JOIN streak s ON d.date = date(s.date, '-1 day')
WHERE d.date = date(s.date, '-1 day')
)
SELECT MAX(length) AS 'Streak Atual (dias)'
FROM streak;
Sim, SQLite suporta CTEs recursivas. Se você achava que SQLite era só pra toy projects, repense.
Automação com Cron — Pra Não Esquecer
Registrar dados todo dia manualmente funciona por uma semana. Depois você esquece. A solução: o mesmo sistema de hábitos que já uso no terminal.
# Crontab -e
# Lembrete às 22:30 pra registrar métricas
30 22 * * * export DISPLAY=:0 && notify-send "📊 Hora das métricas!" "Rodar: ~/metrics/scripts/collect.sh"
# Coleta automática de commits às 23:45
45 23 * * * ~/metrics/scripts/git-stats.sh >> ~/metrics/logs/git-stats.log 2>&1
O git-stats.sh roda automaticamente e registra os commits. Eu só preciso responder as perguntas sobre sono, exercício e humor quando o collect.sh roda à noite. Reduzi o esforço manual de 5 minutos pra 30 segundos.
O Box do Perrengue
🔥 Perrengue Real: Grafana Crashou por 3 Dias e Eu Nem Notei
Na terceira semana, o container do Grafana morreu silenciosamente. O
docker composemostravaRestarting (1) 5 seconds agoem loop. Motivo? Eu tinha mudado a senha no painel web e o docker-compose.yml ainda tinha a senha antiga no environment — o Grafana tentava migrar o banco interno, falhava, morria, repetia.Desafio: Como detectar automaticamente que seu dashboard de métricas está morto? Dica: quem monitora o monitor?
A solução que implementei foi um healthcheck bash que roda a cada hora:
#!/usr/bin/env bash # healthcheck-grafana.sh if ! curl -sf http://localhost:3456/api/health | grep -q "ok"; then echo "GRAFANA DOWN! Reiniciando..." cd ~/metrics && docker compose restart echo "Grafana reiniciado em $(date)" >> ~/metrics/logs/health.log fiAdicionei ao cron:
0 * * * * ~/metrics/scripts/healthcheck-grafana.sh. Agora, se o Grafana cair, ele sobe sozinho em menos de 1 minuto. E eu tenho log de cada incidente.
Resultados Depois de 30 Dias
Depois de um mês registrando dados, os gráficos revelaram coisas que meu cérebro se recusava a aceitar:
- Dormir menos de 6h reduz meus commits em 40% no dia seguinte. Não é impressão. É dado.
- Exercício e humor têm correlação direta. Dias com 30+ minutos de atividade física = humor 4 ou 5 em 85% dos casos.
- Terça é meu pico. Quinta é meu vale. Reorganizei minha agenda pra tarefas criativas na terça e tarefas mecânicas na quinta.
- Mais de 3 cafés = sono abaixo de 5h. Cortando o café depois das 15h.
- Tempo de tela > 90min depois das 21h = insônia garantida. O dado é brutal.
Nenhuma app de meditação, nenhum guru de produtividade, nenhum livro me deu insights tão específicos sobre mim. E tudo veio de uma tabela SQLite e um container Docker.
Produzindo Mais com Dados (Não com Ansiedade)
Um sistema de time blocking automatizado sem dados é um palpite educado. Com dados, vira uma ferramenta cirúrgica. Eu agora bloqueio minha terça pra código criativo porque os números dizem que é quando eu rendo mais. Não é superstição — é estatística com amostra de um (eu).
Se você já usa git hooks para automatizar revisão de código, pode facilmente adicionar um hook que registra métricas a cada push. Quer ir mais longe? Importe dados do Google Fit ou Apple Health via API e alimente o SQLite automaticamente.
O limite é sua criatividade — e o tamanho do seu disco, que com SQLite é basicamente infinito (um ano de dados diários ocupa menos de 100KB).
Próximos Passos — O Que Eu Quero Adicionar
- Integração com o sistema de hábitos em bash que já uso, pra cruzar streak de hábitos com métricas de humor.
- Alertas no Telegram — se meu sono médio da semana cair abaixo de 6h, recebo mensagem automática via bot.
- Importação de dados do Spotify — quero ver se o tipo de música que ouço correlaciona com produtividade.
- Painel mobile-friendly — o Grafana já é responsivo, mas quero uma view otimizada pra conferir no celular de manhã.
Custo Total
- Grafana: Gratuito (open source)
- SQLite: Gratuito (domínio público)
- Docker: Gratuito
- Hardware: Qualquer máquina Linux — até um Raspberry Pi serve
- Tempo de setup: 3-4 horas (na primeira vez)
- Tempo diário de manutenção: 30 segundos
Total: R$ 0,00. O mesmo preço da sua dignidade quando você percebe que dorme 4h por noite há duas semanas.
CTA — E Você, O Que Mediria?
Se você pudesse visualizar qualquer aspecto da sua vida num dashboard, o que seria? Horas de estudo? Dinheiro gasto em delivery? Vezes que abriu o Instagram? Me conta nos comentários — e se quiser que eu publique um tutorial de como integrar alguma fonte de dados específica (Google Fit, Strava, GitHub, qualquer coisa), é só pedir.
O código completo do sistema está disponível no meu GitHub — mas o mais importante é o conceito: seus dados devem ser seus, e você deve poder vê-los do jeito que fizer sentido pra você.
Não espere uma startup resolver seu problema de autoconhecimento. Abra o terminal, crie uma tabela, e comece a registrar. O Grafana é só o começo — o verdadeiro dashboard é o que você constrói na sua cabeça quando os números param de ser abstratos e passam a ser seus.
