Stell dir vor, du schickst deinen Bot auf eigene Faust in die Datenbank, damit er komplexe Operationen ausführt. Früher oder später stolpert er, macht einen Fehler oder trifft auf eine unerwartete Situation. Ohne Logging bleibt er einfach stumm und du fragst dich, was schiefgelaufen ist. Automatisches Logging hilft dir dabei:
- Fehler und Warnungen zu verfolgen.
- Die Ursache und den Grund für Ausfälle zu verstehen.
- Die Diagnose und Performance deines Codes zu verbessern.
Mit automatischem Logging baust du dir eine "Blackbox", die Events in der Datenbank festhält und dir hilft, Fehler zu finden – fast wie ein Top-Detektiv.
Automatisches Logging mit Funktionen erstellen
- Log-Tabelle definieren
Um Fehler zu loggen, brauchen wir einen Speicherort. Wir haben die Tabelle error_log schon in der letzten Vorlesung angelegt:
CREATE TABLE error_log (
id SERIAL PRIMARY KEY, -- Einzigartige ID des Eintrags
error_message TEXT NOT NULL, -- Fehlermeldung
error_time TIMESTAMP DEFAULT NOW(), -- Zeitpunkt des Fehlers
function_name TEXT -- Name der Funktion, die den Fehler ausgelöst hat
);
Diese Tabelle hat alles, was wir zum Fehler-Logging brauchen: Fehlermeldung, Zeitpunkt und die Funktion, aus der der Fehler kam.
- Funktion zum Schreiben ins Log erstellen
Der nächste Schritt: Wir bauen eine universelle Funktion, die Fehler in die error_log-Tabelle schreibt. Diese Funktion rufen wir immer dann auf, wenn wir einen Fehler festhalten wollen.
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);
-- Meldung über erfolgreiches Logging
RAISE NOTICE 'Fehler geloggt: %', p_error_message;
END;
$$ LANGUAGE plpgsql;
Schauen wir uns den Code an:
p_error_messageundp_function_namesind die Parameter der Funktion, die die Fehlermeldung und den Namen der aufrufenden Funktion aufnehmen.INSERT INTO error_logfügt einen Eintrag in die Tabelle ein.RAISE NOTICEgibt eine Meldung in der Konsole aus, damit der Entwickler über das Logging informiert ist.
Damit haben wir die erste Aufgabe gelöst: Wir können Fehler mit minimalem Aufwand in unsere Tabelle schreiben.
Die Funktion log_error in echten Aufgaben nutzen
Beispiel 1: Fehler beim Teilen durch Null loggen
Wir bauen eine Funktion, die eine einfache Division macht, aber einen Fehler loggt, wenn der Nenner 0 ist.
CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
result NUMERIC;
BEGIN
IF b = 0 THEN
-- Logging-Funktion aufrufen
PERFORM log_error('Division durch Null versucht!', 'divide_numbers');
-- Exception werfen
RAISE EXCEPTION 'Division durch Null ist nicht erlaubt.';
END IF;
-- Division ausführen
result := a / b;
RETURN result;
END;
$$ LANGUAGE plpgsql;
- Wenn der Nenner 0 ist, wird die Funktion
log_erroraufgerufen und der Fehler in die Tabelle geschrieben. - Nach dem Logging wird mit
RAISE EXCEPTIONeine Exception geworfen, damit der User Bescheid weiß.
Beispielaufruf:
SELECT divide_numbers(10, 0);
Ergebnis:
- Der Aufruf dieser Funktion mit Division durch Null schreibt einen Fehler in die
error_log-Tabelle. - Der User sieht die Fehlermeldung in seiner Konsole.
Beispiel 2: Logging beim Einfügen ungültiger Daten
Hier ein Beispiel mit einer Funktion, die einen neuen Studenten in die Tabelle students einfügt. Wenn der Name leer ist, loggen wir das Event und brechen ab.
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('Studentenname muss angegeben werden!', 'add_student');
RAISE EXCEPTION 'Studentenname darf nicht leer sein.';
END IF;
INSERT INTO students (name) VALUES (p_name);
END;
$$ LANGUAGE plpgsql;
Beispielaufruf:
SELECT add_student('');
Wenn wir versuchen, einen Studenten ohne Namen hinzuzufügen, erstellt die Funktion einen Eintrag in error_log mit der passenden Meldung.
Beispiel 3: Logging von Warnungen
Manchmal muss man nicht gleich eine Exception werfen. Es reicht, eine Warnung zu loggen. Wir machen eine Funktion, die das Alter eines Studenten prüft:
CREATE OR REPLACE FUNCTION check_age(p_age INT)
RETURNS VOID AS $$
BEGIN
IF p_age < 18 THEN
-- Warnung loggen, aber nicht abbrechen
PERFORM log_error('Studentenalter ist unter 18.', 'check_age');
RAISE NOTICE 'Warnung: Studentenalter ist unter 18.';
END IF;
RAISE NOTICE 'Altersprüfung bestanden.';
END;
$$ LANGUAGE plpgsql;
Beispielaufruf:
SELECT check_age(16);
Ergebnis:
- Warnung wird in die
error_log-Tabelle geschrieben. - In der Konsole gibt es eine Info, dass das Alter unter 18 ist.
Logging und Exception-Handling
Lass uns Logging und Exception-Handling in einer etwas komplexeren Funktion kombinieren. Stell dir vor, wir sollen die Noten der Studenten in der Tabelle grades neu berechnen. Wenn es bei einem Studenten nicht klappt, wird der Fehler geloggt, aber die Operation läuft weiter.
CREATE OR REPLACE FUNCTION recalculate_grades()
RETURNS VOID AS $$
DECLARE
student RECORD;
BEGIN
FOR student IN SELECT * FROM students LOOP
BEGIN
-- Beispielaufgabe: Noten des Studenten updaten
UPDATE grades SET final_grade = final_grade + 1
WHERE student_id = student.id;
RAISE NOTICE 'Noten für Studenten % aktualisiert', student.name;
EXCEPTION WHEN OTHERS THEN
-- Fehler loggen und weitermachen
PERFORM log_error('Konnte Noten für Studenten ' || student.name || ' nicht aktualisieren', 'recalculate_grades');
END;
END LOOP;
END;
$$ LANGUAGE plpgsql;
Beispielaufruf:
SELECT recalculate_grades();
Mit diesem Ansatz wird deine Funktion viel robuster, weil Fehler einzeln geloggt werden und die Verarbeitung für alle Daten nicht gestoppt wird.
GO TO FULL VERSION