一个通用的错误处理器

书上的一个例子:比较好

set serveroutput on size 200000
set linesize 250

-- These 3 procedures illustrate the use of FORMAT_CALL_STACK.
CREATE OR REPLACE PROCEDURE C AS
  v_CallStack VARCHAR2(2000);
BEGIN
  v_CallStack := DBMS_UTILITY.FORMAT_CALL_STACK;
  DBMS_OUTPUT.PUT_LINE(v_CallStack);
END C;
/

CREATE OR REPLACE PROCEDURE B AS
BEGIN
  C;
END B;
/

CREATE OR REPLACE PROCEDURE A AS
BEGIN
  B;
END A;
/

exec A;

DROP TABLE errors;
CREATE TABLE errors (
  module       VARCHAR2(50),
  seq_number   NUMBER,
  error_number NUMBER,
  error_mesg   VARCHAR2(100),
  error_stack  VARCHAR2(2000),
  call_stack   VARCHAR2(2000),
  timestamp    DATE,
  PRIMARY KEY (module, seq_number));

DROP TABLE call_stacks;
CREATE TABLE call_stacks (
  module        VARCHAR2(50),
  seq_number    NUMBER,
  call_order    NUMBER,
  object_handle VARCHAR2(10),
  line_num      NUMBER,
  object_name   VARCHAR2(80),
  PRIMARY KEY (module, seq_number, call_order),
  FOREIGN KEY (module, seq_number) REFERENCES errors ON DELETE CASCADE);

DROP TABLE error_stacks;
CREATE TABLE error_stacks (
  module        VARCHAR2(50),
  seq_number    NUMBER,
  error_order   NUMBER,
  facility      CHAR(3),
  error_number  NUMBER(5),
  error_mesg    VARCHAR2(100),
  PRIMARY KEY (module, seq_number, error_order),
  FOREIGN KEY (module, seq_number) REFERENCES errors ON DELETE CASCADE);

DROP SEQUENCE error_seq;
CREATE SEQUENCE error_seq
  START WITH 1
  INCREMENT BY 1;

/* Generic error-handling package, using
 DBMS_UTILITY.FORMAT_ERROR_STACK and DBMS_UTILITY.FORMAT_CALL_STACK. 
 This package will store general error information in the errors
 table, with detailed call stack and error stack information in the
 call_stacks and error_stacks tables, respectively. */
CREATE OR REPLACE PACKAGE ErrorPkg AS

  -- Entry point for handling errors.  HandleAll should be called
  -- from all exception handlers where you want the error to be
  -- logged.  p_Top should be TRUE only at the topmost level of
  -- procedure nesting.  It should be FALSE at other levels.
  PROCEDURE HandleAll(p_Top BOOLEAN);

  -- Prints the error and call stacks (using DBMS_OUTPUT) for the
  -- given module and sequence number.
  PROCEDURE PrintStacks(p_Module IN errors.module%TYPE,
                        p_SeqNum IN errors.seq_number%TYPE);

  -- Unwinds the call and error stacks, and stores them in the errors
  -- and call_stacks tables.  Returns the sequence number under which
  -- the error is stored.
  -- If p_CommitFlag is TRUE, then the inserts are committed.
  -- In order to use StoreStacks, an error must have been handled. 
  -- Thus HandleAll should have been called with p_Top = TRUE.
  PROCEDURE StoreStacks(p_Module IN errors.module%TYPE,
                        p_SeqNum OUT errors.seq_number%TYPE,
                        p_CommitFlag BOOLEAN DEFAULT FALSE);
END ErrorPkg;
/
show errors

CREATE OR REPLACE PACKAGE BODY ErrorPkg AS

  v_NewLine     CONSTANT CHAR(1) := CHR(10);

  v_Handled     BOOLEAN := FALSE;
  v_ErrorStack  VARCHAR2(2000);
  v_CallStack   VARCHAR2(2000);

  PROCEDURE HandleAll(p_Top BOOLEAN) IS
  BEGIN
    IF p_Top THEN
      v_Handled := FALSE;
    ELSIF NOT v_Handled THEN
      v_Handled := TRUE;
      v_ErrorStack := DBMS_UTILITY.FORMAT_ERROR_STACK;
      v_CallStack := DBMS_UTILITY.FORMAT_CALL_STACK;
    END IF;      
  END HandleAll;

  PROCEDURE PrintStacks(p_Module IN errors.module%TYPE,
                        p_SeqNum IN errors.seq_number%TYPE) IS
    v_TimeStamp errors.timestamp%TYPE;
    v_ErrorMsg  errors.error_mesg%TYPE;

    CURSOR c_CallCur IS
      SELECT object_handle, line_num, object_name
        FROM call_stacks
        WHERE module = p_Module
        AND seq_number = p_SeqNum
        ORDER BY call_order;

    CURSOR c_ErrorCur IS
      SELECT facility, error_number, error_mesg
        FROM error_stacks
        WHERE module = p_Module
        AND seq_number = p_SeqNum
        ORDER BY error_order;
  BEGIN
    SELECT timestamp, error_mesg
      INTO v_TimeStamp, v_ErrorMsg
      FROM errors
      WHERE module = p_Module
      AND seq_number = p_SeqNum;

    -- Output general error information.
    DBMS_OUTPUT.PUT(TO_CHAR(v_TimeStamp, 'DD-MON-YY HH24:MI:SS'));
    DBMS_OUTPUT.PUT('  Module: ' || p_Module);
    DBMS_OUTPUT.PUT('  Error #' || p_SeqNum || ':  ');
    DBMS_OUTPUT.PUT_LINE(v_ErrorMsg);

    -- Output the call stack.
    DBMS_OUTPUT.PUT_LINE('Complete Call Stack:');
    DBMS_OUTPUT.PUT_LINE('  Object Handle  Line Number  Object Name');
    DBMS_OUTPUT.PUT_LINE('  -------------  -----------  -----------');
    FOR v_CallRec in c_CallCur LOOP
      DBMS_OUTPUT.PUT(RPAD('  ' || v_CallRec.object_handle, 15));
      DBMS_OUTPUT.PUT(RPAD('  ' || TO_CHAR(v_CallRec.line_num), 13));
      DBMS_OUTPUT.PUT_LINE('  ' || v_CallRec.object_name);
    END LOOP;

    -- Output the error stack.
    DBMS_OUTPUT.PUT_LINE('Complete Error Stack:');
    FOR v_ErrorRec in c_ErrorCur LOOP
      DBMS_OUTPUT.PUT('  ' || v_ErrorRec.facility || '-');
      DBMS_OUTPUT.PUT(TO_CHAR(v_ErrorRec.error_number) || ': ');
      DBMS_OUTPUT.PUT_LINE(v_ErrorRec.error_mesg);
    END LOOP;
    
  END PrintStacks;

  PROCEDURE StoreStacks(p_Module IN errors.module%TYPE,
                        p_SeqNum OUT errors.seq_number%TYPE,
                        p_CommitFlag BOOLEAN DEFAULT FALSE) IS
    v_SeqNum     NUMBER;

    v_Index      NUMBER;
    v_Length     NUMBER;
    v_End        NUMBER;

    v_Call       VARCHAR2(100);
    v_CallOrder  NUMBER := 1;
    v_Handle     call_stacks.object_handle%TYPE;
    v_LineNum    call_stacks.line_num%TYPE;
    v_ObjectName call_stacks.object_name%TYPE;

    v_Error      VARCHAR2(120);
    v_ErrorOrder NUMBER := 1;
    v_Facility   error_stacks.facility%TYPE;
    v_ErrNum     error_stacks.error_number%TYPE;
    v_ErrMsg     error_stacks.error_mesg%TYPE;

    v_FirstErrNum errors.error_number%TYPE;
    v_FirstErrMsg errors.error_mesg%TYPE;
  BEGIN
    -- First get the error sequence number.
    SELECT error_seq.nextval
      INTO v_SeqNum
      FROM dual;

    p_SeqNum := v_SeqNum;

    -- Insert the first part of the header information into the
    -- errors table.
    INSERT INTO errors
      (module, seq_number, error_stack, call_stack, timestamp)
    VALUES
      (p_Module, v_SeqNum, v_ErrorStack, v_CallStack, SYSDATE);

    -- Unwind the error stack to get each error out.  We do this by
    -- scanning the error stack string.  Start with the index at the
    -- beginning of the string.
    v_Index := 1;
 
    -- Loop through the string, finding each newline.  A newline ends
    -- each error on the stack.
    WHILE v_Index <  LENGTH(v_ErrorStack) LOOP
      -- v_End is the position of the newline.
      v_End := INSTR(v_ErrorStack, v_NewLine, v_Index);

      -- Thus, the error is between the current index and the
      -- newline.
      v_Error := SUBSTR(v_ErrorStack, v_Index, v_End - v_Index);

      -- Skip over the current error, for the next iteration.
      v_Index := v_Index + LENGTH(v_Error) + 1;

      -- An error looks like 'facility-number: mesg'.  We need to get
      -- each piece out for insertion.

      -- First, the facility is the first 3 characters of the error.
      v_Facility := SUBSTR(v_Error, 1, 3); 

      -- Remove the facility and the dash (always 4 characters).
      v_Error := SUBSTR(v_Error, 5);

      -- Now we can get the error number.
      v_ErrNum :=
        TO_NUMBER(SUBSTR(v_Error, 1, INSTR(v_Error, ':') - 1));

      -- Remove the error number, colon and space (always 7
      -- characters).
      v_Error := SUBSTR(v_Error, 8);

      -- What's left is the error message.
      v_ErrMsg := v_Error;

      -- Insert the errors, and grab the first error number and
      -- message while we're at it.
      INSERT INTO error_stacks
        (module, seq_number, error_order, facility, error_number,
         error_mesg)
      VALUES
        (p_Module, p_SeqNum, v_ErrorOrder, v_Facility, v_ErrNum, v_ErrMsg);

      IF v_ErrorOrder = 1 THEN
        v_FirstErrNum := v_ErrNum;
        v_FirstErrMsg := v_Facility || '-' || TO_NUMBER(v_ErrNum) ||
                         ': ' || v_ErrMsg;
      END IF;

      v_ErrorOrder := v_ErrorOrder + 1;
    END LOOP;
 
    -- Update the errors table with the message and code.
    UPDATE errors
      SET error_number = v_FirstErrNum,
          error_mesg = v_FirstErrMsg
      WHERE module = p_Module
      AND seq_number = v_SeqNum;

    -- Now we need to unwind the call stack, to get each call out.
    -- We do this by scanning the call stack string.  Start with the
    -- index after the first call on the stack.  This will be after
    -- the first occurrence of 'name' and the newline.
    v_Index := INSTR(v_CallStack, 'name') + 5;

    -- Loop through the string, finding each newline.  A newline ends
    -- each call on the stack.
    WHILE v_Index <  LENGTH(v_CallStack) LOOP
      -- v_End is the position of the newline.
      v_End := INSTR(v_CallStack, v_NewLine, v_Index);

      -- Thus, the call is between the current index and the newline.
      v_Call := SUBSTR(v_CallStack, v_Index, v_End - v_Index);

      -- Skip over the current call, for the next iteration.
      v_Index := v_Index + LENGTH(v_Call) + 1;

      -- Within a call, we have the object handle, then the line
      -- number, then the object name, separated by spaces.  We need
      -- to separate them out for insertion.

      -- Trim white space from the call first.
      v_Call := LTRIM(v_Call);

      -- First get the object handle.
      v_Handle := SUBSTR(v_Call, 1, INSTR(v_Call, ' '));

      -- Now, remove the object handle, then the white space from
      -- the call.
      v_Call := SUBSTR(v_Call, LENGTH(v_Handle) + 1);
      v_Call := LTRIM(v_Call);

      -- Now we can get the line number.
      v_LineNum := TO_NUMBER(SUBSTR(v_Call, 1, INSTR(v_Call, ' ')));

      -- Remove the line number, and white space.
      v_Call := SUBSTR(v_Call, LENGTH(v_LineNum) + 1);
      v_Call := LTRIM(v_Call);

      -- What is left is the object name.
      v_ObjectName := v_Call;

      -- Insert all calls except the call for ErrorPkg.
      IF v_CallOrder > 1 THEN
        INSERT INTO call_stacks
          (module, seq_number, call_order, object_handle, line_num, 
           object_name)
        VALUES
          (p_Module, v_SeqNum, v_CallOrder, v_Handle, v_LineNum, 
           v_ObjectName);
      END IF;

      v_Callorder := v_CallOrder + 1;

    END LOOP;

    IF p_CommitFlag THEN 
      COMMIT;
    END IF;
  END StoreStacks;

END ErrorPkg;
/
show errors


CREATE OR REPLACE TRIGGER temp_insert
  BEFORE INSERT ON temp_table
BEGIN
  RAISE ZERO_DIVIDE;
END ttt_insert;
/

CREATE OR REPLACE PROCEDURE C AS
BEGIN
  INSERT INTO temp_table (num_col) VALUES (7);
EXCEPTION
  WHEN OTHERS THEN
    ErrorPkg.HandleAll(FALSE);
    RAISE;
END C;
/

CREATE OR REPLACE PROCEDURE B AS
BEGIN
  C;
EXCEPTION
  WHEN OTHERS THEN
    ErrorPkg.HandleAll(FALSE);
    RAISE;
END B;
/

CREATE OR REPLACE PROCEDURE A AS
  v_ErrorSeq NUMBER;
BEGIN
  B;
EXCEPTION
  WHEN OTHERS THEN
    ErrorPkg.HandleAll(TRUE);
    ErrorPkg.StoreStacks('Error Test', v_ErrorSeq, TRUE);
    ErrorPkg.PrintStacks('Error Test', v_ErrorSeq);
END A;
/


SET SERVEROUTPUT ON SIZE 1000000 FORMAT TRUNCATED
exec A;

猜你喜欢

转载自aigo-h.iteye.com/blog/1917536