2012-02-07 13 views
8

すべての例外をOracleパッケージに記録しようとしています。ここで私は手順の最後に持っているものです:これは正常に動作しますOracleパッケージにすべての例外を記録する方法は?

EXCEPTION 
    WHEN OTHERS THEN 
     INSERT INTO VSLogger (MESSAGE) VALUES ('Caught Exception'); 

、しかし、私はまた、エラーコードとメッセージをログに記録します。

EXCEPTION 
    WHEN OTHERS THEN 
     INSERT INTO VSLogger (MESSAGE) VALUES ('Caught Exception: Error ' || SQLCODE || ', Msg: ' || SQLERRM); 

をしかし、これは私にエラーを与える:私が試した

490/7 PL/SQL: SQL Statement ignored 
490/100 PL/SQL: ORA-00984: column not allowed here 

これを行うための正しい方法は何ですか?ありがとう!

答えて

9

SQLERRMを直接使用することはできません。中間変数に割り当てる必要があります。 Oracle 9iを使用すると、それを取り除くことができますが、これは常にdocumented behaviorです。いくつかのサンプルコードについては、hereを参照してください。

このビットを自律型トランザクションでラッピングすることも考えられます。したがって、PL/SQLコードのトランザクションがロールバックされてもログに記録されます。

+0

ありがとうございます!それが働いて.. –

7

ガイウスが短い答えをくれました。自律的なトランザクションにラップすることについての彼のコメントは非常に重要です。あなたのトランザクションがロールバックされた日には、あなたは理由が分からないでしょう。

ここには、いくつかの詳細がスローインされた自律型トランザクションを使用していることが示されています。

あなたの例外ブロックは、このようなものを次のようになります -

exception 
    when exception_pkg.assertion_failure_exception then 
     rollback; 
     raise; 
    when others then 
     rollback; 
     v_code := SQLCODE; 
     v_errm := SUBSTR(SQLERRM, 1, 255); 
     exception_pkg.throw(exception_pkg.unhandled_except, v_code || ' - ' || v_errm || ' ($Header$)'); 

... aaaand、ここにあなたがこれをサポートするために必要なすべてのコードです。それで遊んで、それは、SQLERRMまたはSQLCODE絶対に使用しないでください

-- Create a table to hold the error messages 

CREATE TABLE ERROR_MESSAGES 
(
    ERROR_MESSAGE_ID NUMBER(10)     NOT NULL, 
    ERROR_DATE   TIMESTAMP(6)    DEFAULT SYSDATE    NOT NULL, 
    ERROR_USER   VARCHAR2(30 BYTE)   DEFAULT USER     NOT NULL, 
    MESSAGE_TYPE  VARCHAR2(15 BYTE), 
    PACKAGE_NAME  VARCHAR2(250 BYTE), 
    PROCEDURE_OR_LINE VARCHAR2(30 BYTE), 
    ERROR_CODE   VARCHAR2(10 BYTE), 
    ERROR_MESSAGE1  VARCHAR2(4000 BYTE), 
    ERROR_MESSAGE2  VARCHAR2(4000 BYTE), 
    ERROR_MESSAGE3  VARCHAR2(4000 BYTE), 
    ERROR_MESSAGE4  VARCHAR2(4000 BYTE) 
); 


CREATE UNIQUE INDEX ERROR_MESSAGES_XPK ON ERROR_MESSAGES 
(ERROR_MESSAGE_ID); 

-- Create the sequence used for the ERROR_MESSAGES PK 

CREATE SEQUENCE ERROR_MESSAGE_SEQ 
    START WITH 1; 

-- The package 

CREATE OR REPLACE PACKAGE EXCEPTION_PKG 
as 

    /************************************************************************************ 
    * $Header$ 
    * 
    * Package: exception_pkg 
    * 
    * Purpose: Exception handling functionality 
    * 
    * Authors: M.McAllister (via AskTom - http://tinyurl.com/c43jt) 
    * 
    * Revision History: 
    * 
    * $Log[10]$ 
    ******************************************************************************************/ 

    /*===================================================================== 
    * Constants 
    *=====================================================================*/ 

    c_InfMsg constant error_messages.message_type%type := 'Informational'; 
    c_WarnMsg constant error_messages.message_type%type := 'Warning'; 
    c_ErrMsg constant error_messages.message_type%type := 'Fatal Error'; 
    c_DbgMsg constant error_messages.message_type%type := 'Debug'; 
    c_MaintMsg constant error_messages.message_type%type := 'Maintenance'; 

    /*===================================================================== 
    * Exception Definitions 
    *=====================================================================*/ 

    unhandled_except    constant number := -20001; 
    unhandled_except_exception exception; 
    pragma exception_init(unhandled_except_exception, -20001); 

    bad_parameter     constant number := -20002; 
    bad_parameter_exception  exception; 
    pragma exception_init(bad_parameter_exception, -20002); 

    assertion_failure    constant number := -20003; 
    assertion_failure_exception exception; 
    pragma exception_init(assertion_failure_exception, -20003); 

    /*===================================================================== 
    * Procedures 
    *=====================================================================*/ 

    procedure write_exception_info(p_msg_type error_messages.message_type%type 
           , p_pkg_name error_messages.package_name%type 
           , p_func_name error_messages.procedure_or_line%type 
           , p_error_code error_messages.error_code%type 
           , p_msg1  error_messages.error_message2%type 
           , p_msg2  error_messages.error_message3%type 
           , p_msg3  error_messages.error_message4%type 
           ); 

    procedure who_called_me(p_owner  out varchar2, 
          p_name  out varchar2, 
          p_lineno  out number, 
          p_caller_t out varchar2, 
          p_my_depth in number default 3 
         ); 

    procedure throw(p_exception in number 
        , p_extra_msg in varchar2 default NULL 
       ); 

end exception_pkg; 
/

-- Package Body 

CREATE OR REPLACE PACKAGE BODY EXCEPTION_PKG 
as 

    /************************************************************************************ 
    * $Header$ 
    * 
    * Package: exception_pkg 
    * 
    * Purpose: Exception handling functionality 
    * 
    * Authors: M.McAllister (via AskTom - http://tinyurl.com/c43jt) 
    * 
    * Revision History: 
    * 
    * $Log[10]$ 
    ******************************************************************************************/ 

    /*===================================================================== 
    * Types 
    *=====================================================================*/ 

    type myArray is table of varchar2(255) index by binary_integer; 

    /*===================================================================== 
    * Globals 
    *=====================================================================*/ 

    err_msgs myArray; 

    /*===================================================================== 
    * Procedures 
    *=====================================================================*/ 

    procedure who_called_me(p_owner  out varchar2, 
          p_name  out varchar2, 
          p_lineno  out number, 
          p_caller_t out varchar2, 
          p_my_depth in number default 3 
         ) 
    as 
     call_stack varchar2(4096) default dbms_utility.format_call_stack; 
     n   number; 
     found_stack BOOLEAN default FALSE; 
     line  varchar2(255); 
     cnt   number := 0; 
    begin 

     loop 
      n := instr(call_stack, chr(10)); 
      exit when (cnt = p_my_depth or n is NULL or n = 0); 

      line := substr(call_stack, 1, n-1); 
      call_stack := substr(call_stack, n+1); 

      if (NOT found_stack) then 
       if (line like '%handle%number%name%') then 
        found_stack := TRUE; 
       end if; 
      else 
       cnt := cnt + 1; 
       -- cnt = 1 is ME 
       -- cnt = 2 is MY Caller 
       -- cnt = 3 is Their Caller 
       if (cnt = p_my_depth) then 
        p_lineno := to_number(substr(line, 13, 6)); 
        line := substr(line, 21); 
        if (line like 'pr%') then 
         n := length('procedure '); 
        elsif (line like 'fun%') then 
         n := length('function '); 
        elsif (line like 'package body%') then 
         n := length('package body '); 
        elsif (line like 'pack%') then 
         n := length('package '); 
        elsif (line like 'anonymous%') then 
         n := length('anonymous block '); 
        else 
         n := null; 
        end if; 
        if (n is not null) then 
         p_caller_t := ltrim(rtrim(upper(substr(line, 1, n-1)))); 
        else 
         p_caller_t := 'TRIGGER'; 
        end if; 

        line := substr(line, nvl(n,1)); 
        n := instr(line, '.'); 
        p_owner := ltrim(rtrim(substr(line, 1, n-1))); 
        p_name := ltrim(rtrim(substr(line, n+1))); 
       end if; 
      end if; 
     end loop; 

    end who_called_me; 

    /*===================================================================== 
    * PRIVATE function: get_session_info 
    * purpose: Returns a formatted string containing some information 
    *   about the current session 
    *=====================================================================*/ 

    function get_session_info return varchar2 is 

     l_sessinfo  varchar2(2000); 

    begin 

     select 
     '[SID = ' || sid || '], ' || 
     '[SERIAL# = ' || serial# ||'], ' || 
     '[MACHINE = ' || replace(machine,chr(0),'') || '], ' || 
     '[OSUSER = ' || osuser || '], ' || 
     '[PROGRAM = ' || program || '], ' || 
     '[LOGON_TIME = ' || to_char(logon_time,'mm/dd/yyyy hh:mi:ss') || ']' into l_sessinfo 
     from v$session 
     WHERE audsid = SYS_CONTEXT('userenv','sessionid'); 

     return l_sessinfo; 

    end get_session_info; 

    /*===================================================================== 
    * procedure: write_exception_info 
    * purpose: Call the exception logging routine 
    *=====================================================================*/ 

    procedure write_exception_info(p_msg_type error_messages.message_type%type 
           , p_pkg_name error_messages.package_name%type 
           , p_func_name error_messages.procedure_or_line%type 
           , p_error_code error_messages.error_code%type 
           , p_msg1  error_messages.error_message2%type 
           , p_msg2  error_messages.error_message3%type 
           , p_msg3  error_messages.error_message4%type 
           ) is 

    -- This procedure is autonomous from the calling procedure. 
    -- i.e The calling procedure does not have to be complete 
    -- for this procedure to commit its changes. 
    pragma autonomous_transaction; 
    l_sessinfo  varchar2(2000); 

    begin 

     l_sessinfo := get_session_info; 

     insert into error_messages 
     (error_message_id 
     , error_date 
     , error_user 
     , message_type 
     , package_name 
     , procedure_or_line 
     , error_code 
     , error_message1 
     , error_message2 
     , error_message3 
     , error_message4 
     ) 
     values 
     (error_message_seq.nextval 
     , sysdate 
     , USER 
     , p_msg_type 
     , p_pkg_name 
     , p_func_name 
     , p_error_code 
     , l_sessinfo 
     , p_msg1 
     , p_msg2 
     , p_msg3 
     ); 

     commit; 

    exception 
     when others then 
     -- We don't want an error logging a message to 
     -- cause the application to crash 
     return; 

    end write_exception_info; 

    procedure throw(p_exception in number 
        , p_extra_msg in varchar2 default NULL 
       ) is 

     l_owner  varchar2(30); 
     l_name   varchar2(30); 
     l_type   varchar2(30); 
     l_line   number; 
     l_exception number; 

    begin 

    who_called_me(l_owner, l_name, l_line, l_type); 
    write_exception_info(c_ErrMsg 
         , l_owner || '.' || l_name 
         , 'Line ' || l_line 
         , p_exception 
         , p_extra_msg 
         , NULL 
         , err_msgs(p_exception) 
         ); 
    raise_application_error(p_exception 
          , 'Exception at ' || l_type || ' ' || 
           l_owner || '.' || l_name || '(' || l_line || '). ' || 
           err_msgs(p_exception  ) || '. ' || p_extra_msg 
          , TRUE); 

    exception 
     -- we will get this when we have an invalid exception code, one 
     -- that was not set in the err_msgs array below. The plsql table 
     -- access will raise the NO-DATA-FOUND exception. We'll catch it, 
     -- verify the exception code is in the valid range for raise_application_error 
     -- (if not, set to -20000) and then raise the exception with the message 
     -- "unknown error" 

    when NO_DATA_FOUND then 
     if (p_exception between -20000 and -20999) then 
      l_exception := p_exception; 
     else 
      l_exception := -20000; 
     end if; 

     write_exception_info(c_ErrMsg 
          , l_owner || '.' || l_name 
          , 'Line ' || l_line 
          , p_exception 
          , p_extra_msg 
          , NULL 
          , '**UNKNOWN ERROR**' 
          ); 
     raise_application_error(l_exception 
           , 'Exception at ' || l_type || ' ' || 
            l_owner || '.' || l_name || '(' || l_line || '). ' || 
            '**UNKNOWN ERROR**' || '. ' || p_extra_msg 
           , TRUE); 

    end throw; 

begin 

    -- This code is run once per session when this package is first touched 

    err_msgs(unhandled_except) := 'Unhandled exception'; 
    err_msgs(bad_parameter) := 'Invalid parameter passed into function or procedure'; 
    err_msgs(assertion_failure) := 'Program execution stopped due to assertion failure'; 

end exception_pkg; 
/
+0

恐ろしい!私は明日それを回り乱すだろう。これまでのところ、例外は「データが見つかりませんでした」が分かっていますが、どこから来たのか、何を投げているのか分かりません。 –

+0

埋め込みSQL文がselectを変数セットに代入し、1つのローが返されるのではなく、返されるローはありません。 –

+0

そうですが、この問題は、韻や理由がないため、1日約10回断続的に起こります!同じクエリを使用してページを更新した後、再び動作します。 1週間以上原因を突き止めようとしていました。 –

9

:-)便利です。

常にdbms_utility.format_error_stack||dbms_utility.format_error_backtraceなどを使用してください。

行番号を保存しない例外ログは残酷です。

関連する問題