CodeGym /Cursos /SQL SELF /Logging automático usando funciones

Logging automático usando funciones

SQL SELF
Nivel 52 , Lección 3
Disponible

Imagina que has soltado a tu bot en la base de datos para que haga operaciones complicadas. Tarde o temprano va a tropezar, cometer un error o encontrarse con algo inesperado. Sin logging, simplemente se va a quedar callado y tú te vas a quedar pensando qué demonios ha pasado. El logging automático te ayuda a:

  • Rastrear cuándo ocurren errores y advertencias.
  • Entender la naturaleza y las causas de los fallos.
  • Mejorar la diagnosis y el rendimiento del código.

Con logging automático creas una "caja negra" que registra eventos en la base de datos y te ayuda a encontrar errores como si fueras un detective de primera.

Cómo crear logging automático usando funciones

  1. Definir una tabla para los logs

Para guardar los errores necesitamos un sitio donde almacenarlos. Ya hemos creado la tabla error_log en la lección anterior:

CREATE TABLE error_log (
    id SERIAL PRIMARY KEY,                 -- Identificador único del registro
    error_message TEXT NOT NULL,          -- Mensaje de error
    error_time TIMESTAMP DEFAULT NOW(),   -- Momento en que ocurrió el error
    function_name TEXT                    -- Nombre de la función que lanzó el error
);

Esta tabla tiene todo lo necesario para guardar errores: el texto del error, cuándo ocurrió y la función desde la que salió.

  1. Crear una función para guardar logs

El siguiente paso es crear una función universal que guarde errores en la tabla error_log. Esta función se va a llamar cada vez que quieras registrar un error.

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);
    -- Mensaje de logging exitoso
    RAISE NOTICE 'Error registrado: %', p_error_message;
END;
$$ LANGUAGE plpgsql;

Vamos a ver este código:

  1. p_error_message y p_function_name son los parámetros de la función, que reciben el mensaje de error y el nombre de la función que lo lanzó.
  2. INSERT INTO error_log añade el registro a la tabla.
  3. RAISE NOTICE muestra un mensaje en la consola para que el desarrollador sepa que se está haciendo logging.

Ahora tenemos resuelto el primer problema: podemos guardar errores en nuestra tabla con el mínimo esfuerzo.

Usando la función log_error en tareas reales

Ejemplo 1: Logging de errores al dividir por cero

Vamos a crear una función que hace una división simple, pero guarda un error si el denominador es 0.

CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
    result NUMERIC;
BEGIN
    IF b = 0 THEN
        -- Llamamos a la función de logging
        PERFORM log_error('¡Intento de división por cero!', 'divide_numbers');
        -- Generamos una excepción
        RAISE EXCEPTION 'No se permite dividir por cero.';
    END IF;

    -- Hacemos la división
    result := a / b;
    RETURN result;
END;
$$ LANGUAGE plpgsql;
  • Si el denominador es 0, se llama a la función log_error, que guarda el error en la tabla.
  • Después de guardar el error, se lanza una excepción con RAISE EXCEPTION para avisar al usuario.

Ejemplo de llamada:

SELECT divide_numbers(10, 0);

Resultado:

  • Llamar a esta función con división por cero va a guardar el error en la tabla error_log.
  • El usuario verá el mensaje de error en su consola.

Ejemplo 2: logging al insertar datos no válidos

Vamos a ver un ejemplo con una función que añade un nuevo estudiante a la tabla students. Si el nombre del estudiante está vacío, registramos el evento y paramos la ejecución.

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('¡El nombre del estudiante es obligatorio!', 'add_student');
        RAISE EXCEPTION 'El nombre del estudiante no puede estar vacío.';
    END IF;

    INSERT INTO students (name) VALUES (p_name);
END;
$$ LANGUAGE plpgsql;

Ejemplo de llamada:

SELECT add_student('');

Si intentamos añadir un estudiante sin nombre, la función creará un registro en error_log con el mensaje correspondiente.

Ejemplo 3: logging de advertencias

No siempre hace falta lanzar una excepción al usuario. A veces basta con registrar una advertencia. Vamos a hacer una función para comprobar la edad de un estudiante:

CREATE OR REPLACE FUNCTION check_age(p_age INT)
RETURNS VOID AS $$
BEGIN
    IF p_age < 18 THEN
        -- Registramos una advertencia, pero no paramos la ejecución
        PERFORM log_error('La edad del estudiante es menor de 18.', 'check_age');
        RAISE NOTICE 'Advertencia: La edad del estudiante es menor de 18.';
    END IF;

    RAISE NOTICE 'Comprobación de edad pasada.';
END;
$$ LANGUAGE plpgsql;

Ejemplo de llamada:

SELECT check_age(16);

Resultado:

  • Registro de la advertencia en la tabla error_log.
  • Notificación en la consola de que la edad del estudiante es menor de 18.

Logging y manejo de excepciones

Vamos a juntar el registro de errores y el manejo de excepciones en una función más compleja. Imagina que tienes que recalcular las notas de los estudiantes en la tabla grades. Si el proceso falla para uno de los estudiantes, el error se registra pero la operación sigue para los demás.

CREATE OR REPLACE FUNCTION recalculate_grades()
RETURNS VOID AS $$
DECLARE
    student RECORD;
BEGIN
    FOR student IN SELECT * FROM students LOOP
        BEGIN
            -- Ejemplo de tarea: actualizar las notas del estudiante
            UPDATE grades SET final_grade = final_grade + 1
            WHERE student_id = student.id;

            RAISE NOTICE 'Notas actualizadas para el estudiante %', student.name;
        EXCEPTION WHEN OTHERS THEN
            -- Registramos el error y seguimos ejecutando
            PERFORM log_error('No se pudieron actualizar las notas para el estudiante ' || student.name, 'recalculate_grades');
        END;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

Ejemplo de llamada:

SELECT recalculate_grades();

Este enfoque hace que tu función sea mucho más resistente, porque los errores se registran por separado y no paran la ejecución para todos los datos.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION