我正在尝试记录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
这样做的正确方法是什么?谢谢!
答案 0 :(得分:13)
切勿使用SQLERRM
或SQLCODE
。
始终使用dbms_utility.format_error_stack||dbms_utility.format_error_backtrace
或类似内容。
不存储行号的异常记录只是残忍。
答案 1 :(得分:10)
您不能直接使用SQLERRM
- 您必须将其分配给中间变量。请注意,Oracle 9i可以让你逃脱它,但这始终是documented behavior。有关示例代码,请参阅here。
您还可以考虑将此位包装在自治事务中,因此即使您的PL / SQL代码的事务被回滚,它也会被记录。
答案 2 :(得分:9)
Gaius给了你简短的回答。他关于将其包装成自主交易的评论非常重要。当你的交易被回滚的那一天,你会觉得懊恼,而你却不知道为什么。
这里显示的是使用一个自治事务,其中包含一些额外的细节,因此您可以更多地了解错误发生的位置。
你的异常块变成这样: -
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,这是支持此功能所需的所有代码。玩弄它,它很有用: - )
-- 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;
/