是“数据库触发服务器错误之后”是一个好主意吗?

时间:2019-07-14 10:24:12

标签: oracle performance oracle11g database-trigger audit

对于几个失败问题,我编写了此触发器-audit_failed_trg

作为after servererror on database触发器。

我的第一个想法是根据需要只检查特定的异常\ user \ table。

但是我只是想知道-数据库应该为任何失败触发该触发器。

在生产环境中启用它是一个好主意吗?

这会导致性能问题或其他问题吗?

我正在使用Oracle 11g。

create or replace trigger audit_failed_trg
after servererror on database
declare
l_sql_text ora_name_list_t;
l_n        number;
  begin

insert into T values ( S.NEXTVAL, 1, 'ora_sysevent = ' || ORA_SYSEVENT ,sysdate);
insert into T values ( S.CURRVAL, 2, 'ora_login_user = ' || ORA_LOGIN_USER,sysdate );
insert into T values ( S.CURRVAL, 3, 'ora_server_error = ' || ORA_SERVER_ERROR(1),sysdate );
insert into T values ( S.CURRVAL, 4, 'SID = ' || SYS_CONTEXT ('USERENV','SID'),sysdate);
insert into T values ( S.CURRVAL, 5, 'host = ' || SYS_CONTEXT ('USERENV','HOST') ,sysdate);
insert into T values ( S.CURRVAL, 6, 'ip = ' || SYS_CONTEXT ('USERENV','IP_ADDRESS') ,sysdate);
insert into T values ( S.CURRVAL, 7, 'module = ' || SYS_CONTEXT ('USERENV','MODULE') ,sysdate);
insert into T values ( S.CURRVAL, 8, 'serverhost = ' || SYS_CONTEXT ('USERENV','SERVER_HOST') ,sysdate);


 l_n := ora_sql_txt( l_sql_text );
for i in 1 .. l_n
 LOOP
insert into t values ( s.CURRVAL,8+i, 'l_sql_text(' || i || ') = ' || l_sql_text(i),sysdate );
 end loop;

 end;

1 个答案:

答案 0 :(得分:3)

记录所有服务器错误是一个好主意,我已经看到它在生产环境中运行良好。

理论上,错误记录由应用程序处理。实际上,大多数应用程序不会捕获所有数据库错误。拥有一个包含数据库生成的所有错误的表是很有用的。

但是,这种触发因素有一些特殊的挑战,需要仔细考虑:

  1. 敏感信息-如果查询具有硬编码的社会保险号,而查询失败,则该号码将在错误日志中。确保您的组织对此风险表示满意。不要授予所有人访问该表的权限。 (并且不要认为应用程序绑定变量会避免此问题。最有可能失败的查询是在应用程序外部运行的即席查询,它们将使用硬编码文字。)
  2. 别怪人-错误的数量可能会让您感到惊讶。抵制产生过多错误消息的责怪人。如果您总是对不相关的错误消息感到困扰,则会激怒许多开发人员。 (这似乎很明显,但是我遇到了那么多DBA,他们只是喜欢抱怨任何人都会产生错误。这就是Oracle成为最讨厌的数据库的原因之一。)
  3. 要格外仔细地进行测试-不良的系统事件触发器确实会破坏数据库。 LOGON触发器是最明显的问题,可以有效地破坏整个数据库。在某些方面,AFTER SERVERERROR是更安全的事件,因为某些东西在调用之前已经坏掉了。但是奇怪的事情仍然可能发生。例如,您会注意到exception when others then null;代码。该代码通常是一种反模式,但这是您真正想要抑制所有异常以避免无限循环的少数几个地方之一。
  4. 性能-错误的额外开销无关紧要。如果您的系统必须针对错误进行优化,那么您将遇到更严重的问题。但是您可能需要担心表的大小。如果某个进程每天流氓并且每天发出一百万个无效查询,则您不希望它占用大量空间。 (这就是默认情况下Oracle不会记录每次执行或每个错误的原因。拒绝服务攻击的机会太多。)

示例架构

花一些时间在更好的桌子设计上。忽略问题中使用的表的键值对类型。而是创建一个表,该表将为每个错误存储一行,并使用有意义的名称。像这样的表将更容易有意义地查询:

--drop trigger audit_failed_trg;
--drop table server_errors;
--drop sequence server_error_seq;

create sequence server_error_seq;

create table server_errors
(
    id               number not null,
    error_date       date not null,
    ora_sysevent     varchar2(128),
    ora_login_user   varchar2(128),
    ora_server_error varchar2(4000),
    sid              number,
    host             varchar2(256),
    ip               varchar2(15),
    module           varchar2(4000),
    serverhost       varchar2(256),
    sql              clob,
    constraint server_errors_pk primary key(id)
);

触发

create or replace trigger audit_failed_trg
after servererror on database
declare
    v_sql_text ora_name_list_t;
    v_sql      clob;
    v_n        number;
begin
    v_n := ora_sql_txt(v_sql_text);
    for i in 1 .. v_n loop
        v_sql := v_sql || v_sql_text(i);
    end loop;

    --If you find a huge number of irrelevant errors, you might want to filter them out here.

    insert into server_errors
    values
    (
        server_error_seq.nextval,
        sysdate,
        ora_sysevent,
        ora_login_user,
        ora_server_error(1),
        sys_context ('USERENV','SID'),
        sys_context ('USERENV','HOST'),
        sys_context ('USERENV','IP_ADDRESS'),
        sys_context ('USERENV','MODULE'),
        sys_context ('USERENV','SERVER_HOST'),
        v_sql
    );
    commit;

--Never raise an exception from this trigger.
--No matter what happens we don't want recursive errors.
exception when others then
    null;
end;
/