对于几个失败问题,我编写了这个触发器 -audit_failed_trg
作为数据库发生服务器错误后
触发器。
我的第一个想法是根据需要仅检查特定的异常\用户\表。
但我只是想知道 - 数据库应该在发生任何故障时触发此触发器。
在生产环境中启用它是个好主意吗?
这是否会导致任何性能问题或其他问题?
我使用的是 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;
最佳答案
记录所有服务器错误是一个好主意,我已经看到它在生产环境中运行良好。
理论上,错误日志记录是由应用程序处理的。实际上,大多数应用程序并不能捕获所有数据库错误。拥有一个包含数据库生成的所有错误的表非常有用。
但是,这种触发器具有需要仔细考虑的特殊挑战:
- 敏感信息 - 如果查询有硬编码的社会安全号码,并且查询失败,则该号码将出现在错误日志中。确保您的组织能够承受该风险。不要授予每个人访问该表的权限。 (不要认为应用程序绑定(bind)变量可以避免这个问题。最有可能失败的查询是在应用程序外部运行的临时查询,它们将使用硬编码文字。)
- 不要责怪别人 - 您可能会对错误的数量感到惊讶。抵制责怪人们产生太多错误信息的冲动。如果你总是纠缠不相关的错误消息,你会激怒很多开发人员。 (这似乎是显而易见的,但我遇到过很多 DBA,他们只是喜欢提示任何人生成错误。这就是 Oracle 成为最令人讨厌的数据库的原因之一。)
- 格外仔细地测试 - 构建不当的系统事件触发器确实会毁坏数据库。最明显的问题是 LOGON 触发器,它可以有效地破坏整个数据库。在某些方面,
AFTER SERVERERROR
是一个更安全的事件,因为在调用之前某些东西已经损坏了。但奇怪的事情仍然可能发生。例如,您会注意到exception when others then null;
代码。该代码通常是反模式,但这是您真正想要抑制所有异常以避免无限循环的少数地方之一。 - 性能 - 错误带来的额外开销应该不重要。如果您的系统必须针对错误进行优化,那么您会遇到更严重的问题。但您可能需要担心 table 的大小。如果某个进程失控并在一天内发出一百万个无效查询,您不希望它消耗大量空间。 (这就是为什么默认情况下 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;
/
关于oracle - "after servererror on database trigger"是个好主意吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57026506/