Logging em scripts Bash
1. Por que fazer logging em scripts Bash?
Logging é uma prática essencial em scripts Bash por várias razões. Primeiramente, permite o rastreamento de execução — você pode ver exatamente o que aconteceu durante a execução do script, em que ordem e em que momento. Isso é crucial para depuração de erros, especialmente em scripts que rodam em produção ou em horários agendados (cron jobs).
Além disso, logs servem para auditoria de ações e conformidade. Em ambientes corporativos, é comum precisar registrar quem executou o quê e quando. Por fim, a diferenciação entre logs de sucesso, aviso e falha ajuda a priorizar problemas: um erro crítico merece atenção imediata, enquanto um aviso pode ser revisado depois.
2. Função de logging simples e reutilizável
Vamos criar uma função de logging básica que inclui timestamp, nome do script e nível da mensagem. Essa função pode ser colocada em um arquivo separado e reutilizada em vários scripts.
#!/bin/bash
# Função de logging simples
log() {
local LEVEL="$1"
local MESSAGE="$2"
local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
local SCRIPT_NAME=$(basename "$0")
echo "[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE"
}
# Uso da função
log "INFO" "Script iniciado"
log "WARN" "Variável X não definida, usando valor padrão"
log "ERROR" "Falha ao conectar ao banco de dados"
3. Níveis de severidade e formatação de mensagens
Definir níveis de severidade ajuda a categorizar a importância das mensagens. Os níveis comuns são:
- DEBUG: Informações detalhadas para depuração
- INFO: Informações normais de execução
- WARN: Avisos sobre situações anormais que não impedem a execução
- ERROR: Erros que afetam a funcionalidade, mas não interrompem o script
- FATAL: Erros críticos que interrompem imediatamente o script
A formatação consistente segue o padrão: [DATA HORA] [NÍVEL] Mensagem. Opcionalmente, podemos adicionar cores no terminal para destacar visualmente cada nível.
#!/bin/bash
# Cores para terminal (opcional)
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log() {
local LEVEL="$1"
local MESSAGE="$2"
local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
local SCRIPT_NAME=$(basename "$0")
case "$LEVEL" in
DEBUG) COLOR="$BLUE" ;;
INFO) COLOR="$GREEN" ;;
WARN) COLOR="$YELLOW" ;;
ERROR) COLOR="$RED" ;;
FATAL) COLOR="$RED" ;;
*) COLOR="$NC" ;;
esac
echo -e "${COLOR}[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE${NC}"
}
log "INFO" "Processando arquivo..."
log "WARN" "Arquivo duplicado encontrado"
log "ERROR" "Falha na leitura do arquivo"
4. Redirecionamento de saída: stdout vs stderr
Em scripts Bash, é importante separar logs normais (stdout) de erros (stderr). Isso permite direcionar cada tipo de saída para destinos diferentes.
#!/bin/bash
log() {
local LEVEL="$1"
local MESSAGE="$2"
local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
local SCRIPT_NAME=$(basename "$0")
if [ "$LEVEL" = "ERROR" ] || [ "$LEVEL" = "FATAL" ]; then
echo "[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE" >&2
else
echo "[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE"
fi
}
log "INFO" "Operação normal"
log "ERROR" "Falha crítica"
# Redirecionamento para arquivos
# script.sh > app.log 2> error.log
O redirecionamento 2>&1 combina stderr com stdout, útil quando você quer tudo em um único arquivo.
5. Logging em arquivo com rotação básica
Para logging persistente, escrevemos em arquivo usando >> (append). Uma rotação básica pode ser implementada manualmente verificando o tamanho do arquivo.
#!/bin/bash
LOG_FILE="/var/log/meu_script.log"
MAX_LOG_SIZE=1048576 # 1 MB em bytes
log() {
local LEVEL="$1"
local MESSAGE="$2"
local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
local SCRIPT_NAME=$(basename "$0")
# Verifica tamanho do log e faz rotação se necessário
if [ -f "$LOG_FILE" ]; then
local FILE_SIZE=$(stat -c%s "$LOG_FILE" 2>/dev/null)
if [ "$FILE_SIZE" -gt "$MAX_LOG_SIZE" ]; then
mv "$LOG_FILE" "${LOG_FILE}.old"
echo "[$TIMESTAMP] Log rotacionado" > "$LOG_FILE"
fi
fi
echo "[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE" >> "$LOG_FILE"
echo "[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE"
}
log "INFO" "Script em execução"
Para ambientes de produção, a ferramenta logrotate é mais robusta e recomendada, mas a implementação manual é útil para scripts simples.
6. Centralização de logs e boas práticas
Centralizar configurações de logging em variáveis globais facilita a manutenção. Boas práticas incluem:
- Definir diretório e nome do arquivo de log como variáveis
- Permitir configuração via argumentos de linha de comando
- Evitar logging de informações sensíveis (senhas, tokens)
#!/bin/bash
# Configurações globais
LOG_DIR="/var/log/meu_app"
LOG_FILE="${LOG_DIR}/app.log"
LOG_LEVEL="INFO" # Pode ser DEBUG, INFO, WARN, ERROR
VERBOSE=false
# Processa argumentos
while getopts "l:d:v" opt; do
case $opt in
l) LOG_LEVEL="$OPTARG" ;;
d) LOG_DIR="$OPTARG"; LOG_FILE="${LOG_DIR}/app.log" ;;
v) VERBOSE=true ;;
*) echo "Uso: $0 [-l nível] [-d diretório] [-v]" >&2; exit 1 ;;
esac
done
# Cria diretório se não existir
mkdir -p "$LOG_DIR"
log() {
local LEVEL="$1"
local MESSAGE="$2"
local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
local SCRIPT_NAME=$(basename "$0")
# Filtra por nível
case "$LOG_LEVEL" in
DEBUG) ;;
INFO) [ "$LEVEL" = "DEBUG" ] && return ;;
WARN) [ "$LEVEL" = "DEBUG" ] || [ "$LEVEL" = "INFO" ] && return ;;
ERROR) [ "$LEVEL" != "ERROR" ] && [ "$LEVEL" != "FATAL" ] && return ;;
esac
echo "[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE" >> "$LOG_FILE"
[ "$VERBOSE" = true ] && echo "[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE"
}
log "INFO" "Script configurado com nível $LOG_LEVEL"
7. Exemplo completo de script com logging
Abaixo, um script completo que integra todas as técnicas apresentadas:
#!/bin/bash
# ============================================
# Script de backup com logging estruturado
# ============================================
# Configurações
LOG_DIR="/var/log/backup"
LOG_FILE="${LOG_DIR}/backup_$(date +%Y%m%d).log"
BACKUP_DIR="/backup"
SOURCE_DIR="/dados"
MAX_LOG_SIZE=5242880 # 5 MB
# Cria diretórios necessários
mkdir -p "$LOG_DIR" "$BACKUP_DIR"
# Função de logging
log() {
local LEVEL="$1"
local MESSAGE="$2"
local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
local SCRIPT_NAME=$(basename "$0")
# Rotação de log
if [ -f "$LOG_FILE" ]; then
local FILE_SIZE=$(stat -c%s "$LOG_FILE" 2>/dev/null || echo 0)
if [ "$FILE_SIZE" -gt "$MAX_LOG_SIZE" ]; then
mv "$LOG_FILE" "${LOG_FILE}.old"
echo "[$TIMESTAMP] Log rotacionado" > "$LOG_FILE"
fi
fi
# Saída para arquivo
echo "[$TIMESTAMP] [$SCRIPT_NAME] [$LEVEL] $MESSAGE" >> "$LOG_FILE"
# Saída para terminal com cores
case "$LEVEL" in
INFO) echo -e "\033[0;32m[$TIMESTAMP] [$LEVEL] $MESSAGE\033[0m" ;;
WARN) echo -e "\033[1;33m[$TIMESTAMP] [$LEVEL] $MESSAGE\033[0m" >&2 ;;
ERROR) echo -e "\033[0;31m[$TIMESTAMP] [$LEVEL] $MESSAGE\033[0m" >&2 ;;
FATAL) echo -e "\033[0;31m[$TIMESTAMP] [$LEVEL] $MESSAGE\033[0m" >&2 ;;
esac
}
# Função para realizar backup
backup_directory() {
local DIR="$1"
local DEST="$2"
local NAME=$(basename "$DIR")
log "INFO" "Iniciando backup de $DIR para $DEST"
if [ ! -d "$DIR" ]; then
log "ERROR" "Diretório $DIR não existe"
return 1
fi
tar -czf "${DEST}/${NAME}_$(date +%Y%m%d_%H%M%S).tar.gz" "$DIR" 2>> "$LOG_FILE"
if [ $? -eq 0 ]; then
log "INFO" "Backup de $DIR concluído com sucesso"
else
log "ERROR" "Falha no backup de $DIR"
return 1
fi
}
# Execução principal
log "INFO" "=== Início do script de backup ==="
backup_directory "$SOURCE_DIR" "$BACKUP_DIR"
BACKUP_STATUS=$?
if [ $BACKUP_STATUS -eq 0 ]; then
log "INFO" "=== Backup concluído com sucesso ==="
else
log "FATAL" "=== Backup falhou ==="
exit 1
fi
log "INFO" "Script finalizado"
8. Considerações finais e próximos passos
O logging estruturado em scripts Bash traz benefícios significativos: facilita a depuração, fornece auditoria confiável e permite monitoramento automatizado. As técnicas apresentadas — funções reutilizáveis, níveis de severidade, redirecionamento adequado e rotação de logs — formam uma base sólida para qualquer script.
Para avançar, considere integrar seus logs com ferramentas externas como syslog (usando logger), journald (systemd) ou sistemas centralizados como ELK Stack. Além disso, explore artigos complementares desta série sobre debugging com set -x e tratamento de erros com trap para um controle ainda mais refinado.
Referências
- Bash Reference Manual: Redirections — Documentação oficial sobre redirecionamento de stdout e stderr no Bash
- Advanced Bash-Scripting Guide: Logging — Guia avançado com exemplos de logging e depuração em scripts Bash
- Logrotate Man Page — Documentação oficial da ferramenta logrotate para rotação de arquivos de log
- Bash Hackers Wiki: Debugging — Dicas de debugging e logging para scripts Bash
- Shell Scripting Tutorial: Error Handling — Tutorial sobre tratamento de erros e logging com trap no Bash