Here is a way to create generic triggers, from Tracking changes in PostgreSQL by Hans-Jürgen Schönig and comments on that blog post.
CREATE SCHEMA logging;
CREATE TABLE logging.t_history (
id serial,
tstamp timestamp DEFAULT now(),
schemaname text,
tabname text,
operation text,
who text DEFAULT current_user,
new_val jsonb,
old_val jsonb
);
CREATE FUNCTION logging.change_trigger() RETURNS trigger AS $$
BEGIN
INSERT INTO logging.t_history (tabname, schemaname, operation, new_val, old_val)
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, pg_catalog.row_to_json(NEW), pg_catalog.row_to_json(OLD));
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER
SET search_path = pg_catalog,pg_temp;
CREATE TRIGGER audit_important_table
AFTER INSERT OR UPDATE OR DELETE ON important_table
FOR EACH ROW EXECUTE PROCEDURE logging.change_trigger();
Note that the trigger is marked SECURITY DEFINER which is the moral equivalent of a setuid binary; see Writing
SECURITY DEFINER
Functions Safely and Abusing SECURITY DEFINER functions in PostgreSQL.