【问题标题】:How to develop an after serverror trigger in Oracle?如何在 Oracle 中开发后服务器错误触发器?
【发布时间】:2010-10-06 21:30:39
【问题描述】:

我正在尝试将数据库中的所有错误记录到一个表中。因此,作为用户 sys,我编写了以下代码:

CREATE TABLE servererror_log (
    error_datetime  TIMESTAMP,
    error_user      VARCHAR2(30),
    db_name         VARCHAR2(9),
    error_stack     VARCHAR2(2000),
    captured_sql    VARCHAR2(1000));
/
CREATE OR REPLACE TRIGGER log_server_errors
AFTER SERVERERROR
ON DATABASE
DECLARE
 captured_sql VARCHAR2(1000);
BEGIN
  SELECT q.sql_text
  INTO captured_sql
  FROM gv$sql q, gv$sql_cursor c, gv$session s
  WHERE s.audsid = audsid
  AND s.prev_sql_addr = q.address
  AND q.address = c.parent_handle;

  INSERT INTO servererror_log
  (error_datetime, error_user, db_name,
   error_stack, captured_sql)
  VALUES
  (systimestamp, sys.login_user, sys.database_name,
  dbms_utility.format_error_stack, captured_sql);
END log_server_errors;

但是当我强制执行错误时,例如尝试从不存在的表中进行选择,它不会在表中记录错误。

有什么方法可以检查触发器是否触发?另外,我尝试创建一个测试表以插入那里,但它也不起作用,即使将触发器定义为自主事务并在触发器内提交。

谢谢, 华金

【问题讨论】:

    标签: oracle plsql triggers error-logging


    【解决方案1】:

    不查询 v$sql;使用 ora_sql_txt 获取语句。

    CREATE OR REPLACE TRIGGER log_server_errors
    AFTER SERVERERROR
    ON DATABASE
    DECLARE
    sql_text ora_name_list_t;
    stmt clob;
    n number;
    BEGIN
      n := ora_sql_txt(sql_text);
      if n > 1000 then n:= 1000; end if ;
      FOR i IN 1..n LOOP
         stmt := stmt || sql_text(i);
       END LOOP;
    
       INSERT INTO servererror_log
       (error_datetime, error_user, db_name,
        error_stack, captured_sql)
       VALUES
       (systimestamp, sys.login_user, sys.database_name,
       dbms_utility.format_error_stack, stmt);
       commit;
     END log_server_errors;
     /
    

    然后:

    SQL> select * from c;
    

    这会产生:

    select * from c
                  *
    ERROR at line 1:
    ORA-00942: table or view does not exist
    

    现在可以查询了:

    select * from servererror_log;
    

    生产:

    ERROR_DATETIME
    ---------------------------------------------------------------------------
    ERROR_USER                     DB_NAME
    ------------------------------ ---------
    ERROR_STACK
    --------------------------------------------------------------------------------
    CAPTURED_SQL
    --------------------------------------------------------------------------------
    11-FEB-09 02.55.35.591259 PM
    SYS                            TS.WORLD
    ORA-00942: table or view does not exist
    select * from c
    

    【讨论】:

    • 它不起作用。我之前也尝试过同样的方法,但没有结果。触发器似乎没有触发。还有其他想法吗?您知道如何检查触发器是否已触发吗?类似于具有 LAST_FIRED_TIME 列的视图或类似的东西。谢谢华金
    • 可能是插入。问题是,如果触发器失败,就很难捕捉到。我要么使用 UTL_FILE 要么使用具有自主事务的单独过程。见stackoverflow.com/questions/492705/…
    【解决方案2】:

    要查看触发器是否正在触发,请向其中添加一行或多行,如下所示:

    DBMS_OUTPUT.PUT_LINE( 'Got this far' );
    

    在 SQLPlus 中,SET SERVEROUTPUT ON 然后执行命令以生成错误。你应该得到这样的输出:

    dev> select * from aldfjh;
    select * from aldfjh
                  *
    ERROR at line 1:
    ORA-00942: table or view does not exist
    
    
    ORA-00942: table or view does not exist
    
    Got this far
    

    【讨论】:

      【解决方案3】:

      检查您的触发器的状态和/或其他触发器的存在:

      select trigger_name, status
      from all_triggers
      where triggering_event like 'ERROR%' 
      

      这应该会导致:

      TRIGGER_NAME        STATUS
      ------------        -------
      LOG_SERVER_ERRORS   ENABLED
      

      如果未启用触发器或另一个触发器失败,则可能无法正常工作。

      【讨论】:

        【解决方案4】:

        另存为ORA-00942.sql:

        -- Drop trigger and ignore errors (e.g., not exists).
        DECLARE
            existential_crisis EXCEPTION;
            PRAGMA EXCEPTION_INIT( existential_crisis, -4080 );
        BEGIN
            EXECUTE IMMEDIATE 'DROP TRIGGER TRG_CATCH_ERRORS /*+ IF EXISTS */';
            EXCEPTION WHEN existential_crisis THEN
            DBMS_OUTPUT.PUT_LINE('Ignoring non-existence.');
        END;
        /
        
        -- Drop table and ignore errors (e.g., not exists).
        DECLARE
            existential_crisis EXCEPTION;
            PRAGMA EXCEPTION_INIT( existential_crisis, -942 );
        BEGIN
            EXECUTE IMMEDIATE 'DROP TABLE TBL_ERROR_LOG /*+ IF EXISTS */';
            EXCEPTION WHEN existential_crisis THEN
            DBMS_OUTPUT.PUT_LINE('Ignoring non-existence.');
        END;
        /
        
        -- Create the table (will not exist due to drop statement).
        CREATE TABLE TBL_ERROR_LOG (
            occurred      timestamp,
            account       varchar2(32),
            database_name varchar2(32),
            stack         clob,
            query         clob
        );
        
        -- Create the trigger to log the errors.
        CREATE TRIGGER TRG_CATCH_ERRORS AFTER servererror ON database
        DECLARE
            sql_text   ora_name_list_t;
            n          number;
            query_     clob;
        BEGIN
            n := ora_sql_txt( sql_text );
        
            IF n > 1000 THEN n := 1000; END IF;
        
            FOR i IN 1 .. n LOOP
                query_ := query_ || sql_text( i );
            END LOOP;
        
            INSERT INTO TBL_ERROR_LOG
                (occurred, account, database_name, stack, query)
            VALUES
                (systimestamp, sys.login_user, sys.database_name,
             dbms_utility.format_error_stack, query_);
        END;
        /
        

        使用 sqlplus 运行:

        SQL> @ORA-00942.sql
        PL/SQL procedure successfully completed.
        
        PL/SQL procedure successfully completed.
        
        Table created.
        
        Trigger created.
        

        测试一下:

        select * from blargh;
        select * from TBL_ERROR_LOG;
        

        输出:

          2017-10-20 15:15:25.061   SCHEMA  XE  "ORA-00942: table or view does not exist"   select * from blargh
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-01-10
          • 2017-06-10
          • 2018-11-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多