CodeGym /Cursos /SQL SELF /Log automático usando funções

Log automático usando funções

SQL SELF
Nível 52, Lição 3
Disponível

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

  1. 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.

  1. 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:

  1. p_error_message e p_function_name — são os parâmetros da função, recebem a mensagem do erro e o nome da função que chamou.
  2. INSERT INTO error_log adiciona o registro na tabela.
  3. RAISE NOTICE mostra 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 EXCEPTION pra 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.

2
Tarefa
SQL SELF, nível 52, lição 3
Bloqueado
Log de erros em cálculos
Log de erros em cálculos
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION