Using Triggers To Add An Audit Log To Postgres Databases
Using Triggers To Add An Audit Log To Postgres Databases
schema_name | public
table_name | orders
relid | 17803
session_user_name | bill
action_tstamp_tx | 2014-12-03 03:47:38.832537+00
transaction_id | 79400
application_name |
client_addr | 54.155.24.2
client_port | 42333
action | U
statement_only | f
--
-- http://www.postgresql.org/docs/9.1/static/functions-info.html
--
-- Remember, every column you add takes up more audit table space
and slows audit
-- inserts.
--
-- Every index you add has a big impact too, so avoid adding
indexes to the
-- audit table unless you REALLY need them. The hstore GIST indexes
are
-- particularly expensive.
--
--
session_user_name text,
application_name text,
client_addr inet,
client_port integer,
client_query text,
row_data hstore,
changed_fields hstore,
);
DECLARE
audit_row audit.logged_actions;
include_values boolean;
log_diffs boolean;
h_old hstore;
h_new hstore;
BEGIN
END IF;
audit_row = ROW(
nextval('audit.logged_actions_event_id_seq'), -- event_id
TG_TABLE_SCHEMA::text, --
schema_name
TG_TABLE_NAME::text, -- table_name
TG_RELID, -- relation
OID for much quicker searches
session_user::text, --
session_user_name
current_timestamp, --
action_tstamp_tx
statement_timestamp(), --
action_tstamp_stm
clock_timestamp(), --
action_tstamp_clk
txid_current(), --
transaction ID
current_setting('application_name'), -- client
application
inet_client_addr(), --
client_addr
inet_client_port(), --
client_port
current_query(), -- top-level
query or queries (if multistatement) from client
substring(TG_OP,1,1), -- action
'f' --
statement_only
);
audit_row.client_query = NULL;
END IF;
excluded_cols = TG_ARGV[1]::text[];
END IF;
IF (TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW') THEN
audit_row.row_data = hstore(OLD.*);
audit_row.changed_fields = (hstore(NEW.*) -
audit_row.row_data) - excluded_cols;
RETURN NULL;
END IF;
audit_row.statement_only = 't';
ELSE
RETURN NULL;
END IF;
RETURN NULL;
END;
$body$
LANGUAGE plpgsql
SECURITY DEFINER
a 'FOR EACH STATEMENT' rather than 'FOR EACH ROW' trigger if you do
not
Note that the user name logged is the login role for the session.
The audit trigger
$body$;
DECLARE
_q_txt text;
BEGIN
IF audit_rows THEN
END IF;
target_table ||
quote_literal(audit_query_text) ||
_ignored_cols_snip || ');';
EXECUTE _q_txt;
stm_targets = 'TRUNCATE';
ELSE
END IF;
target_table ||
quote_literal(audit_query_text) || ');';
EXECUTE _q_txt;
END;
$body$
language 'plpgsql';
COMMENT ON FUNCTION audit.audit_table(regclass, boolean, boolean,
text[]) IS $body$
Arguments:
$body$;
--
$$ LANGUAGE 'sql';
$body$;
Use the comments to share how you have handled the need to store
information about changes made to your databases.