Ás vezes precisamos vigiar todas as alterações que acontecem em nossas tabelas para fim de auditoria. Abaixo segue uma função que pode ser colocada para fazer tal trabalho. Usaremos a extensão hstore para nos ajudar.

CREATE EXTENSION hstore;


--Tabela de Auditoria
CREATE TABLE auditoria(
id bigserial,
tabela text,
data date default current_date,
hora time WITHOUT TIME ZONE default CURRENT_TIME,
type char(1),
usuario text,
estacao text,
ip_estacao text,
ordem text,
campos hstore);

Criamos a extensão e a tabela que armazenará as alterações feitas nas tabelas. 

--FUNCTION
CREATE OR REPLACE FUNCTION fn_auditoria()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
v_new text[];
v_old text[];
v_fields hstore;
v_fields_old hstore;
pk_field record;
BEGIN
CASE TG_OP
WHEN 'INSERT' THEN
v_fields = hstore(NEW);
WHEN 'UPDATE' THEN
v_fields = hstore(array[]::text[]);
v_fields_old = hstore(array[]::text[]);
v_new = hstore_to_matrix(hstore(NEW));
v_old = hstore_to_matrix(hstore(OLD));


FOR i IN 1..array_upper(v_new, 1) LOOP
RAISE NOTICE 'Value of % is %', v_new[i][1], v_new[i][2];

IF (v_new[i][2] <> v_old[i][2]) THEN
v_fields = v_fields || hstore(v_new[i][1], v_new[i][2]);
v_fields_old = v_fields_old || hstore(v_old[i][1], v_old[i][2]);

ELSE
FOR pk_field IN (SELECT kcu.column_name
FROM INFORMATION_SCHEMA.TABLES t
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
ON tc.table_catalog = t.table_catalog
AND tc.table_schema = t.table_schema
AND tc.table_name = t.table_name
AND tc.constraint_type = 'PRIMARY KEY'
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
ON kcu.table_catalog = tc.table_catalog
AND kcu.table_schema = tc.table_schema
AND kcu.table_name = tc.table_name
AND kcu.constraint_name = tc.constraint_name
WHERE t.table_schema = TG_TABLE_SCHEMA AND t.table_name = TG_TABLE_NAME
ORDER BY t.table_catalog,
t.table_schema,
t.table_name,
kcu.constraint_name,
kcu.ordinal_position) LOOP

IF (v_new[i][1] = pk_field.column_name) THEN
v_fields = v_fields || hstore(v_new[i][1], v_new[i][2]);
v_fields_old = v_fields_old || hstore(v_old[i][1], v_old[i][2]);
END IF;
END LOOP;
END IF;
END LOOP;
WHEN 'DELETE' THEN
v_fields_old = hstore(old);
END CASE;

IF (TG_OP = 'INSERT') OR (TG_OP = 'UPDATE') THEN

INSERT INTO auditoria(
tabela,
type,
usuario,
estacao,
ip_estacao,
ordem,
campos)
VALUES(
TG_TABLE_NAME,
substr(TG_OP, 1, 1),
current_setting('app.usuario'),
current_setting('app.estacao'),
current_setting('app.ip_estacao'),
'NOVO',
v_fields);
END IF;

IF (TG_OP = 'DELETE') OR (TG_OP = 'UPDATE') THEN

INSERT INTO auditoria(
tabela,
type,
usuario,
estacao,
ip_estacao,
ordem,
campos)
VALUES(
TG_TABLE_NAME,
substr(TG_OP, 1, 1),
current_setting('app.usuario'),
current_setting('app.estacao'),
current_setting('app.ip_estacao'),
'ANTIGO',
v_fields_old);
END IF;
RETURN NULL;
END;
$function$;

Com a função criada, basta criar a trigger nas tabelas que deseja monitorar as alterações.

CREATE TRIGGER tg_produtos
AFTER INSERT OR DELETE OR UPDATE ON nome_tabela
FOR EACH ROW EXECUTE PROCEDURE fn_auditoria();
Lembre-se que alguns valores devem ser definidos na aplicação, ou podem ser passados diretamente no Postgresql com o comando SET.

set app.usuario to 'NOME';
set app.estacao to 'PC';
set app.ip_estacao to '192.168.0.1';
Desta forma os registros criados, deletados ou alterados nas tabelas, serão inseridos como registro na tabela auditoria.