Imagina que você soltou seu bot pra rodar livremente no banco de dados, fazendo operações cabulosas. Mais cedo ou mais tarde ele vai tropeçar, errar ou cair numa situação inesperada. Sem logging, ele pode simplesmente ficar quieto e você vai ficar sem entender o que rolou. O log automático ajuda a:
- Acompanhar quando rolam erros e avisos.
- Sacar a natureza e as causas dos problemas.
- Melhorar o diagnóstico e a performance do código.
Com log automático, você cria uma “caixa preta” que registra eventos no banco e te ajuda a achar bugs, tipo um detetive de primeira.
Criando log automático com funções
- Definindo a tabela de logs
Pra logar erros, a gente precisa de um lugar pra guardar eles. Já criamos a tabela error_log na aula passada:
CREATE TABLE error_log (
id SERIAL PRIMARY KEY, -- Identificador único do registro
error_message TEXT NOT NULL, -- Mensagem do erro
error_time TIMESTAMP DEFAULT NOW(), -- Momento em que o erro rolou
function_name TEXT -- Nome da função que causou o erro
);
Essa tabela tem tudo que precisa pra registrar erros: texto do erro, quando rolou e de qual função veio.
- Criando a função pra registrar logs
O próximo passo é criar uma função universal que vai registrar erros na tabela error_log. Essa função vai ser chamada toda vez que a gente quiser registrar um erro.
CREATE OR REPLACE FUNCTION log_error(p_error_message TEXT, p_function_name TEXT)
RETURNS VOID AS $$
BEGIN
INSERT INTO error_log (error_message, function_name)
VALUES (p_error_message, p_function_name);
-- Mensagem de log registrado com sucesso
RAISE NOTICE 'Erro registrado: %', p_error_message;
END;
$$ LANGUAGE plpgsql;
Vamos destrinchar esse código:
p_error_messageep_function_name— são os parâmetros da função, recebem a mensagem do erro e o nome da função que chamou.INSERT INTO error_logadiciona o registro na tabela.RAISE NOTICEmostra uma mensagem no console pra avisar o dev que o log rolou.
Agora a gente já resolveu a primeira missão: dá pra registrar erros na nossa tabela sem esforço.
Usando a função log_error em tarefas reais
Exemplo 1: Logando erro ao dividir por zero
Vamos criar uma função que faz uma divisão simples, mas registra o erro se o denominador for 0.
CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
result NUMERIC;
BEGIN
IF b = 0 THEN
-- Chama a função de log
PERFORM log_error('Tentativa de divisão por zero!', 'divide_numbers');
-- Gera exceção
RAISE EXCEPTION 'Divisão por zero não é permitida.';
END IF;
-- Faz a divisão
result := a / b;
RETURN result;
END;
$$ LANGUAGE plpgsql;
- Se o denominador for 0, chama a função
log_error, que registra o erro na tabela. - Depois de registrar o erro, gera uma exceção
RAISE EXCEPTIONpra avisar o usuário.
Exemplo de chamada:
SELECT divide_numbers(10, 0);
Resultado:
- Chamar essa função com divisão por zero vai registrar o erro na tabela
error_log. - O usuário vai ver a mensagem de erro no console.
Exemplo 2: logando ao tentar inserir dados inválidos
Vamos ver um exemplo com uma função que adiciona um novo estudante na tabela students. Se o nome do estudante estiver vazio, a gente loga o evento e interrompe a execução.
CREATE OR REPLACE FUNCTION add_student(p_name TEXT)
RETURNS VOID AS $$
BEGIN
IF p_name IS NULL OR p_name = '' THEN
PERFORM log_error('Nome do estudante deve ser informado!', 'add_student');
RAISE EXCEPTION 'Nome do estudante não pode ser vazio.';
END IF;
INSERT INTO students (name) VALUES (p_name);
END;
$$ LANGUAGE plpgsql;
Exemplo de chamada:
SELECT add_student('');
Se a gente tentar adicionar um estudante sem nome, a função vai criar um registro em error_log com a mensagem certa.
Exemplo 3: logando avisos
Nem sempre precisa gerar exceção pro usuário. Às vezes só registrar um aviso já resolve. Vamos fazer uma função pra checar a idade do estudante:
CREATE OR REPLACE FUNCTION check_age(p_age INT)
RETURNS VOID AS $$
BEGIN
IF p_age < 18 THEN
-- Loga o aviso, mas não interrompe a execução
PERFORM log_error('Idade do estudante é menor que 18.', 'check_age');
RAISE NOTICE 'Aviso: idade do estudante é menor que 18.';
END IF;
RAISE NOTICE 'Checagem de idade passou.';
END;
$$ LANGUAGE plpgsql;
Exemplo de chamada:
SELECT check_age(16);
Resultado:
- Registro do aviso na tabela
error_log. - Notificação no console avisando que a idade do estudante é menor que 18.
Logging e tratamento de exceções
Bora juntar registro de erros e tratamento de exceções numa função mais complexa. Imagina que a gente tem que recalcular as notas dos estudantes na tabela grades. Se não conseguir pra algum estudante, o erro é registrado mas a operação segue.
CREATE OR REPLACE FUNCTION recalculate_grades()
RETURNS VOID AS $$
DECLARE
student RECORD;
BEGIN
FOR student IN SELECT * FROM students LOOP
BEGIN
-- Exemplo de tarefa: atualizar nota do estudante
UPDATE grades SET final_grade = final_grade + 1
WHERE student_id = student.id;
RAISE NOTICE 'Notas atualizadas para o estudante %', student.name;
EXCEPTION WHEN OTHERS THEN
-- Loga o erro e continua a execução
PERFORM log_error('Falha ao atualizar notas para o estudante ' || student.name, 'recalculate_grades');
END;
END LOOP;
END;
$$ LANGUAGE plpgsql;
Exemplo de chamada:
SELECT recalculate_grades();
Esse jeito deixa sua função bem mais resiliente, porque os erros são registrados separadamente e não travam a execução pra todo mundo.
GO TO FULL VERSION