Immagina di aver lasciato il tuo bot libero di girare nel database per fare operazioni complesse. Prima o poi inciampa, sbaglia o si trova davanti a una situazione inaspettata. Senza logging, potrebbe semplicemente zittirsi e tu rimani lì a chiederti cosa sia andato storto. Il logging automatico ti aiuta a:
- Tracciare quando succedono errori e warning.
- Capire la natura e le cause dei problemi.
- Migliorare la diagnostica e la performance del codice.
Con il logging automatico crei una sorta di "scatola nera" che registra gli eventi nel database e ti aiuta a trovare i bug, come se fossi un vero detective.
Come creare il logging automatico tramite funzioni
- Definiamo una tabella per i log
Per loggare gli errori, ci serve un posto dove salvarli. Abbiamo già creato la tabella error_log nella lezione precedente:
CREATE TABLE error_log (
id SERIAL PRIMARY KEY, -- Identificatore unico della riga
error_message TEXT NOT NULL, -- Messaggio di errore
error_time TIMESTAMP DEFAULT NOW(), -- Momento in cui è successo l'errore
function_name TEXT -- Nome della funzione che ha causato l'errore
);
Questa tabella ha tutto quello che serve per registrare gli errori: testo dell'errore, quando è successo e la funzione da cui è partito.
- Creiamo una funzione per scrivere i log
Il prossimo step è creare una funzione universale che scrive gli errori nella tabella error_log. Questa funzione verrà chiamata ogni volta che vogliamo registrare un errore.
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);
-- Messaggio di logging avvenuto con successo
RAISE NOTICE 'Errore loggato: %', p_error_message;
END;
$$ LANGUAGE plpgsql;
Vediamo questo codice:
p_error_messageep_function_namesono i parametri della funzione che ricevono il messaggio di errore e il nome della funzione chiamante.INSERT INTO error_logaggiunge una riga nella tabella.RAISE NOTICEstampa un messaggio in console così lo sviluppatore sa che il logging è avvenuto.
Ora abbiamo risolto il primo problema: possiamo scrivere errori nella nostra tabella con il minimo sforzo.
Usare la funzione log_error in casi reali
Esempio 1: Logging di errori nella divisione per zero
Creiamo una funzione che fa una semplice divisione, ma logga un errore se il denominatore è 0.
CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
result NUMERIC;
BEGIN
IF b = 0 THEN
-- Chiamiamo la funzione di logging
PERFORM log_error('Tentativo di divisione per zero!', 'divide_numbers');
-- Generiamo un'eccezione
RAISE EXCEPTION 'Divisione per zero non permessa.';
END IF;
-- Facciamo la divisione
result := a / b;
RETURN result;
END;
$$ LANGUAGE plpgsql;
- Se il denominatore è 0, viene chiamata la funzione
log_errorche scrive l'errore nella tabella. - Dopo aver scritto l'errore, viene generata un'eccezione con
RAISE EXCEPTIONper avvisare l'utente.
Esempio di chiamata:
SELECT divide_numbers(10, 0);
Risultato:
- Chiamando questa funzione con divisione per zero, l'errore viene scritto nella tabella
error_log. - L'utente vede il messaggio di errore nella sua console.
Esempio 2: logging quando si inseriscono dati non validi
Vediamo un esempio con una funzione che aggiunge un nuovo studente nella tabella students. Se il nome dello studente è vuoto, logghiamo l'evento e interrompiamo l'esecuzione.
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('Il nome dello studente deve essere inserito!', 'add_student');
RAISE EXCEPTION 'Il nome dello studente non può essere vuoto.';
END IF;
INSERT INTO students (name) VALUES (p_name);
END;
$$ LANGUAGE plpgsql;
Esempio di chiamata:
SELECT add_student('');
Se proviamo ad aggiungere uno studente senza nome, la funzione crea una riga in error_log con il messaggio giusto.
Esempio 3: logging di warning
Non sempre serve generare un'eccezione per l'utente. A volte basta registrare un warning. Facciamo una funzione che controlla l'età dello studente:
CREATE OR REPLACE FUNCTION check_age(p_age INT)
RETURNS VOID AS $$
BEGIN
IF p_age < 18 THEN
-- Logghiamo il warning ma non interrompiamo l'esecuzione
PERFORM log_error('L\'età dello studente è sotto i 18.', 'check_age');
RAISE NOTICE 'Attenzione: l\'età dello studente è sotto i 18.';
END IF;
RAISE NOTICE 'Controllo età superato.';
END;
$$ LANGUAGE plpgsql;
Esempio di chiamata:
SELECT check_age(16);
Risultato:
- Registrazione del warning nella tabella
error_log. - Notifica in console che l'età dello studente è sotto i 18.
Logging e gestione delle eccezioni
Mettiamo insieme la scrittura degli errori e la gestione delle eccezioni in una funzione più complessa. Immagina di dover ricalcolare i voti degli studenti nella tabella grades. Se il processo fallisce per uno studente, l'errore viene loggato ma l'operazione continua.
CREATE OR REPLACE FUNCTION recalculate_grades()
RETURNS VOID AS $$
DECLARE
student RECORD;
BEGIN
FOR student IN SELECT * FROM students LOOP
BEGIN
-- Esempio di task: aggiornamento dei voti dello studente
UPDATE grades SET final_grade = final_grade + 1
WHERE student_id = student.id;
RAISE NOTICE 'Voti aggiornati per lo studente %', student.name;
EXCEPTION WHEN OTHERS THEN
-- Logghiamo l'errore e continuiamo
PERFORM log_error('Impossibile aggiornare i voti per lo studente ' || student.name, 'recalculate_grades');
END;
END LOOP;
END;
$$ LANGUAGE plpgsql;
Esempio di chiamata:
SELECT recalculate_grades();
Questo approccio rende la tua funzione molto più resistente, perché gli errori vengono loggati separatamente e non bloccano l'esecuzione su tutti i dati.
GO TO FULL VERSION