Level based Logging in Oracle - oracle

I am Kanagaraj. In our stored procedure, we are logging messages at 500 places and logs are stored in a table where we are having some performance issues. We need to split these messages into Debug, Info and Error messages. Based on the level, only limited messages should be logged. If necessary, we will be enabling the next level and see the more logs. What could be the effective way for introducing this level based logging in our procedure?.
Thanks in advance.
Kanagaraj.

Something like this...
create or replace package logger as
min_level number := 2;
procedure ins_log(level number, message varchar2);
end;
create or replace package body logger as
procedure ins_log(level number, message varchar2) is
pragma autonomous_transaction;
begin
if level>=min_level then
insert into loggin(ts, msg) values (sysdate, message);
end if;
commit; // autonomous_transaction requires that
end;
end;
EDIT: Added pragma autonomous_transaction;, thanks Adam

There is a port of log4j for Oracle PL/SQL that can be found on sourceforge. This allows logging to be enabled/disabled at various levels, and for specific packages/functions/procedures simply be modifying a configuration. It also supports redirection to different destinations.

A bit late; I recommend you use Logger: https://github.com/tmuth/Logger---A-PL-SQL-Logging-Utility It will handle your requirements.

Check out PLJ-Logger at https://sourceforge.net/p/plj-logger/home/. Its really easy to implement and has your desired functionality and a lot more. Proper logging built into PL/SQL code will transform it.
P

Related

Debugging PL/SQL Procedure for reservation service report

For a school project, unable to figure out how to debug the code, I've managed to figure out that the problem is in this line of code: Reservation_id:=: reservation_ID; but I am unsure how to fix it
Set serveroutput ON;
Create or replace procedure Reservation_Services_Report (reservation_ID IN number)
As
Service_number reservation.service_type_id%type;
People_attending reservation.numb_people_attend%type;
Begin
Reservation_id:=: reservation_ID;
Select s.service_name, s.service_type, s.service_type_food, s.service_type_entertainment, r.numb_people_attend from services s, reservation r
where services.service_type_id = reservation.service_type_id;
Exception
When no_data_found then
dbm_output.put_line(‘No services for this reservation’);
End;
Oh, it only it were your only problem!
as you declared an IN parameter, why don't you use it? It is here to be passed to the procedure, not to accept it at runtime (which is what you planned to do with preceding its name with a colon).
besides, it is a good idea to distinguish parameter name from column name, otherwise you'll have problems as Oracle won't know which is which. That's why I renamed it to par_reservation_id
select in PL/SQL requires into clause. In your case, you need to declare all those variables which match select column list. I named them all with the l_ prefix (as a l_ocal variable); some people use v_; pick whichever you want, just try to stick to some standards - it'll make your code easier to read, debug and maintain
select you wrote would probably return too_many_rows error as there's no restriction to number of rows; I presume you "forgot" to include the where clause so I put it there; I don't know whether it is correct or not as I don't have your tables. Fix it, if necessary
when joining tables, try to switch to modern ANSI syntax and separate joins from (where) conditions, as I tried
unless you're running that code in a tool that supports dbms_output to be displayed on the screen, you won't see anything. Exception is handled (kind of), but nobody will know what happened. Consider raising the error instead, e.g. raise_application_error(-20000, 'No services for this reservation')
Here's how the procedure might look like; hopefully, it is somewhat better than the original version. See if it helps.
Create or replace procedure
Reservation_Services_Report (par_reservation_ID IN number)
As
l_service_name services.service_name%type;
l_service_type services.service_type%type;
l_service_type_Food services.service_type_Food%type;
l_service_type_entertainment services.service_type_entertainment%type;
l_numb_people_attend reservation.numb_people_attend%type;
Begin
Select s.service_name,
s.service_type,
s.service_type_food,
s.service_type_entertainment,
r.numb_people_attend
into l_service_name,
l_service_type,
l_service_type_food,
l_service_type_entertainment,
l_numb_people_attend
from services s join reservation r on s.service_type_id = r.service_type_id
where r.reservation_id = par_reservation_id;
Exception
When no_data_found then
dbms_output.put_line(‘No services for this reservation’);
End;

Error logging/debugging mechanism for PL/SQL code

I want to create an error logging and debugging mechanism for my PLSQL code.
There will be a log_Error table in which i will inserting the errors/debugs.
I am planning to insert debugs periodically in my code so that it will be easy for me to identify till which point my code is getting execution
In the exception section i will be inserting the error messages in this log table.
Also,I want a mechanism in which i can enable this logging mechanism for a particular session instead of all the sessions by default.
If this logging happens by default, it will create unnecessary performance impact and create unwanted logs
Can you please suggest and approach in which i am able to enable/disable logging mechanism for a session manually?
You can create a small logging package where you set a flag per session like this
create package debug_log_pk as
bLogflag boolean := false;
end debug_log_pk;
then create a procedure that insert data into your table:
create or replace procedure log_error( ..... )
as
pragma autonomous_transaction;
begin
if debug_log_pk.bLogflag then
insert into logging_table (...) values (...);
commit;
end if;
end;
Somewhere in your program. set:
debug_log_pk.bLogflag := true;
That can be done anywhere in your application code before you want to log, and will apply for the reset of the session. And you can turn logging off as well :)
Also the pragma autonomous_transaction; puts the logging into a separate transaction so it will survive a rollback in the db.
Also have a look at this:
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1171766400346817259

Using a pl/sql procedure to log errors and handle exceptions

so far stack overflow and the oracle forums and docs have been my best friend in learning PLSQL. I'm running into an issue here. Any advice is appreciated. I'm writing a procedure that would be used to log any errors a package may encounter and log them into the error log table I created. here is my code thus far.
CREATE OR REPLACE PROCEDURE APMS.test_procedure AS
procedure write_error_log (errcode number, errstr varchar2, errline varchar2) is
pragma autonomous_transaction;
-- this procedure stays in its own new private transaction
begin
INSERT INTO error_log
(ora_err_tmsp,
ora_err_number,
ora_err_msg,
ora_err_line_no)
values (CURRENT_TIMESTAMP,
errcode,
errstr,
errline);
COMMIT; -- this commit does not interfere with the caller's transaction.
end write_error_log;
BEGIN
INSERT INTO mockdata
VALUES ('data1', 'mockname', 'mockcity');
exception when others then
write_error_log(sqlcode,sqlerrm,dbms_utility.format_error_backtrace);
raise;
END test_procedure;
/
In the procedure I currently am using a mockdata table to induce an invalid number error and log that to the error_log table. At this point the error log table proves to be functional and inserts the data needed. The next step for me is to use this procedure to be used in the exception handlers in other programs so that the error is caught and logged to the table. Currently, my procedure is only unique to the mock_data table. My mentor/superior is telling me I need to pass this program some parameters to use it in other packages and exception handlers. I'm just having a bit of trouble. Any help would be appreciated thank you!
Steven Feuerstein has written several articles in Oracle Magazine on how to handle errors in PLSQL. He offers a small framework (errpkg) for doing this. The DRY principle (Don't Repeat Yourself) is his mantra!
https://resources.oreilly.com/examples/0636920024859/blob/master/errpkg.pkg
First, you should not make your caller pass in errline. That's very tedious! And what happens when the developer needs to insert a line or two of code? Do they need to update every call to write_error_log after that point to update the line numbers? Your write_error_log should use dbms_utility.format_call_stack (or the 12c more convenient variant of that.. don't have its name handy) to figure out what line of code issued the call to write_error_log.
Then, you should have a 2nd procedure called, say, write_exception. All that needs to do is something like this:
write_error_log (SQLCODE, SUBSTR (DBMS_UTILITY.format_error_stack || DBMS_UTILITY.format_error_backtrace, 1, 4000));

Quick method to catch/capture Stored Procedure calls (with parameters) in Oracle?

I need to capture Oracle stored procedures calls (with parameters) to trace an application (which uses JDBC to connect to the DB). I need something like sp_trace_setevent for Rpc:Completed event in MS SQL SERVER.
I do not have access to this application, but have mostly all rights in the database. I would like to stay in PL/SQL (and Oracle SQL Developer 3.2.20).
I have tried:
Oracle SQL Developer UI "Tools"/ "RealTime SQL Monitoring" and "Tools"/ "Sessions" instruments but can't understand how to enabling accumulating information instead of capturing moment snapshot.
exploring v$sql - it seems there are no sp calls.
v$sqlarea differences (Oracle: is there a tool to trace queries, like Profiler for sql server? , mdj3884 reply) - there I am able to find my test call, but without parameters...
Suggestion from Tom's article : http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:767025833873. Particularly, it is looping through v$sqltext_with_newlines, but I can't understand what is a script result. Something more like moment snapshot; isn't it? But then why they call it tracking?
use DBMS_APPLICATION_INFO - as I understand using this I can add custom info to V$SESSION and V$SESSION_LONGOPS - it can be useful for monitoring tasks but I can't imagine how it can be used for accumulating information about sp calls and theirs parameters.
use DBMS_MONITOR for enabling tracing into file. but I can't find option to enable tracing only sp call events, also it requires access to the server files.
DBMS_PROFILER - collects as I understand by default it collects only statistics (min, max time); there should be possibility to add custom information to plsql_profiler_runs but I can't find this table (when DBMS_PROFILER is in place).
What to see next? What I have missed?
P.S. If the only one way is to change SP body (those SP which need to be traced) then what is the quickest and safest way to log sp parameters from sp body in Oracle? It could be logging to custom table, but may be I could choose between generating another types of events (that are not rollbacked, something like SQL Server custom trace events)?
It would be easy to add some custom functionality to do this (see below for most of what is required) or you could use sqltrace or the enterprise manager reports and search through them:
create package p_audit as
type t_param_type is table of varchar2(50) index by binary_integer;
procedure p_audit (p_procedure varchar2, l_param_type t_param_type);
end;
create table audit_table (procedure_name varchar2(50), parameters varchar2(500))
create or replace package body p_audit is
procedure p_audit (p_procedure varchar2, l_param_type t_param_type) is
pragma autonomous_transaction;
begin
insert into audit_table values (p_procedure,l_param_type(1));
commit;
end;
end p_audit;
declare
l_param_type p_audit.t_param_type;
begin
l_param_type(1):='parameter 1';
p_audit.p_audit('test procedure',l_param_type);
end;
I used DBMS_AUDIT trail to determine what kind of procedures/functions/packages used when a client side application executed. I would really recommend you to use it once, it really helps, but problem is that you cannot analise deeper in a package hierarchy, its function/procedure called (calls), but only its usage. If you want to know dependency of the package you can use ALL_DEPENDENCIES. It can be helpful.

Semantics of Oracle stored procedures / functions in a transactional context

I have recently had some input from a colleague regarding committing in a stored function. Whether we use procedures or functions to execute offline / batch logic in an Oracle database is mostly a matter of taste in our application. In both cases, we return a code either as function result, or as procedure OUT parameter. We usually require those offline / batch routines to be called from PL/SQL, not from SQL:
-- good
declare
rc number(7);
begin
rc := our_function(1, 2, 3);
end;
-- less good
select our_function(1, 2, 3) from dual;
The reason why the latter is less good is because our_function may commit the transaction for performance reasons. This is ok for a batch routine.
The question is: Are there any best practices around this topic, or some special keywords that prevent such functions from being used in SQL statements on a compiler-level? Or should we avoid functions for batch operations and only use procedures?
You can use RESTRICT_REFERENCES to indicate that a function won't read/write package or database state.
CREATE PACKAGE t_pkg AS
FUNCTION showup (msg VARCHAR2) RETURN VARCHAR2;
PRAGMA RESTRICT_REFERENCES(showup, WNDS, RNDS);
END t_pkg;
/
-- create the package body
CREATE OR REPLACE PACKAGE BODY t_pkg AS
FUNCTION showup (msg VARCHAR2) RETURN VARCHAR2 IS
v_val varchar2(1);
BEGIN
select dummy into v_val from dual;
RETURN v_val;
END;
END t_pkg;
/
It used to be the case that SQL wouldn't allow you to call a function unless it made such a promise, but that restriction got dropped.
I'd prefer to make it a differentiator between a procedure and a function. It's worth bearing in mind that if a PL/SQL function raises a NO_DATA_FOUND exception, a calling SQL statement does not fail (as no data found isn't an SQL error). So I prefer to use procedures unless the object is specifically designed to be called from SQL.
Are there any best practices around this topic, or some special
keywords that prevent such functions from being used in SQL statements
on a compiler-level?
If you use a function that requires a transaction (and therefore a commit), AFAIK you will not be able to call it from a SELECT, unless the function uses an AUTONOMOUS TRANSACTION (otherwise you get a ORA-14551 cannot perform a DML operation inside a query).
See also: ORA-14551: cannot perform a DML operation inside a query
So, having a function that requires a transaction itself should prevent it from being called from a SELECT.
From my point of view there is no way to accomplish this.Although you can avoid runtime errors like "ORA-14551" by using PRAGMA RESTRICT_REFERENCES in "our_function(1, 2, 3)" to be sure that it is safe to use it the SQL query,but you can't prevent it from using in sql at the compiler-level

Resources