diff --git a/.gitignore b/.gitignore index c0d261f..3ab8155 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,9 @@ releases/* !releases/*.zip #For developers temp/* +Sandbox/ #For IMDone Atom.io plugin .imdone/* +*.tst +*.dsk +*.prj diff --git a/Sandbox/test1.tst b/Sandbox/test1.tst new file mode 100644 index 0000000..db4a5f8 --- /dev/null +++ b/Sandbox/test1.tst @@ -0,0 +1,20 @@ +PL/SQL Developer Test script 3.0 +16 +--clear screen +--set serveroutput on size unlimited + +declare +begin + logger.set_level(p_level => 'ERROR'); + for indx in 1 .. 100 loop + logger.log(p_text => 'Debug message '||to_char(indx), p_scope => 'Anonymous'); + logger.log_information(p_text => 'Information message '||to_char(indx), p_scope => 'Anonymous'); + logger.log_warning(p_text => 'Warning message '||to_char(indx), p_scope => 'Anonymous'); + end loop; + logger.log_error(p_text => 'An error '||to_char(systimestamp, 'YYYY-MM-DD HH24:MI:SS.FF6') + ,p_scope => 'Anonymous' + ); +end; +--/ +0 +0 diff --git a/logger.dsk b/logger.dsk new file mode 100644 index 0000000..ef3b119 --- /dev/null +++ b/logger.dsk @@ -0,0 +1,88 @@ +PL/SQL Developer Project Desktop + +[Desktop] +SavePath=C:\MyStack\MyGitHub\Patch72\Logger_Patch72\ + +Index=1 +Filename=C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\install\create_user.sql +Connection=2670417640743124500642484146431647424128441047884238410441784556317431043354354040784408449843484454448043144724439844244306340434143424 +DPI=120 +Left=0 +Top=0 +Width=1123 +Height=850 +State=0 +Type=4 +ChildListIndex=1 +Pinned=0 + +Index=2 +Filename= +Connection=217947894783469746753677483947374587462935994585505948935047356935633573406339453955 +DPI=120 +Left=0 +Top=0 +Width=1123 +Height=850 +State=0 +Type=4 +ChildListIndex=3 +Pinned=-1 + +Index=3 +Filename=C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\jobs\logger_purge_job.sql +Connection=2228475846563578456448304600477043645094473643784948507050485010359636863936399446604990495250584972493446404122485248784728385838683878 +DPI=120 +Left=0 +Top=0 +Width=1123 +Height=850 +State=0 +Type=4 +ChildListIndex=2 +Pinned=0 + +Index=4 +Filename=C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\packages\logger.pks +VCSDBObject=0 +Connection=2114467647983720441846844742478442504692487842324834460446784640396240203790400846424844509449444858504447824232499449884614400038823892 +DPI=120 +Left=0 +Top=0 +Width=1123 +Height=850 +State=0 +Type=3 +ChildListIndex=5 +Pinned=0 + +Index=5 +Filename=C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\packages\logger.pkb +VCSDBObject=0 +Connection=2725426341293051509342714073424347974087420948434421457544894547310131593377346741654431442545314509443941134651445343834233333133413351 +DPI=120 +Left=0 +Top=0 +Width=626 +Height=541 +State=0 +Type=3 +ChildListIndex=4 +Pinned=0 + +[Files] + +[MRU] +3,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\packages\logger.pkb +3,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\packages\logger.pks +3,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\procedures\logger_configure.plb +4,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\jobs\logger_purge_job.sql +4,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\scripts\grant_logger_to_user.sql +4,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\scripts\grant_logger_to_public.sql +4,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\scripts\create_logger_synonyms.sql +4,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\scripts\create_logger_public_synonyms.sql +4,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\scripts\logger_AA_prefs.sql +4,C:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\install\post_install_configuration.sql + +[Layout] +Group= diff --git a/logger.prj b/logger.prj new file mode 100644 index 0000000..b945156 --- /dev/null +++ b/logger.prj @@ -0,0 +1,58 @@ +PL/SQL Developer Project + +[Options] +AutoConnect=0 +Username= +Password=2933 +Database= +ConnectAs= +Edition= +Workspace= +HaltAfterError=0 +BrowseHistory=0 +FileBrowser=0 +BeautfierRulesFile= +VersionControl=0 +VersionControlPath= +ShowItems=-1 + +[History] +Programs=D:\MyStack\MyProjects\OracleDeveloper\Packages +SQL Scripts=D:\MyStack\MyProjects +Report Files= +Test Scripts= +Command Scripts=D:\MyStack\MyGitHub\Patch72\Logger_Patch72\source\contexts +Diagram Files=D:\MyStack\MyProjects\OracleDeveloper\Diagram + +[BrowserFolders] + + +[Groups] + +[Files] +4,0,,,source\install\drop_logger.sql +4,0,,,source\install\create_user.sql +4,4,,,source\install\logger_install_prereqs.sql +4,4,,,source\tables\logger_logs.sql +4,4,,,source\tables\logger_prefs.sql +4,4,,,source\tables\logger_logs_apex_items.sql +4,4,,,source\tables\logger_prefs_by_client_id.sql +4,4,,,source\tables\logger_prefs_by_scope.sql +4,4,,,source\views\logger_logs_5_min.sql +4,4,,,source\views\logger_logs_60_min.sql +4,4,,,source\views\logger_logs_terse.sql +4,4,,,source\jobs\logger_unset_prefs_by_client.sql +4,4,,,source\jobs\logger_unset_prefs_by_scope.sql +3,4,,,source\packages\logger.pks +3,4,,,source\packages\logger.pkb +4,4,,,source\contexts\logger_context.sql +3,4,,,source\procedures\logger_configure.plb +4,4,,,source\install\post_install_configuration.sql +4,4,,,source\scripts\logger_AA_prefs.sql +4,4,,,source\scripts\create_logger_public_synonyms.sql +4,0,,,source\scripts\create_logger_synonyms.sql +4,4,,,source\scripts\grant_logger_to_public.sql +4,0,,,source\scripts\grant_logger_to_user.sql +4,4,,,source\jobs\logger_purge_job.sql + +[Notes] diff --git a/source/jobs/logger_unset_prefs_by_scope.sql b/source/jobs/logger_unset_prefs_by_scope.sql new file mode 100644 index 0000000..84a7939 --- /dev/null +++ b/source/jobs/logger_unset_prefs_by_scope.sql @@ -0,0 +1,22 @@ +declare + l_count pls_integer; + l_job_name user_scheduler_jobs.job_name%type := 'LOGGER_UNSET_PREFS_BY_SCOPE'; +begin + + select count(1) + into l_count + from user_scheduler_jobs + where job_name = l_job_name; + + if l_count = 0 then + dbms_scheduler.create_job( + job_name => l_job_name, + job_type => 'PLSQL_BLOCK', + job_action => 'begin logger.unset_scope_level; end; ', + start_date => systimestamp, + repeat_interval => 'FREQ=HOURLY; BYHOUR=1', + enabled => TRUE, + comments => 'Clears expired logger prefs by scope'); + end if; +end; +/ diff --git a/source/packages/logger.pkb b/source/packages/logger.pkb index 8826ab6..479b2c2 100644 --- a/source/packages/logger.pkb +++ b/source/packages/logger.pkb @@ -32,13 +32,31 @@ as -- TYPES type ts_array is table of timestamp index by varchar2(100); - + type log_rec is record( + id logger_logs.id%type + , logger_level logger_logs.logger_level%type + , text varchar2(32767) default null -- Not using type since want to be able to pass in 32767 characters + , time_stamp logger_logs.time_stamp%type default systimestamp + , scope logger_logs.scope%type default null + , module logger_logs.module%type default null + , action logger_logs.action%type default null + , user_name logger_logs.user_name%type default null + , client_identifier logger_logs.client_identifier%type default null + , call_stack logger_logs.call_stack%type default null + , unit_name logger_logs.unit_name%type default null + , line_no logger_logs.line_no%type default null + , scn logger_logs.scn%type default null + , extra logger_logs.extra%type default null + , sid logger_logs.sid%type default null + , client_info logger_logs.client_info%type default null + ); + type log_aat is table of log_rec index by pls_integer; -- VARIABLES g_log_id number; g_proc_start_times ts_array; g_running_timers pls_integer := 0; - + g_internal_log log_aat; -- #46 g_plug_logger_log_error rec_logger_log; @@ -55,9 +73,13 @@ as gc_date_format constant varchar2(255) := 'DD-MON-YYYY HH24:MI:SS'; gc_timestamp_format constant varchar2(255) := gc_date_format || ':FF'; gc_timestamp_tz_format constant varchar2(255) := gc_timestamp_format || ' TZR'; - + gc_default_logger_aa_size constant number := 100; -- PBA 20200316 + gc_default_logger_aa_prefix constant varchar2(100) := ''; -- PBA 20200316 + gc_default_logger_aa_suffix constant varchar2(100) := ''; -- PBA 20200316 + gc_ctx_attr_level constant varchar2(5) := 'level'; gc_ctx_attr_include_call_stack constant varchar2(18) := 'include_call_stack'; + gc_ctx_attr_scope constant varchar2(5) := 'scope'; -- #46 Plugin context names gc_ctx_plugin_fn_log constant varchar2(30) := 'plugin_fn_log'; @@ -77,7 +99,9 @@ as gc_pref_client_id_expire_hours constant logger_prefs.pref_name%type := 'PREF_BY_CLIENT_ID_EXPIRE_HOURS'; gc_pref_logger_debug constant logger_prefs.pref_name%type := 'LOGGER_DEBUG'; gc_pref_plugin_fn_error constant logger_prefs.pref_name%type := 'PLUGIN_FN_ERROR'; - + gc_pref_logger_aa_size constant logger_prefs.pref_name%type := 'LOGGER_AA_SIZE'; -- PBA 20200316 + gc_pref_logger_aa_prefix constant logger_prefs.pref_name%type := 'LOGGER_AA_PREFIX'; -- PBA 20200316 + gc_pref_logger_aa_suffix constant logger_prefs.pref_name%type := 'LOGGER_AA_SUFFIX'; -- PBA 20200316 @@ -429,6 +453,124 @@ as end admin_security_check; + /** + * + * + * Notes: + * - Private + * + * @author Patrick Barel + * @created 20200316 + * @return The number of messages to save in the Associative Array + */ + function get_logger_aa_size + return number + $if $$rac_lt_11_2 $then + $if not dbms_db_version.ver_le_10_2 $then + result_cache relies_on (logger_prefs) + $end + $end + is + l_size number; + + $if $$logger_debug $then + l_scope varchar2(30) := 'get_logger_aa_size'; + $end + + begin + $if $$no_op $then + return gc_default_aa_size; + $else + $if $$logger_debug $then + dbms_output.put_line(l_scope || ': selecting logger AA Size'); + $end + + l_size := nvl(logger.get_pref(p_pref_name => logger.gc_pref_logger_aa_size + ,p_pref_type => logger.g_pref_type_logger_aa),gc_default_logger_aa_size); + + return l_size; + $end + end get_logger_aa_size; + + + /** + * + * + * Notes: + * - Private + * + * @author Patrick Barel + * @created 20200316 + * @return The suffix which get added to the scope column + */ + function get_logger_aa_prefix + return varchar2 + $if $$rac_lt_11_2 $then + $if not dbms_db_version.ver_le_10_2 $then + result_cache relies_on (logger_prefs) + $end + $end + is + l_prefix varchar2(100); + + $if $$logger_debug $then + l_scope varchar2(30) := 'get_logger_aa_prefix'; + $end + + begin + $if $$no_op $then + return gc_default_aa_prefix; + $else + $if $$logger_debug $then + dbms_output.put_line(l_scope || ': selecting logger AA Prefix'); + $end + + l_prefix := nvl(logger.get_pref(p_pref_name => logger.gc_pref_logger_aa_prefix + ,p_pref_type => logger.g_pref_type_logger_aa),gc_default_logger_aa_prefix); + + return l_prefix; + $end + end get_logger_aa_prefix; + + + /** + * + * + * Notes: + * - Private + * + * @author Patrick Barel + * @created 20200316 + * @return The suffix which get added to the scope column + */ + function get_logger_aa_suffix + return varchar2 + $if $$rac_lt_11_2 $then + $if not dbms_db_version.ver_le_10_2 $then + result_cache relies_on (logger_prefs) + $end + $end + is + l_suffix varchar2(100); + + $if $$logger_debug $then + l_scope varchar2(30) := 'get_logger_aa_suffix'; + $end + + begin + $if $$no_op $then + return gc_default_aa_suffix; + $else + $if $$logger_debug $then + dbms_output.put_line(l_scope || ': selecting logger AA Suffix'); + $end + + l_suffix := nvl(logger.get_pref(p_pref_name => logger.gc_pref_logger_aa_suffix + ,p_pref_type => logger.g_pref_type_logger_aa),gc_default_logger_aa_suffix); + + return l_suffix; + $end + end get_logger_aa_suffix; /** * * @@ -440,14 +582,14 @@ as * * @author Tyler Muth * @created ??? - * @param + * @param p_scope * @return If client level specified will return it, otherwise global level. */ - function get_level_number + function get_level_number(p_scope in varchar2 default null) -- PBA/MNU 201704 return number $if $$rac_lt_11_2 $then $if not dbms_db_version.ver_le_10_2 $then - result_cache relies_on (logger_prefs, logger_prefs_by_client_id) + result_cache relies_on (logger_prefs, logger_prefs_by_client_id, logger_prefs_by_scope) -- PBA/MNU 201704 $end $end is @@ -466,7 +608,8 @@ as dbms_output.put_line(l_scope || ': selecting logger_level'); $end - l_level := convert_level_char_to_num(logger.get_pref(logger.gc_pref_level)); + l_level := convert_level_char_to_num(logger.get_pref(p_pref_name => logger.gc_pref_level + ,p_scope => p_scope)); -- PBA/MNU 201704 return l_level; $end @@ -605,6 +748,184 @@ as $end end get_debug_info; + /** + * Procedure to dump the info of the AA into the table + * + * + * @author Patrick Barel + * @created 2020-03-15 + * + */ + procedure aa_dump_log is + pragma autonomous_transaction; + +-- l_id logger_logs.id%type; +-- l_text varchar2(32767); -- := p_text; +-- l_extra logger_logs.extra%type; -- := p_extra; +-- l_tmp_clob clob; + begin + $if $$no_op $then + null; + $else + -- PBA 20200316 update the scope with the prefix and the suffix + for indx in g_internal_log.first .. g_internal_log.last loop + g_internal_log(indx).scope := trim(both ' ' from get_logger_aa_prefix || g_internal_log(indx).scope || get_logger_aa_suffix); + end loop; + -- + forall indx in g_internal_log.first .. g_internal_log.last + insert into logger_logs( + id, logger_level, text, + time_stamp, scope, module, + action, + user_name, + client_identifier, + call_stack, unit_name, line_no , + scn, + extra, + sid, + client_info + ) + values( + logger_logs_seq.nextval, g_internal_log(indx).logger_level, g_internal_log(indx).text, + g_internal_log(indx).time_stamp, lower(g_internal_log(indx).scope), sys_context('userenv','module'), + sys_context('userenv','action'), + nvl($if $$apex $then apex_application.g_user $else user $end,user), + sys_context('userenv','client_identifier'), + g_internal_log(indx).call_stack, upper(g_internal_log(indx).unit_name), g_internal_log(indx).line_no, + null, + g_internal_log(indx).extra, + to_number(sys_context('userenv','sid')), + sys_context('userenv','client_info') + ); + -- as soon as everything got dumped, clean out the AA + g_internal_log.delete; + $end -- $$NO_OP + + commit; + end aa_dump_log; + /** + * Procedure that will store log data into the AA. It will also check that there will not be more than + * the designated number of items. + * + * + * @author Patrick Barel + * @created 2020-03-15 + * @param p_unit_name + * @param p_scope + * @param p_log_level + * @param p_extra + * @param p_text + * @param p_callstack + * @param p_line_no + * + */ + procedure aa_add_log(p_unit_name in logger_logs.unit_name%type default null + ,p_scope in logger_logs.scope%type default null + ,p_logger_level in logger_logs.logger_level%type + ,p_extra in logger_logs.extra%type default null + ,p_text in varchar2 default null -- Not using type since want to be able to pass in 32767 characters + ,p_call_stack in logger_logs.call_stack%type default null + ,p_line_no in logger_logs.line_no%type default null) is + l_text varchar2(32767) := p_text; + l_extra logger_logs.extra%type := p_extra; + l_tmp_clob clob; + begin + $if $$large_text_column $then -- Only check for moving to Clob if small text column + -- Don't do anything since column supports large text + $else + if lengthb(l_text) > 4000 then -- #109 Using lengthb for multibyte characters + if l_extra is null then + l_extra := l_text; + else + -- Using temp clob for performance purposes: http://www.talkapex.com/2009/06/how-to-quickly-append-varchar2-to-clob.html + l_tmp_clob := gc_line_feed || gc_line_feed || '*** Content moved from TEXT column ***' || gc_line_feed; + l_extra := l_extra || l_tmp_clob; + l_tmp_clob := l_text; + -- PBA 20200315 I think the temp clob should be copied instead of the orginal text +-- l_extra := l_extra || l_text; + l_extra := l_extra || l_tmp_clob; + end if; -- l_extra is not null + + l_text := 'Text moved to EXTRA column'; + end if; -- length(l_text) + $end + + -- add a record to the AA + g_internal_log(nvl(g_internal_log.last,0) + 1).unit_name := p_unit_name; + g_internal_log(g_internal_log.last).scope := p_scope; + g_internal_log(g_internal_log.last).logger_level := p_logger_level; + g_internal_log(g_internal_log.last).extra := l_extra; + g_internal_log(g_internal_log.last).text := l_text; + g_internal_log(g_internal_log.last).call_stack := p_call_stack; + g_internal_log(g_internal_log.last).line_no := p_line_no; + g_internal_log(g_internal_log.last).time_stamp := systimestamp; + -- check if we have more than the AA size records + if g_internal_log.count > get_logger_aa_size then + -- then delete the first one + g_internal_log.delete(g_internal_log.first); + end if; + end aa_add_log; +-- + /** + * Main procedure that will store log data into an AA, for later use + * + * + * @author Patrick Barel + * @created 20200315 + * @param p_text + * @param p_log_level + * @param p_scope + * @param p_extra + * @param p_callstack + * @param p_params + * + */ + procedure aa_log( + p_text in varchar2, + p_log_level in number, + p_scope in varchar2, + p_extra in clob default null, + p_callstack in varchar2 default null, + p_params in tab_param default logger.gc_empty_tab_param) + is + l_proc_name varchar2(100); + l_lineno varchar2(100); + l_text varchar2(32767); + l_callstack varchar2(3000); + l_extra logger_logs.extra%type; + begin + $if $$no_op $then + null; + $else + l_text := p_text; + + -- Generate callstack text + if p_callstack is not null and logger.include_call_stack then + logger.get_debug_info( + p_callstack => p_callstack, + o_unit => l_proc_name, + o_lineno => l_lineno); + + l_callstack := regexp_replace(p_callstack,'^.*$','',1,4,'m'); + l_callstack := regexp_replace(l_callstack,'^.*$','',1,1,'m'); + l_callstack := ltrim(replace(l_callstack,chr(10)||chr(10),chr(10)),chr(10)); + + end if; + + l_extra := set_extra_with_params(p_extra => p_extra, p_params => p_params); + + -- save all the info into the AA + aa_add_log( + p_unit_name => upper(l_proc_name) , + p_scope => p_scope , + p_logger_level =>p_log_level, + p_extra => l_extra, + p_text =>l_text, + p_call_stack =>l_callstack, + p_line_no => l_lineno); + $end + end aa_log; + /** * Main procedure that will store log data into logger_logs table @@ -929,15 +1250,17 @@ as * @created ??? * * @param p_level Level (number) + * @param p_scope * @return True of statement can be logged to LOGGER_LOGS */ - function ok_to_log(p_level in number) + function ok_to_log(p_level in number, + p_scope in varchar2 default null) return boolean $if 1=1 and $$rac_lt_11_2 and not dbms_db_version.ver_le_10_2 and ($$no_op is null or not $$no_op) $then - result_cache relies_on (logger_prefs, logger_prefs_by_client_id) + result_cache relies_on (logger_prefs, logger_prefs_by_client_id, logger_prefs_by_scope) -- PBA/MNU 201704 $end is l_level number; @@ -960,21 +1283,31 @@ as $if $$logger_debug $then dbms_output.put_line(l_scope || ': calling get_level_number'); $end - l_level := get_level_number; + l_level := get_level_number(p_scope => p_scope); -- PBA/MNU 201704 $else - l_level := sys_context(g_context_name,gc_ctx_attr_level); + l_level := sys_context(g_context_name,gc_ctx_attr_level); + + -- PBA/MNU 201704 + -- if the current scope is different from the saved scope (context) then clear the level + -- (only applies to non-empty scopes) + if coalesce(sys_context(g_context_name,gc_ctx_attr_scope),'#^') <> coalesce(p_scope,'#^') then + l_level := null; + end if; if l_level is null then $if $$logger_debug $then dbms_output.put_line(l_scope || ': level was null, getting and setting in context'); $end - l_level := get_level_number; + l_level := get_level_number(p_scope => p_scope); -- PBA/MNU 201704 save_global_context( p_attribute => gc_ctx_attr_level, p_value => l_level, p_client_id => sys_context('userenv','client_identifier')); + save_global_context( + p_attribute => gc_ctx_attr_scope, + p_value => p_scope); end if; $end @@ -994,16 +1327,19 @@ as * @created 25-Jul-2013 * * @param p_level Level (DEBUG etc..) + * @param p_scope * @return True of log statements for that level or below will be logged */ - function ok_to_log(p_level in varchar2) + function ok_to_log(p_level in varchar2, + p_scope in varchar2 default null) return boolean as begin $if $$no_op $then return false; $else - return ok_to_log(p_level => convert_level_char_to_num(p_level => p_level)); + return ok_to_log(p_level => convert_level_char_to_num(p_level => p_level) + ,p_scope => p_scope); $end end ok_to_log; @@ -1182,7 +1518,8 @@ as $if $$no_op $then null; $else - if ok_to_log(logger.g_error) then +-- if ok_to_log(logger.g_error) then -- PBA 20190428 take the scope into account + if ok_to_log(logger.g_error, p_scope) then get_debug_info( p_callstack => dbms_utility.format_call_stack, o_unit => l_proc_name, @@ -1222,6 +1559,8 @@ as run_plugin(p_logger_log => g_plug_logger_log_error); end if; -- not g_in_plugin_error $end + -- PBA 20200315: When we log an error, also dump the AA + aa_dump_log; end if; -- ok_to_log $end @@ -1254,7 +1593,8 @@ as $if $$no_op $then null; $else - if ok_to_log(logger.g_permanent) then +-- if ok_to_log(logger.g_permanent) then -- PBA 20190428 take the scope into account + if ok_to_log(logger.g_permanent, p_scope) then log_internal( p_text => p_text, p_log_level => logger.g_permanent, @@ -1264,6 +1604,14 @@ as p_params => p_params ); end if; + -- PBA 20200315 Always log in the AA internally + aa_log( + p_text => p_text, + p_log_level => logger.g_debug, + p_scope => p_scope, + p_extra => p_extra, + p_callstack => dbms_utility.format_call_stack, + p_params => p_params); $end end log_permanent; @@ -1294,7 +1642,8 @@ as $if $$no_op $then null; $else - if ok_to_log(logger.g_warning) then +-- if ok_to_log(logger.g_warning) then -- PBA 20190428 take the scope into account + if ok_to_log(logger.g_warning, p_scope) then log_internal( p_text => p_text, p_log_level => logger.g_warning, @@ -1302,6 +1651,15 @@ as p_extra => p_extra, p_callstack => dbms_utility.format_call_stack, p_params => p_params); + else + -- PBA 20200315 If we don't log in the table, we always log in the AA internally + aa_log( + p_text => p_text, + p_log_level => logger.g_warning, + p_scope => p_scope, + p_extra => p_extra, + p_callstack => dbms_utility.format_call_stack, + p_params => p_params); end if; $end end log_warning; @@ -1364,7 +1722,8 @@ as $if $$no_op $then null; $else - if ok_to_log(logger.g_information) then +-- if ok_to_log(logger.g_information) then -- PBA 20190428 take the scope into account + if ok_to_log(logger.g_information, p_scope) then log_internal( p_text => p_text, p_log_level => logger.g_information, @@ -1372,6 +1731,15 @@ as p_extra => p_extra, p_callstack => dbms_utility.format_call_stack, p_params => p_params); + else + -- PBA 20200315 If we don't log in the table, we always log in the AA internally + aa_log( + p_text => p_text, + p_log_level => logger.g_information, + p_scope => p_scope, + p_extra => p_extra, + p_callstack => dbms_utility.format_call_stack, + p_params => p_params); end if; $end end log_information; @@ -1436,7 +1804,8 @@ as $if $$no_op $then null; $else - if ok_to_log(logger.g_debug) then +-- if ok_to_log(logger.g_debug) then -- PBA 20190428 take the scope into account + if ok_to_log(logger.g_debug, p_scope) then log_internal( p_text => p_text, p_log_level => logger.g_debug, @@ -1444,6 +1813,15 @@ as p_extra => p_extra, p_callstack => dbms_utility.format_call_stack, p_params => p_params); + else + -- PBA 20200315 If we don't log in the table, we always log in the AA internally + aa_log( + p_text => p_text, + p_log_level => logger.g_debug, + p_scope => p_scope, + p_extra => p_extra, + p_callstack => dbms_utility.format_call_stack, + p_params => p_params); end if; $end end log; @@ -1525,7 +1903,8 @@ as $if $$no_op $then null; $else - if ok_to_log(nvl(p_level, logger.g_debug)) then +-- if ok_to_log(nvl(p_level, logger.g_debug)) then -- PBA 20190428 take the scope into account + if ok_to_log(nvl(p_level, logger.g_debug), p_scope) then l_extra := get_sys_context( p_detail_level => p_detail_level, p_vertical => true, @@ -1566,7 +1945,8 @@ as $if $$no_op $then null; $else - if ok_to_log(nvl(p_level, logger.g_debug)) then +-- if ok_to_log(nvl(p_level, logger.g_debug)) then -- PBA 20190428 take the scope into account + if ok_to_log(nvl(p_level, logger.g_debug), p_scope) then l_extra := get_cgi_env(p_show_null => p_show_null); log_internal( p_text => 'CGI ENV values stored in the EXTRA column', @@ -1606,7 +1986,8 @@ as $if $$no_op $then null; $else - if ok_to_log(nvl(p_level, logger.g_debug)) then +-- if ok_to_log(nvl(p_level, logger.g_debug)) then -- PBA 20190428 take the scope into account + if ok_to_log(nvl(p_level, logger.g_debug), p_scope) then l_dump := get_character_codes(p_text,p_show_common_codes); log_internal( @@ -1651,7 +2032,8 @@ as $if $$no_op $then null; $else - if ok_to_log(nvl(p_level, logger.g_debug)) then +-- if ok_to_log(nvl(p_level, logger.g_debug)) then -- PBA 20190428 take the scope into account + if ok_to_log(nvl(p_level, logger.g_debug), p_scope) then $if $$apex $then -- Validate p_item_type @@ -1755,7 +2137,8 @@ as $if $$no_op $then null; $else - if ok_to_log(logger.g_debug) then +-- if ok_to_log(logger.g_debug) then + if ok_to_log(logger.g_debug, p_scope) then if g_proc_start_times.exists(p_unit) then if g_running_timers > 1 then @@ -1810,7 +2193,8 @@ as $if $$no_op $then null; $else - if ok_to_log(logger.g_debug) then +-- if ok_to_log(logger.g_debug) then -- PBA 20190428 take the scope into account + if ok_to_log(logger.g_debug, p_scope) then if g_proc_start_times.exists(p_unit) then l_time_string := rtrim(regexp_replace(localtimestamp - (g_proc_start_times(p_unit)),'.+?[[:space:]](.*)','\1',1,0),0); @@ -1866,7 +2250,8 @@ as $if $$no_op $then null; $else - if ok_to_log(logger.g_debug) then +-- if ok_to_log(logger.g_debug) then -- PBA 20190428 take the scope into account + if ok_to_log(logger.g_debug, p_scope) then if g_proc_start_times.exists(p_unit) then l_interval := localtimestamp - (g_proc_start_times(p_unit)); l_seconds := extract(day from l_interval) * 86400 + extract(hour from l_interval) * 3600 + extract(minute from l_interval) * 60 + extract(second from l_interval); @@ -1936,12 +2321,14 @@ as */ function get_pref( p_pref_name in logger_prefs.pref_name%type, - p_pref_type in logger_prefs.pref_type%type default logger.g_pref_type_logger) + p_pref_type in logger_prefs.pref_type%type default logger.g_pref_type_logger, + p_scope in varchar2 default null -- PBA/MNU 201704 + ) return varchar2 $if not dbms_db_version.ver_le_10_2 $then result_cache $if $$no_op is null or not $$no_op $then - relies_on (logger_prefs, logger_prefs_by_client_id) + relies_on (logger_prefs, logger_prefs_by_client_id, logger_prefs_by_scope) -- PBA/MNU 201704 $end $end is @@ -1950,6 +2337,10 @@ as l_client_id logger_prefs_by_client_id.client_id%type; l_pref_name logger_prefs.pref_name%type := upper(p_pref_name); l_pref_type logger_prefs.pref_type%type := upper(p_pref_type); + -- PBA/MNU 201704 + -- p_scope is entered as lowercase in the table so make the parameter lowercase here + -- l_scope is already used, so we use lp_scope + lp_scope logger_prefs_by_scope.logger_scope%type := lower(p_scope); begin $if $$no_op $then @@ -1966,6 +2357,18 @@ as from ( select pref_value, row_number () over (order by rank) rn from ( + -- Scope specific logger levels trump client specific AND system level loggel level + select pref_value + , 0 rank + from (select logger_level pref_value + from logger_prefs_by_scope + where 1 = 1 + and l_pref_name = logger.gc_pref_level + and lp_scope like logger_scope + order by length(logger_scope) desc) + where 1 = 1 + and rownum <= 1 + union all -- Client specific logger levels trump system level logger level select case @@ -2622,6 +3025,26 @@ as return case p_val when true then 'TRUE' when false then 'FALSE' else null end; end tochar; + function tochar( + p_val in interval year to month) + return varchar2 + as + begin + return to_char(extract(year FROM p_val), 'fm99999') || ' years ' || + to_char(extract(month FROM p_val), 'fm99') || ' months'; + end tochar; + + function tochar( + p_val in interval day to second) + return varchar2 + as + begin + return to_char(extract(DAY FROM p_val), 'fmS99999') || ' ' || + to_char(extract(HOUR FROM p_val), 'fm00') || ':' || + to_char(extract(MINUTE FROM p_val), 'fm00') || ':' || + to_char(extract(SECOND FROM p_val), 'fm00.000'); + end tochar; + -- Handle Parameters @@ -2742,6 +3165,34 @@ as $end end append_param; + procedure append_param( + p_params in out nocopy logger.tab_param, + p_name in varchar2, + p_val in interval year to month) + as + l_param logger.rec_param; + begin + $if $$no_op $then + null; + $else + logger.append_param(p_params => p_params, p_name => p_name, p_val => logger.tochar(p_val => p_val)); + $end + end append_param; + + procedure append_param( + p_params in out nocopy logger.tab_param, + p_name in varchar2, + p_val in interval day to second) + as + l_param logger.rec_param; + begin + $if $$no_op $then + null; + $else + logger.append_param(p_params => p_params, p_name => p_name, p_val => logger.tochar(p_val => p_val)); + $end + end append_param; + /** * Handles inserts into LOGGER_LOGS. @@ -2952,5 +3403,24 @@ as end if; end get_plugin_rec; + /** + * Unsets scope_level that are stale (i.e. past their expiry date) + * + * @author Patrick Barel/Milco Numan + * @created 18-Apr-2017 + */ + procedure unset_scope_level + as + begin + $if $$no_op $then + null; + $else + delete + from logger_prefs_by_scope + where sysdate > expiry_date + ; + commit; + $end + end unset_scope_level; end logger; / diff --git a/source/packages/logger.pks b/source/packages/logger.pks index a34a848..35c7b49 100644 --- a/source/packages/logger.pks +++ b/source/packages/logger.pks @@ -29,7 +29,9 @@ as -- TYPES type rec_param is record( name varchar2(255), - val varchar2(4000)); +-- val varchar2(4000)); + val varchar2(32767) -- PBA: This is enlarged, since it is stored in a CLOB anyway + ); type tab_param is table of rec_param index by binary_integer; @@ -40,16 +42,16 @@ as -- VARIABLES - g_logger_version constant varchar2(10) := 'x.x.x'; -- Don't change this. Build script will replace with right version number - g_context_name constant varchar2(35) := substr(sys_context('USERENV','CURRENT_SCHEMA'),1,23)||'_LOGCTX'; + g_logger_version constant varchar2(10) := 'x.x.x'; -- Don't change this. Build script will replace with right version number + g_context_name constant varchar2(35) := substr(sys_context('USERENV','CURRENT_SCHEMA'),1,23)||'_LOGCTX'; g_off constant number := 0; g_permanent constant number := 1; - g_error constant number := 2; - g_warning constant number := 4; - g_information constant number := 8; + g_error constant number := 2; + g_warning constant number := 4; + g_information constant number := 8; g_debug constant number := 16; - g_timing constant number := 32; + g_timing constant number := 32; g_sys_context constant number := 64; g_apex constant number := 128; @@ -75,6 +77,7 @@ as -- #127 -- Note to developers: This is only for internal Logger code. Do not use this as part of your code. g_pref_type_logger constant logger_prefs.pref_type%type := 'LOGGER'; -- If this changes need to modify logger_prefs.sql as it has a dependancy. + g_pref_type_logger_aa constant logger_prefs.pref_type%type := 'LOGGER_AA'; -- PBA 20200316 -- Expose private functions only for testing during development $if $$logger_debug $then @@ -107,8 +110,8 @@ as function admin_security_check return boolean; - function get_level_number - return number; +-- function get_level_number(p_scope in varchar2 default null) +-- return number; function include_call_stack return boolean; @@ -142,9 +145,9 @@ as function date_text_format (p_date in date) return varchar2; - function get_character_codes( - p_string in varchar2, - p_show_common_codes in boolean default true) + function get_character_codes( + p_string in varchar2, + p_show_common_codes in boolean default true) return varchar2; procedure log_error( @@ -190,8 +193,8 @@ as p_params in tab_param default logger.gc_empty_tab_param); function get_cgi_env( - p_show_null in boolean default false) - return clob; + p_show_null in boolean default false) + return clob; procedure log_userenv( p_detail_level in varchar2 default 'USER',-- ALL, NLS, USER, INSTANCE, @@ -217,12 +220,12 @@ as p_log_null_items in boolean default true, p_level in logger_logs.logger_level%type default null); - procedure time_start( - p_unit in varchar2, + procedure time_start( + p_unit in varchar2, p_log_in_table in boolean default true); - procedure time_stop( - p_unit in varchar2, + procedure time_stop( + p_unit in varchar2, p_scope in varchar2 default null); function time_stop( @@ -241,7 +244,8 @@ as function get_pref( p_pref_name in logger_prefs.pref_name%type, - p_pref_type in logger_prefs.pref_type%type default logger.g_pref_type_logger) + p_pref_type in logger_prefs.pref_type%type default logger.g_pref_type_logger, + p_scope in varchar2 default null) return varchar2 $if not dbms_db_version.ver_le_10_2 $then result_cache @@ -258,18 +262,18 @@ as p_pref_type in logger_prefs.pref_type%type, p_pref_name in logger_prefs.pref_name%type); - procedure purge( - p_purge_after_days in varchar2 default null, - p_purge_min_level in varchar2 default null); + procedure purge( + p_purge_after_days in varchar2 default null, + p_purge_min_level in varchar2 default null); procedure purge( p_purge_after_days in number default null, p_purge_min_level in number); - procedure purge_all; + procedure purge_all; - procedure status( - p_output_format in varchar2 default null); -- SQL-DEVELOPER | HTML | DBMS_OUPUT + procedure status( + p_output_format in varchar2 default null); -- SQL-DEVELOPER | HTML | DBMS_OUPUT procedure sqlplus_format; @@ -322,10 +326,23 @@ as p_name in varchar2, p_val in boolean); - function ok_to_log(p_level in number) + -- PBA 20211230 added interval types to overloading + procedure append_param( + p_params in out nocopy logger.tab_param, + p_name in varchar2, + p_val in interval year to month); + + procedure append_param( + p_params in out nocopy logger.tab_param, + p_name in varchar2, + p_val in interval day to second); + + function ok_to_log(p_level in number, + p_scope in varchar2 default null) return boolean; - function ok_to_log(p_level in varchar2) + function ok_to_log(p_level in varchar2, + p_scope in varchar2 default null) return boolean; function tochar( @@ -352,6 +369,14 @@ as p_val in boolean) return varchar2; + function tochar( + p_val in interval year to month) + return varchar2; + + function tochar( + p_val in interval day to second) + return varchar2; + procedure ins_logger_logs( p_logger_level in logger_logs.logger_level%type, p_text in varchar2 default null, -- Not using type since want to be able to pass in 32767 characters @@ -381,5 +406,11 @@ as function get_plugin_rec( p_logger_level in logger_logs.logger_level%type) return logger.rec_logger_log; + + procedure unset_scope_level; + + -- PBA 20190414 062841 expose this function to be used in testing routine + function get_level_number(p_scope in varchar2 default null) + return number; end logger; / diff --git a/source/scripts/create_logger_public_synonyms.sql b/source/scripts/create_logger_public_synonyms.sql new file mode 100644 index 0000000..f3dae09 --- /dev/null +++ b/source/scripts/create_logger_public_synonyms.sql @@ -0,0 +1,23 @@ +-- Creates public synonyms from defined user for Logger objects + + +set define '&' +set verify off + +-- Parameters +define from_user=LOGGER_USER +accept from_user char default &from_user prompt 'Name of the logger schema [&from_user] :' + + +whenever sqlerror exit sql.sqlcode + +create or replace public synonym logger for &from_user..logger; +create or replace public synonym logger_logs for &from_user..logger_logs; +create or replace public synonym logger_logs_apex_items for &from_user..logger_logs_apex_items; +create or replace public synonym logger_prefs for &from_user..logger_prefs; +create or replace public synonym logger_prefs_by_client_id for &from_user..logger_prefs_by_client_id; +create or replace public synonym logger_logs_5_min for &from_user..logger_logs_5_min; +create or replace public synonym logger_logs_60_min for &from_user..logger_logs_60_min; +create or replace public synonym logger_logs_terse for &from_user..logger_logs_terse; +-- PBA/MNU 3.1.2 +create or replace public synonym logger_prefs_by_scope for &from_user..logger_prefs_by_scope; diff --git a/source/scripts/create_logger_synonyms.sql b/source/scripts/create_logger_synonyms.sql index 22caeab..317846b 100644 --- a/source/scripts/create_logger_synonyms.sql +++ b/source/scripts/create_logger_synonyms.sql @@ -15,3 +15,5 @@ create or replace synonym logger_prefs_by_client_id for &from_user..logger_prefs create or replace synonym logger_logs_5_min for &from_user..logger_logs_5_min; create or replace synonym logger_logs_60_min for &from_user..logger_logs_60_min; create or replace synonym logger_logs_terse for &from_user..logger_logs_terse; +-- PBA/MNU 3.1.2 +create or replace synonym logger_prefs_by_scope for &from_user..logger_prefs_by_scope; diff --git a/source/scripts/grant_logger_to_public.sql b/source/scripts/grant_logger_to_public.sql new file mode 100644 index 0000000..644ad39 --- /dev/null +++ b/source/scripts/grant_logger_to_public.sql @@ -0,0 +1,18 @@ +-- Grants privileges for logger objects from current user to public + + +-- Parameters +-- none + +whenever sqlerror exit sql.sqlcode + +grant execute on logger to public; +grant select, delete on logger_logs to public; +grant select on logger_logs_apex_items to public; +grant select, update on logger_prefs to public; +grant select on logger_prefs_by_client_id to public; +grant select on logger_logs_5_min to public; +grant select on logger_logs_60_min to public; +grant select on logger_logs_terse to public; +-- PBA/MNU 3.1.2 +grant select, insert, update, delete on logger_prefs_by_scope to public; diff --git a/source/scripts/grant_logger_to_user.sql b/source/scripts/grant_logger_to_user.sql index 813447d..0e12ee0 100644 --- a/source/scripts/grant_logger_to_user.sql +++ b/source/scripts/grant_logger_to_user.sql @@ -15,3 +15,6 @@ grant select on logger_prefs_by_client_id to &to_user; grant select on logger_logs_5_min to &to_user; grant select on logger_logs_60_min to &to_user; grant select on logger_logs_terse to &to_user; +-- PBA/MNU 3.1.2 +grant select, insert, update, delete on logger_prefs_by_scope to &to_user; + diff --git a/source/scripts/logger_AA_prefs.sql b/source/scripts/logger_AA_prefs.sql new file mode 100644 index 0000000..f7d020c --- /dev/null +++ b/source/scripts/logger_AA_prefs.sql @@ -0,0 +1,18 @@ +clear screen +set serveroutput on size unlimited + +begin + insert into logger_prefs(pref_name, pref_value, pref_type) values ('LOGGER_AA_SIZE','100','LOGGER_AA'); + commit; +end; +/ +begin + insert into logger_prefs(pref_name, pref_value, pref_type) values ('LOGGER_AA_PREFIX','#','LOGGER_AA'); + commit; +end; +/ +begin + insert into logger_prefs(pref_name, pref_value, pref_type) values ('LOGGER_AA_SUFFIX','#','LOGGER_AA'); + commit; +end; +/ diff --git a/source/tables/logger_prefs_by_scope.sql b/source/tables/logger_prefs_by_scope.sql new file mode 100644 index 0000000..8e50f8e --- /dev/null +++ b/source/tables/logger_prefs_by_scope.sql @@ -0,0 +1,44 @@ +-- Initial table script built from 3.1.2 +declare + l_count pls_integer; + +begin + -- Create Table + select count(1) + into l_count + from user_tables + where table_name = 'LOGGER_PREFS_BY_SCOPE'; + + if l_count = 0 then + execute immediate q'! +create table logger_prefs_by_scope( + logger_scope varchar2(512) not null, + logger_level varchar2(20) not null, + created_date date default sysdate not null, + expiry_date date default sysdate+1/24 not null, + constraint logger_prefs_by_scope_pk primary key (logger_scope) enable, + constraint logger_prefs_by_scope_ck1 check (logger_level in ('OFF','PERMANENT','ERROR','WARNING','INFORMATION','DEBUG','TIMING', 'APEX', 'SYS_CONTEXT')), + constraint logger_prefs_by_scope_ck2 check (expiry_date >= created_date) +)!'; + end if; + +-- Add comments to the table + execute immediate q'!comment on table LOGGER_PREFS_BY_SCOPE is 'Scope specific logger levels. Only active scopes/logger_levels will be maintained in this table'!'; +-- Add comments to the columns + execute immediate q'!comment on column LOGGER_PREFS_BY_SCOPE.LOGGER_SCOPE is 'Scope. Wildcards allowed. Interpreted as LIKE pattern'!'; + execute immediate q'!comment on column LOGGER_PREFS_BY_SCOPE.LOGGER_LEVEL is 'Logger level. Must be OFF, PERMANENT, ERROR, WARNING, INFORMATION, DEBUG, TIMING'!'; + execute immediate q'!comment on column LOGGER_PREFS_BY_SCOPE.CREATED_DATE is 'Date that entry was created on'!'; + execute immediate q'!comment on column LOGGER_PREFS_BY_SCOPE.expiry_date is 'After the given expiry date the logger_level will be disabled for the specific client_id. Unless sepcifically removed from this table a job will clean up old entries'!'; +end; +/ +create or replace trigger biu_logger_prefs_by_scope + before insert or update on logger_prefs_by_scope + for each row +begin + $if $$logger_no_op_install $then + null; + $else + :new.logger_scope := lower(:new.logger_scope); + $end +end biu_logger_prefs_by_scope; +/