Oracle full error message in log - oracle

I'm trying to get the full error message from oracle.
For example - I have a very long procedure that doing a lot of manipulation on
a lot of objects, and in my log I got the error
object no longer exist.
And this is my insert to the log (even it is a generally question - not specific to this example):
EXCEPTION WHEN OTHERS THEN
v_errno := sqlcode;
V_ERRMSG := SQLERRM;
INSERT INTO ERR_TABLE (ERROR_NUMBER, ERROR_MESSAGE,PROGRAM#)
VALUES (V_ERRNO, V_ERRMSG,'MY_PKG');
COMMIT;
The problem is that I don't know which table it talking about - because this
information doesn't exsits.
Is there a way to get it?
I guess that oracle save it in some place.
thanks!

For internal logging (not only for errors) I use a procedure like this:
PROCEDURE Put(
LogMessage IN T_LOG_ENTRIES.LOG_MESSAGE%TYPE,
ErrCode IN T_LOG_ENTRIES.LOG_ERROR_CODE%TYPE DEFAULT 0) IS
ErrorStack T_LOG_ENTRIES.LOG_ERROR_STACK%TYPE;
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
IF ErrCode <> 0 THEN
ErrorStack := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE();
ErrorStack := SQLERRM(ErrCode) || CHR(13) || ErrorStack;
END IF;
INSERT INTO T_LOG_ENTRIES
(LOG_DATE, LOG_MESSAGE, LOG_ERROR_CODE, LOG_ERROR_STACK)
VALUES
(CURRENT_TIMESTAMP, LogMessage, ErrCode, ErrorStack);
COMMIT;
END Put;
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() provides the full error stack. You should use AUTONOMOUS_TRANSACTION since transactions are rolled back in case of exception, i.e. your log message would be deleted also.
Then you can use the procedure for example as this:
BEGIN
...
EXCEPTION WHEN OTHERS THEN
Put('Error in my procedure', sqlcode);
END;

Related

how to insert the result of dbms_output.put_line to a table for error catch?

Exception
WHEN OTHERS THEN
--dbms_output.put_line('pl_update_sidm_user r: ERROR CODE:' || sqlcode || '~' ||
--sqlerrm || ' EMPL_NBR:' || r.EMPL_NBR);
insert into ERROR_MSG (ERROR_MSG_ID,ERROR_MSG) values (ERROR_MSG_ID_SEQ.NEXTVAL, 'pl_update_sidm_user_duty_role r2');
END;
I would like to put the error result to a table.
However, how can I do that?
Can I put the result of dbms_output to a table as a string?
If not, can I get the sqlcode,sqlerrm without using dbms_output?
Thank you !!
From the documentation,
A SQL statement cannot invoke SQLCODE or SQLERRM. To use their values
in a SQL statement, assign them to local variables first
Also,
Oracle recommends using DBMS_UTILITY.FORMAT_ERROR_STACK except when using the FORALL statement with its SAVE EXCEPTIONS clause
So, for SQLCODE or SQLERRM, you should assign them into variables and use them.
DECLARE
v_errcode NUMBER;
v_errmsg VARCHAR2(1000);
BEGIN
--some other statements that may raise exception.
EXCEPTION
WHEN OTHERS THEN
v_errcode := SQLCODE;
v_errmsg := SQLERRM;
insert into ERROR_TABLE (ERROR_MSG_ID,ERROR_MSG) --change your table name
values (ERROR_MSG_ID_SEQ.NEXTVAL,
v_errcode||':'||v_errmsg);
END;
/
Preferably use insert like this instead, as per Oracle's recommendation.
insert into ERROR_TABLE (ERROR_MSG_ID,ERROR_MSG) values (ERROR_MSG_ID_SEQ.NEXTVAL,
DBMS_UTILITY.FORMAT_ERROR_STACK);
Demo
Technically what others are suggesting is correct: the "insert" operation executed in the "exception when others" block will actually insert a new row in the log table.
the problem is that such insert statement will be part of the same transaction of the main procedure and, since you had an error while executing it, you are very likely to rollback that transaction, and this will rollback also the insert in your log table
I suppose the problem you are facing is not that you aren't successfully logging the error message: it is that you are rolling it back immediately afterwards, along with all the other writes you did in the same transaction.
Oracle gives you a way of executing code in a SEPARATE transaction, by using "autonomous transaction" procedures.
you need to create such a procedure:
create or replace procedure Write_Error_log(
arg_error_code number,
arg_error_msg varchar2,
arg_error_backtrace varchar2) is
PRAGMA AUTONOMOUS_TRANSACTION;
begin
INSERT INTO error_msg (
error_msg_id,
error_code,
error_msg,
error_stack)
VALUES (
error_msg_id_seq.NEXTVAL,
arg_error_code,
arg_error_msg,
arg_error_backtrace);
commit; -- you have to commit or rollback always, before exiting a
-- pragma autonomous_transaction procedure
end;
What this procedure does is to write a new record in the log table using a totally separate and independent transaction: the data will stay in the log table even if you execute a roll back in your calling procedure. You can also use such a procedure to create a generic log (not only errors).
All you have to do now is to call the procedure above whenever you need to log something, so your code becomes:
DECLARE
v_errcode NUMBER;
v_errmsg VARCHAR2(1000);
BEGIN
--some other statements that may raise exception.
EXCEPTION WHEN OTHERS THEN
Write_Error_log(SQLCODE, SQLERRM, dbms_utility.format_error_backtrace);
END;
/
P.S: there might be some typos in my code: I can't test it right now since I can't reach an oracle server in this moment.

unable to call exception block in oracle

I have trying to execute below pl sql block in my oracle developer edition.I have made calls to functions and procedures and it works fines.But i am not able to call exception in case my query does not get executed.I have been trying to get a wrong query exectued by passing a string value instead of int value.So it throws error but also i need to get exception block to executed in case of such error.Block 2 should through exception as i am passing string value.But exception block does not get call,Any help?? Below is my block
DECLARE
DBCID INT := 102;
CNT INT;
BEGIN
SELECT DEVOPS_ISDBCEXECUTED(DBCID, 'DDL') INTO CNT FROM DUAL;
IF (CNT = 0) THEN
BEGIN
DEVOPS_DBCINSERT (DBCID,'DDL','hsolanki','Prj1','Item1','avarne');
BEGIN
DECLARE W_CNT int;
BEGIN
SELECT COUNT(*) INTO W_CNT FROM HS WHERE NAM = 'DK'; //block 1
IF (W_CNT = 0) THEN
INSERT INTO HS
(NAM, AGE)
VALUES ('Dk',8);
END IF;
END;
END;
BEGIN
DECLARE W_CNT int;
BEGIN
SELECT COUNT(*) INTO W_CNT FROM HS WHERE NAM = 'Ab';
IF (W_CNT = 0) THEN
INSERT INTO HS
(NAM, AGE) //block 2
VALUES ('Ab',s);
END IF;
END;
END;
DEVOPS_DBCUPDATE(DBCID, NULL,'SUCCESS');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('ERROR OCCURED : ' || sqlerrm);
DEVOPS_DBCUPDATE (DBCID,sqlerrm,'Failed');
rollback;
END;
END IF;
END;
Your exception handling block is within the 'IF (CNT = 0) THEN .. END IF' block. If you pass a string value, most probably the exception was thrown at the first function call ( SELECT DEVOPS_ISDBCEXECUTED...), which is not 'protected' by an excpetion handler. You would need to move the exception handler to the outermost block, e.g.:
DECLARE
DBCID INT := 102;
CNT INT;
BEGIN
....
EXCEPTION
WHEN OTHERS THEN
...
END;
So we know ...
DEVOPS_DBCUPDATE is a procedure which updates a table
... and ...
IN exception i am calling DEVOPS_DBCUPDATE ... my table is not getting updated
... and ...
i dont know what is pragma autonomous_transaction
Putting these clues altogether we can see that the rollback in the EXCEPTION block will wipe out the change to the table executed by the preceding call to DEVOPS_DBCUPDATE(), so it only seems as though the EXCEPTION block is not being executed ( a check on whether the DBMS_OUTPUT message is displayed would confirm that it is being called).
Anyway the solution is to make DEVOPS_DBCUPDATE() run in a nested transaction, so the change is applied regardless of what happens in the wider transaction. We do this with the autonomous_transaction pragma.
Obviously I don't know the exact structure of your code, but it will look something like this:
create or replace procedure DEVOPS_DBCUPDATE( ... ) is
pragma autonomous_transaction;
begin
update your_table
set ....
commit;
end;
The COMMIT in the procedure will persist the change to your table but will not save anything in the outer transaction. So the rollback in the EXCEPTION block would still reverse the inserts into the HS table.
Autonomous transactions are very useful when employed properly, but it is easy to misuse them. This scenario - persistent logging in the event of exception or rollback - is their main use case. But generally, use with caution: it's easy to abuse autonomous transactions and end up with a corrupted database. There's more information in the Oracle documentation.
An error raise in the DECLARE section is not handled by that block's EXCEPTION section. For this reason it is often safer to initialise variables after the BEGIN i.e.
DECLARE
DBCID INT;
CNT INT;
BEGIN
DBCID := 'xxx';
...
EXCEPTION
WHEN OTHERS THEN
... -- The assignment will be caught here
END;

Saving a PL/SQL exception and raising it later?

I have a PL/SQL procedure (in an Oracle 12c database) that tries to insert some data. If that fails, it should check a table to see if it can find some info there to help solve the problem. If it finds the information everything is fine, if not it should reraise the error.
This is my code:
BEGIN
-- Try to insert some data.
INSERT INTO table VALUES x;
EXCEPTION
WHEN OTHERS THEN
BEGIN
-- Check a table to fins some info to help solve the problem.
-- If we find a row here, we can fix it.
-- If not, we should reraise the error.
SELECT * INTO y FROM table WHERE a = b;
-- Do some more stuff here to fix the problem.
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- We could not find anything in the table,
-- so we could not handle the situation.
-- Reraise the error.
RAISE;
END;
END;
The problem here is that the RAISE; statement raises the latest exception, which is the NO_DATA_FOUND that the SELECT statement threw. The original exception from the INSERT is further down in the stack, but not at the top.
Can I somehow "save" the error from the INSERT and reraise it? Or can I run a SELECT INTO that does not throw an error if it finds nothing? My goal here is to reraise the original INSERT exception without having any traces of the NO_DATA_FOUND exception on it.
EDIT: See comments as to why this is not a duplicate.
Pull the raise statement out of the block trying to fix the problem. In case you need to reraise contingent to failure of the fixing, set a flag in the inner exception handler and only execute raise when that flag is true.
As code:
DECLARE
b_reraise BOOLEAN := FALSE;
BEGIN
-- Try to insert some data.
INSERT INTO table VALUES x;
EXCEPTION
WHEN OTHERS THEN
BEGIN
-- Check a table to fins some info to help solve the problem.
-- If we find a row here, we can fix it.
-- If not, we should reraise the error.
SELECT * INTO y FROM table WHERE a = b;
-- Do some more stuff here to fix the problem.
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- We could not find anything in the table,
-- so we could not handle the situation.
-- Reraise the error.
b_reraise := TRUE;
END;
IF b_reraise THEN
RAISE;
END IF;
END;
May be something like this:
DECLARE
l_sqlerrm VARCHAR2(4000);
l_sqlerrc NUMBER;
l_exc EXCEPTION;
BEGIN
-- some code with errors
EXCEPTION
WHEN OTHERS THEN
l_sqlerrm := SQLERRM;
l_sqlerrc := SQLCODE;
-- loggin
INSERT INTO my_log (code, text) VALUES (l_sqlerrc, l_sqlerrm);
COMMIT;
-- some your code "Check a table to fins some info to help solve the problem"
-- some code to SELECT code INTO l_sqlerrc FROM my_log
PRAGMA exception_init(l_exc, l_sqlerrc);
RAISE l_exc;
END;

When to re-raise the same exception in Oracle

I'm reading Steven Feuerstein's PL/SQL book. According to it:
Use this form when you want to re-raise (or propagate out) the same
exception from within an exception handler, as you see here:
EXCEPTION
WHEN NO_DATA_FOUND
THEN
-- Use common package to record all the "context" information,
-- such as error code, program name, etc.
errlog.putline (company_id_in);
-- And now propagate NO_DATA_FOUND unhandled to the enclosing block.
RAISE;
This feature is useful when you want to log the fact that an error
occurred, but then pass that same error out to the enclosing block.
That way, you record where the error occurred in your application but
still stop the enclosing block(s) without losing the error
information.
I give it a try:
create table log_error
(
error_code number,
error_name varchar2(400)
);
declare
l_q number := 400;
l_r number := 0;
l_result number;
err_num NUMBER;
err_msg VARCHAR2(100);
begin
l_result := l_q/l_r;
EXCEPTION
WHEN OTHERS THEN
err_num := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 100);
insert into log_error values (err_num , err_msg);
end;
select * from log_error;
I got below data in my log_error table:
-1476 ORA-01476: divisor is equal to zero
Now I place raise in my exception block:
declare
l_q number := 400;
l_r number := 0;
l_result number;
err_num NUMBER;
err_msg VARCHAR2(100);
begin
l_result := l_q/l_r;
EXCEPTION
WHEN OTHERS THEN
err_num := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 100);
insert into log_error values (err_num , err_msg);
raise;
end;
Now when I run this block, i got nothing in my log table and also I got the error.
Error report -
ORA-01476: divisor is equal to zero
ORA-06512: at line 14
01476. 00000 - "divisor is equal to zero"
What is the use of raise? When I have to use this?
Your logging process needs to be a little different. When an exception is called all data/transactions that are not committed are rolled back. You can add more details with the newer features Oracle has added.
err_msg := DBMS_UTILITY.FORMAT_ERROR_STACK()||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE();
You need to create a logging procedure which uses the PRAGMA AUTONOMOUS TRANSACTION. Pass in the SQLCODE and err_msg with the details and this will log the error no matter what. Here is what I use which also uses Feurstein's Q Error package. This link to the Q$Error package is quite informative.
PROCEDURE LOG (err_in IN INTEGER:= SQLCODE,
msg_in IN VARCHAR2:= NULL,
vlocation_in IN VARCHAR2:= NULL)
IS
/******************************************************************************
PURPOSE: log a code error, business logic error or information message in APPLICATION_ERROR_LOGGING
we want the error message to be logged even if the calling transaction fails or hangs
******************************************************************************/
PRAGMA AUTONOMOUS_TRANSACTION;
v_err_text VARCHAR2 (4000) := SQLERRM;
BEGIN
v_err_text := v_err_text || ' ' || GET_MORE_ERROR_DESCRIPTION (err_in);
INSERT INTO application_error_logging (ID,
request_uri,
ERROR_CODE,
user_id,
stack_trace,
information,
"TIMESTAMP")
VALUES (application_error_logging_seq.NEXTVAL,
vlocation_in,
TO_CHAR (err_in),
g_admin_id,
msg_in,
v_err_text,
localtimestamp);
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
q$error_manager.raise_error (
error_code_in => SQLCODE,
text_in => SQLERRM,
name1_in => 'LOCATION',
value1_in => 'APP_UTIL.LOG',
name2_in => 'v_location',
value2_in => vlocation_in,
name3_in => 'err_in',
value3_in => TO_CHAR (err_in)
);
END LOG;
Apparently, Steven Feuerstein's procedure errlog.putline() uses an autonomous transaction to insert the record into the log table. In your case you perform the insert in the same transaction, which is rolled back by the caller when the exception is re-raised.

Customize PL/SQL exceptions in Oracle

Frequently I found myself doing some functions to insert/delete/update in one or more tables and I've seen some expected exceptions been taken care of, like no_data_found, dupl_val_on_index, etc. For an insert like this:
create or replace FUNCTION "INSERT_PRODUCTS" (
a_supplier_id IN FORNECEDOR.ID_FORNECEDOR%TYPE,
a_prodArray IN OUT PRODTABLE
)
RETURN NUMBER IS
v_error_code NUMBER;
v_error_message VARCHAR2(255);
v_result NUMBER:= 0;
v_prod_id PRODUTO.ID_PROD%TYPE;
v_supplier FORNECEDOR%ROWTYPE;
v_prodInserted PROD_OBJ;
newList prodtable := prodtable();
BEGIN
SELECT FORNEC_OBJ(ID_FORNECEDOR,NOME_FORNECEDOR,MORADA,ARMAZEM,EMAIL,TLF,TLM,FAX) into v_supplier from fornecedor where id_fornecedor = a_supplier_id;
FOR i IN a_prodArray.FIRST .. a_prodArray.LAST LOOP
INSERT INTO PRODUTO (PRODUTO.ID_PROD,PRODUTO.NOME_PROD,PRODUTO.PREC_COMPRA_PROD,PRODUTO.IVA_PROD,PRODUTO.PREC_VENDA_PROD,PRODUTO.QTD_STOCK_PROD,PRODUTO.QTD_STOCK_MIN_PROD)
VALUES (S_PRODUTO.nextval,a_prodArray(i).NOME_PROD,a_prodArray(i).PREC_COMPRA_PROD,a_prodArray(i).IVA_PROD,NULL,NULL,NULL);
/* If the above insert didn't failed, we can insert in weak entity PROD_FORNECIDO. */
SELECT ID_PROD into v_prod_id from PRODUTO where NOME_PROD = a_prodArray(i).NOME_PROD;
INSERT INTO PROD_FORNECIDO VALUES (a_supplier_id, v_prod_id,a_prodArray(i).PREC_COMPRA_PROD);
SELECT PROD_OBJ(ID_PROD,NOME_PROD,PREC_COMPRA_PROD,PREC_VENDA_PROD,QTD_STOCK_PROD,QTD_STOCK_MIN_PROD,IVA_PROD) into v_prodInserted from PRODUTO where ID_PROD= v_prod_id;
a_prodarray(i).ID_PROD := v_prod_id;
END LOOP;
INSERT INTO FORNECPRODS VALUES (a_supplier_id,v_supplier, a_prodarray);
v_result:= 1;
RETURN v_result;
COMMIT;
Exception
When no_data_found then
v_error_code := 0;
v_error_message:= 'Insert Products: One of selects returned nothing';
Insert Into errors Values (v_error_code,v_error_message, systimestamp);
RETURN v_result;
When others Then
ROLLBACK;
v_error_code := SQLCODE;
v_error_message:=substr(SQLERRM,1,50);
Insert Into errors Values (v_error_code,'Error inserting products list',systimestamp);
RETURN v_result;
END;
I would like to customize more of my exceptions or do an exception block for each select/insert. Is that possible or correct?
If so, could please show me some code with important exceptions being throwed by this function?
If you just want to substitute your own error message, there is RAISE_APPLICATION_ERROR...
When no_data_found then
RAISE_APPLICATION_ERROR(-20000
, 'Insert Products: One of selects returned nothing';
, true);
The third parameter returns the original error as well as your custom one.
Oracle also gives us the option to define our exceptions. This can be useful if we want to pass the exception to a calling program...
Declare
no_product_found exception;
Begin
....
When no_data_found then
raise no_product_found;
This would be most effective if we defined the NO_PRODUCT_FOUND exception in a package specification where it could be referenced by external program units.
In addition, Oracle provides the INIT_EXCEPTION pragma which allows us to associate Oracle error numbers with our custom exceptions. Unfortunately we cannot overload error numbers which Oracle has already defined (for instance, we cannot create our own exceptions for ORA-1403 which is already covered by the NO_DATA_FOUND exception). Find out more.
In the exception section; you can raise application error or return 0 with error code explanation. It is about your choice.
If you want to log your errors in the exception section (or in main section), write your own logging procedure with AUTONOMOUS TRANSACTION. so, your logging mechanism is not affected by your main transaction's COMMIT or ROLLBACK. (see: http://www.dba-oracle.com/t_autonomous_transaction.htm)
Another logging mechanism (DML Error Log) in Oracle 10gR2 (and above) is LOG ERRORS clause (see: http://www.oracle-base.com/articles/10g/DmlErrorLogging_10gR2.php).

Resources