Extending Oracle for System Event Auditing
Some of the most exciting new features within Oracle are the new system-level triggers that were introduced in Oracle8i. What is truly exciting is that we can combine the system-level triggers with STATSPACK extension tables, thereby building a mechanism that will give complete auditing information on user logon and logoff, DDL, and server errors.
Just as an Oracle trigger fires on a specific DM event, system-level triggers are fired at specific system events such as logon, logoff, database startup, DDL execution, and servererror triggers:
- Database startup triggers - Database startup triggers can be used to invoke the dbms_shared_pool.keep procedure to pin all frequently-referenced PL/SQL packages into RAM.
- Logon triggers - The logon triggers can be used to store login information directly inside Oracle tables, thereby providing a complete audit of all those times when users entered your system.
- Logoff triggers - Oracle logoff triggers can automatically capture elapsed session duration times for all users who have accessed your Oracle database.
- Servererror triggers - With the servererror trigger, the Oracle administrator can automatically log all serious PL/SQL errors into an Oracle STATSPACK extension table. This table, in turn, has an insert trigger that e-mails the server error directly to the Oracle professional for immediate notification.
- DDL triggers - Using the DDL trigger, the Oracle administrator can automatically track all changes to the database including changes to tables, indexes, and constraints. The data from this trigger is especially useful for change control for the Oracle DBA.
Now let's take a close look at how these triggers work with Oracle tables.
Database Startup Event Triggers
Package pinning has become a very important part of Oracle tuning, and with the introduction of system-level triggers in Oracle8i, we now have an easy tool to ensure that frequently executed PL/SQL remains cached inside the shared pool.
Just like using the KEEP pool with the data buffer caches, pinning packages ensures that the specified package always remains in the Most Recently Used (MRU) end of the data buffer. This prevents the PL/SQL from being paged-out, and then re-parsed on reload. The Oracle DBA controls the size of this RAM region by setting the shared_pool_size parameter to a value large enough to hold all of the PL/SQL.
Pinning of packages involves two areas:
- Pinning frequently executed packages - Oracle performance can be greatly enhanced by pinning frequently executed packages inside the SGA.
- Pinning the standard Oracle packages - These are shown in the code listing below, and should always be opined to prevent re-parsing by the Oracle SGA.
You can interrogate the v$db_object_cache view to see the most frequently used packages, and automatically pin them at database startup time (with an ON DATABASE STARTUP trigger) using dbms_shared_pool.keep.
create or replace trigger pin_packs after startup on database begin -- Application-specific packages execute dbms_shared_pool.keep('MAIN_PACK'); execute dbms_shared_pool.keep('OTHER_PACK'); -- Oracle-supplied software packages execute dbms_shared_pool.keep('DBMS_ALERT'); execute dbms_shared_pool.keep('DBMS_DDL'); execute dbms_shared_pool.keep('DBMS_DESCRIBE'); execute dbms_shared_pool.keep('DBMS_LOCK'); execute dbms_shared_pool.keep('DBMS_OUTPUT'); execute dbms_shared_pool.keep('DBMS_PIPE'); execute dbms_shared_pool.keep('DBMS_SESSION'); execute dbms_shared_pool.keep('DBMS_STANDARD'); execute dbms_shared_pool.keep('DBMS_UTILITY'); execute dbms_shared_pool.keep('STANDARD'); end;
Extending STATSPACK Tables for System Events
Because the Oracle system-level triggers can interface with Oracle tables, the logical approach is to create Oracle tables to hold the data. And the logical place for this data is inside the STATSPACK tablespace, owned by the PERFSTAT user. Extending Oracle STATSPACK adds additional information to STATSPACK, and enhances existing metrics such as cumulative logins in the stats$sysstat table.
-
DDL table -Using the code snippet shown below, we create an Oracle table to capture all of the salient metrics required to do effective change management within an
Oracle environment. Note this table contains the date that the DDL was made, the ID of the user who originated the DDL change, the type of the object, and the object's
name. This information can be quite useful for tracking purposes.
connect sys/manager; drop table perfstat.stats$ddl_log; create table perfstat.stats$ddl_log ( user_name varchar2(30), ddl_date date, ddl_type varchar2(30), object_type varchar2(18), owner varchar2(30), object_name varchar2(128) ) tablespace perfstat ;
-
servererror table - The code snippet below creates an Oracle
table that will capture all information relating to PL/SQL errors:
connect sys/manager; drop table perfstat.stats$servererror_log; create table perfstat.stats$servererror_log ( error varchar2(30), timestamp date, username varchar2(30), osuser varchar2(30), machine varchar2(64), process varchar2(8), program varchar2(48) ) tablespace perfstat ;
-
login and logoff table -- We have created a table called
stats$user_log that can be used to trace both login and logoff events. Notice that this table contains the Oracle user ID, the name of the host server where the connection originated, the last program that was executed by the Oracle user, as well as their login and logoff times. Also, notice a special derived column called elapsed_minutes that is essentially the time difference (expressed in minutes) between the login time and logoff time.
connect sys/manager; drop table perfstat.stats$user_log; create table stats_user_log ( user_id varchar2(30), session_id number(8), host varchar2(30), last_program varchar2(48), last_action varchar2(32), last_module varchar2(32), logon_day date, logon_time varchar2(10), logoff_day date, logoff_time varchar2(10), elapsed_minutes number(8) ) ;
Inside the Trigger Definition Scripts
Once we've created the Oracle tables to support the system-level triggers, the next step is to actually write to triggers to add the data to the Oracle STATSPACK extension tables. By storing system event data in Oracle tables, we have the ability to track user behavior over time and audit and monitor important usage trends. Let's examine some working triggers to see how they function:
- DDL Trigger -- The DDL trigger executes every time a DDL statement is executed, and adds new entries to the stats$ddl_log table.
connect sys/manager create or replace trigger DDLTrigger AFTER DDL ON DATABASE BEGIN insert into perfstat.stats$ddl_log ( user_name, ddl_date, ddl_type, object_type, owner, object_name ) VALUES ( ora_login_user, sysdate, ora_sysevent, ora_dict_obj_type, ora_dict_obj_owner, ora_dict_obj_name ); END; /
-
Servererror trigger -- The servererror trigger takes whatever
server error was generated from Oracle PL/SQL and places it into an Oracle table. Note that by capturing the user ID and the time of the error, the Oracle administrator can build an insert trigger on the stats dollar or server error log table and immediately be notified via e-mail whenever a server error occurs.
connect sys/manager create or replace trigger log_errors_trig after servererror on database declare var_user varchar2(30); var_osuser varchar2(30); var_machine varchar2(64); var_process varchar2(8); var_program varchar2(48); begin select username, osuser, machine, process, program into var_user, var_osuser, var_machine, var_process, var_program from v$session where audsid=userenv('sessionid'); insert into perfstat.stats$servererror_log values( dbms_standard.server_error(1), sysdate, var_user, var_osuser, var_machine, var_process, var_program); end; /
-
Logon trigger -- Next, we need to take look at the login trigger.
In the code listing below we see that we are inserting into a table called stats_user_log. Inside the logon trigger you'll notice that we only populate selected columns of the table. At login time we only populate the user ID of the person who logs in and the time when they log into the system. We will use logoff trigger to fill in all the additional columns, including the all-important elapsed_minutes column.
create or replace trigger
logon_audit_trigger
AFTER LOGON ON DATABASE
BEGIN
insert into stats_user_log values(
user,
sys_context('USERENV','SESSIONID'),
sys_context('USERENV','HOST'),
null,
null,
null,
sysdate,
to_char(sysdate, 'hh24:mi:ss'),
null,
null,
null
);
COMMIT;
END;
/
- Logoff trigger -- Using the logoff trigger functionality, we can gain information about the time that the end user logged off of the system, the last program they were executing, the host that they were on as well as the elapsed time for that individual user. The code listing below shows how we implement this using an Oracle trigger:
create or replace trigger
logoff_audit_trigger
BEFORE LOGOFF ON DATABASE
BEGIN
-- *************************************************
-- Update the last action accessed
-- *************************************************
update
stats_user_log
set
last_action = (select action from v$session where
sys_context('USERENV','SESSIONID') = audsid)
where
sys_context('USERENV','SESSIONID') = session_id;
--*************************************************
-- Update the last program accessed
-- *************************************************
update
stats_user_log
set
last_program = (select program from v$session where
sys_context('USERENV','SESSIONID') = audsid)
where
sys_context('USERENV','SESSIONID') = session_id;
-- *************************************************
-- Update the last module accessed
-- *************************************************
update
stats_user_log
set
last_module = (select module from v$session where
sys_context('USERENV','SESSIONID') = audsid)
where
sys_context('USERENV','SESSIONID') = session_id;
-- *************************************************
-- Update the logoff day
-- *************************************************
update
stats_user_log
set
logoff_day = sysdate
where
sys_context('USERENV','SESSIONID') = session_id;
-- *************************************************
-- Update the logoff time
-- *************************************************
update
stats_user_log
set
logoff_time = to_char(sysdate, 'hh24:mi:ss')
where
sys_context('USERENV','SESSIONID') = session_id;
-- *************************************************
-- Compute the elapsed minutes
-- *************************************************
update
stats_user_log
set
elapsed_minutes =
round((logoff_day - logon_day)*1440)
where
sys_context('USERENV','SESSIONID') = session_id;
COMMIT;
END;
/
Now that we have seen the scripts, let's take a look at how we can get great reports on the behavior of Oracle system events.
Putting It All Together
Once we have all of this information captured in the Oracle tables and the system level triggers, we can now add great reports that show the behavior of things within our Oracle database. As we know, the Oracle administrator is very interested in information that relates to end-user activity, and we also want to be able to track all the significant DDL, server errors, and user login information.
Servererror Reports
Servererror reports are especially useful for auditing the behavior of PL/SQL in your production environment. Any time an error occurs, the servererror trigger will log the error to a table, and you can write summary reports of all Oracle server errors.
Sat Mar 09 page 1 Production Server Error Report Rollup of errors my day Error Date Hr MESSAGE Count ------------- -------------------------------------------------- -------- 03-08 ORA-02289: sequence does not exist 2,421 ORA-01001: invalid cursor 480 ORA-00054: resource busy and acquire with NOWAIT s 114 ORA-00942: table or view does not exist 39 ORA-00942: table or view does not exist 10 ORA-01017: invalid username/password; logon denied 2 ORA-00920: invalid relational operator 1 ORA-01445: cannot select ROWID from a join view wi 1 03-09 ORA-01001: invalid cursor 25 ORA-02289: sequence does not exist 12 ORA-00942: table or view does not exist 11 ORA-00054: resource busy and acquire with NOWAIT s 10 ORA-01017: invalid username/password; logon denied 2
This report shows daily details, but we can also create a weekly rollup of server errors:
Mon Jul 22 page 1 Production Database Changes Summary DDL Report Changed Number of DDL_D USER_NAME Object Production Changes ----- -------------------- --------------- ---------- 07-21 MWCEDI PACKAGE 6 MWCEDI PACKAGE BODY 6 ***** ---------- sum 12 07-17 MWCEDI PACKAGE 3 MWCEDI PACKAGE BODY 3 ***** ---------- sum 6 07-16 EUL_MWC VIEW 2 ***** ---------- sum 2 07-15 MWCEDI PACKAGE 5 MWCEDI PACKAGE BODY 5 APPS VIEW 1 MWCEDI PROCEDURE 1 ***** ----------- sum 12
Oracle Logon Report
We can use the logon and logoff trigger to store information into stats$user_log and then write SQL to get detailed reports on average visit length, number of logons per hour, and many other useful auditing reports.
Total Day User Minutes ---------- ---------- ------- 02-03-06 APPLSYSPUB 0 APPS 466 OPS$ORACLE 7 PERFSTAT 11 02-03-07 APPLSYSPUB 5 APPS 1,913 CUSJAN 1 JANEDI 5 OPS$ORACLE 6 PERFSTAT 134 SYS 58 02-03-08 APPLSYSPUB 1 APPS 5,866 OPS$ORACLE 15 PERFSTAT 44 SYS 6 02-03-09 APPS 0 OPS$ORACLE 0 PERFSTAT 29 Day HO NUMBER_OF_LOGINS ---------- -- ---------------- 02-03-06 11 37 12 28 13 45 14 38 15 26 16 26 17 25 18 26 19 26 20 26 21 49 22 26 23 24
DDL Reports
This report is critical for the Oracle DBA who must track changes to their production database. This report uses the DDL trigger and produces a complete audit log of all Oracle databases changes.
Date Type Object Object User of DDL of DDL Type Name ---------- ------------ -------- ----------- ---------------------------- SYS 03-07 10:11 DROP TRIGGER ERROR_AUDIT_TRIGGER APPS 03-07 10:12 ALTER TRIGGER LOG_ERRORS_TRIG APPS 03-07 10:14 ALTER TRIGGER LOG_ERRORS_TRIG SYS 03-07 10:23 CREATE TRIGGER ERROR_AUDIT_TRIGGER SYS 03-07 10:24 CREATE TRIGGER ERROR_AUDIT_TRIGGER SYS 03-07 10:25 CREATE TRIGGER ERROR_AUDIT_TRIGGER SYS 03-07 10:27 CREATE TRIGGER ERROR_AUDIT_TRIGGER SYS 03-07 10:28 CREATE TRIGGER ERROR_AUDIT_TRIGGER SYS 03-07 10:33 DROP TRIGGER LOG_ERRORS_TRIG OPS$ORACLE 03-07 12:56 CREATE TABLESPACE JANEDI OPS$ORACLE 03-07 12:57 CREATE TABLESPACE JANHF APPS 03-07 13:10 ALTER PACKAGE PA_MC_CURRENCY_PKG APPS 03-07 13:10 ALTER TRIGGER PA_MRC_DRAFT_INV_ITEMS_AIUD JANEDI 03-07 14:15 CREATE TABLE JAN_EDI_HEADERS JANEDI 03-07 14:15 CREATE INDEX JAN_EDI_HEAD_N1 JANEDI 03-07 14:15 CREATE TABLE JAN_EDI_LINES JANEDI 03-07 14:15 CREATE INDEX JAN_EDI_LINE_N1 JANEDI 03-07 14:15 CREATE TABLE JAN_EDI_ERRORS JANEDI 03-07 14:15 CREATE TABLE JAN_EDI_GP_ORDERS JANEDI 03-07 14:15 CREATE INDEX JAN_EDI_GP_N1 JANEDI 03-07 14:15 CREATE INDEX JAN_EDI_GP_N2 JANEDI 03-07 14:15 CREATE TABLE JAN_EDI_GP_ERRORS JANEDI 03-07 14:15 CREATE TABLE JAN_EDI_GP_CONTROLS JANEDI 03-07 14:15 CREATE INDEX JAN_EDI_GP_CNTL_N1 JANEDI 03-07 14:18 ALTER TABLE JAN_EDI_HEADERS JANEDI 03-07 14:18 ALTER TABLE JAN_EDI_LINES JANEDI 03-07 14:18 ALTER TABLE JAN_EDI_ERRORS JANEDI 03-07 14:18 ALTER TABLE JAN_EDI_GP_ORDERS JANEDI 03-07 14:18 ALTER TABLE JAN_EDI_GP_ERRORS JANEDI 03-07 14:18 ALTER TABLE JAN_EDI_GP_CONTROLS OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_DEPARTMENTS OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_FORECAST_INTERFACE OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_XREF_LOAD OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_JOBS_INTERFACE OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_ROUTING_COMMENTS OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_EDI_HEADERS OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_EDI_LINES OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_EDI_ERRORS OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_EDI_GP_ORDERS OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_EDI_GP_ERRORS OPS$ORACLE 03-07 14:20 ALTER TABLE JAN_EDI_GP_CONTROLS SYS 03-07 15:44 CREATE TRIGGER ERROR_AUDIT_TRIGGER SYS 03-07 15:45 CREATE TRIGGER ERROR_AUDIT_TRIGGER OPS$ORACLE 03-08 07:20 DROP TABLE ORACHECK_FS_TEMP OPS$ORACLE 03-08 07:20 CREATE TABLE ORACHECK_FS_TEMP APPS 03-08 11:21 ALTER TRIGGER ERROR_AUDIT_TRIGGER SYS 03-08 11:21 CREATE TRIGGER ERROR_AUDIT_TRIGGER APPS 03-08 11:23 ALTER TRIGGER ERROR_AUDIT_TRIGGER SYS 03-08 11:25 CREATE TRIGGER ERROR_AUDIT_TRIGGER SYS 03-08 12:54 ALTER TRIGGER ERROR_AUDIT_TRIGGER SYS 03-08 12:54 DROP TRIGGER ERROR_AUDIT_TRIGGER SYS 03-08 12:56 CREATE TRIGGER ERROR_AUDIT_TRIGGER OPS$ORACLE 03-09 07:20 DROP TABLE ORACHECK_FS_TEMP OPS$ORACLE 03-09 07:20 CREATE TABLE ORACHECK_FS_TEMP
--
Donald K. Burleson is one of the world’s top Oracle Database experts with more than 20 years of full-time DBA experience. He specializes in creating database architectures for very large online databases and he has worked with some of the world’s most powerful and complex systems. A former Adjunct Professor, Don Burleson has written 15 books, published more than 100 articles in national magazines, serves as Editor-in-Chief of Oracle Internals and edits for Rampant TechPress. Don is a popular lecturer and teacher and is a frequent speaker at Oracle Openworld and other international database conferences. Don's Web sites include DBA-Oracle, Remote-DBA, Oracle-training, remote support and remote DBA.
Contributors : Donald K. Burleson
Last modified 2005-06-22 12:34 AM