From 68c7b44b2f1da7524f33c358f8a7e13da1a09893 Mon Sep 17 00:00:00 2001 From: Patrick Barel Date: Fri, 31 May 2019 08:28:13 +0200 Subject: [PATCH 1/3] Use level setting per scope The log level can be set per scope. This specific log level will be used regardsless of the main log level. The specific log level can be higher or lower than the generic level --- source/jobs/logger_unset_prefs_by_scope.sql | 22 ++++ source/packages/logger.pkb | 116 ++++++++++++++---- source/packages/logger.pks | 65 ++++++---- .../scripts/create_logger_public_synonyms.sql | 23 ++++ source/scripts/create_logger_synonyms.sql | 2 + source/scripts/grant_logger_to_public.sql | 18 +++ source/scripts/grant_logger_to_user.sql | 3 + source/tables/logger_prefs_by_scope.sql | 44 +++++++ 8 files changed, 241 insertions(+), 52 deletions(-) create mode 100644 source/jobs/logger_unset_prefs_by_scope.sql create mode 100644 source/scripts/create_logger_public_synonyms.sql create mode 100644 source/scripts/grant_logger_to_public.sql create mode 100644 source/tables/logger_prefs_by_scope.sql 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..00f35e3 100644 --- a/source/packages/logger.pkb +++ b/source/packages/logger.pkb @@ -58,6 +58,7 @@ as 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'; @@ -440,14 +441,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 +467,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 @@ -929,15 +931,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 +964,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 +1008,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 +1199,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, @@ -1254,7 +1272,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, @@ -1294,7 +1313,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, @@ -1364,7 +1384,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, @@ -1436,7 +1457,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, @@ -1525,7 +1547,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 +1589,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 +1630,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 +1676,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 +1781,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 +1837,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 +1894,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 +1965,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 +1981,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 +2001,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 @@ -2952,5 +2999,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..688cdeb 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; @@ -107,8 +109,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 +144,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 +192,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 +219,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 +243,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 +261,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 +325,12 @@ as p_name in varchar2, p_val in boolean); - function ok_to_log(p_level in number) + 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( @@ -381,5 +386,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/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; +/ From bfd562c22118f8d516203abc096907df8735a35c Mon Sep 17 00:00:00 2001 From: Patrick Barel Date: Wed, 14 Apr 2021 15:39:46 +0200 Subject: [PATCH 2/3] Logging in an AA If the log threshold is set to a level lower than the level currently requested, then the log item is written to an Associative Array (with a definable maximum number of items) which is dumped in the table when an error occurs. --- source/packages/logger.pkb | 474 ++++++++++++++++++++++++++++- source/packages/logger.pks | 16 + source/scripts/logger_AA_prefs.sql | 18 ++ 3 files changed, 504 insertions(+), 4 deletions(-) create mode 100644 source/scripts/logger_AA_prefs.sql diff --git a/source/packages/logger.pkb b/source/packages/logger.pkb index 00f35e3..7aecf8e 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,7 +73,7 @@ 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_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'; @@ -78,7 +96,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 @@ -430,6 +450,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),logger.g_default_logger_aa_size); + + return greatest(l_size, 0); + $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),logger.g_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),logger.g_default_logger_aa_suffix); + + return l_suffix; + $end + end get_logger_aa_suffix; /** * * @@ -608,6 +746,193 @@ as 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 20200322 Check if the AA has at least one (1) item + if g_internal_log.count >= 1 then + -- 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 if; + $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; + l_aa_size integer; + begin + -- fetch the current AA size into a variable because we need it more than once + l_aa_size := get_logger_aa_size; + if l_aa_size > 0 then + $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 > l_aa_size then + -- then delete the first one + g_internal_log.delete(g_internal_log.first); + end if; + 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 * @@ -805,6 +1130,110 @@ as -- **** PUBLIC **** + /** + * Sets the AA Size + * + * Notes: + * - + * + * @author Patrick Barel + * @created 2020-03-22 + * + * @param p_size_in Valid values: Numeric, 0 or greater + */ + procedure set_AA_size( + p_size_in in varchar2 default logger.g_default_logger_aa_size + ) + is + pragma autonomous_transaction; + begin + $if $$no_op $then + raise_application_error (-20000, + 'Either the NO-OP version of Logger is installed or it is compiled for NO-OP, so you cannot set the level.'); + $else + assert(is_number(p_size_in), 'Size must be numeric'); + assert(to_number(p_size_in) >= 0, 'Size must be greater than or equal to 0'); + + -- Global settings + update logger_prefs + set pref_value = p_size_in + where 1=1 + and pref_type = logger.g_pref_type_logger_aa + and pref_name = gc_pref_logger_aa_size; + + $end + commit; + end set_AA_size; + + /** + * Sets the AA Size + * + * Notes: + * - + * + * @author Patrick Barel + * @created 2020-03-22 + * + * @param p_size_in Valid values: Numeric, 0 or greater + */ + procedure set_AA_prefix( + p_prefix in varchar2 default logger.g_default_logger_aa_prefix + ) + is + pragma autonomous_transaction; + begin + $if $$no_op $then + raise_application_error (-20000, + 'Either the NO-OP version of Logger is installed or it is compiled for NO-OP, so you cannot set the level.'); + $else +-- assert(is_number(p_size_in), 'Size must be numeric'); +-- assert(to_number(p_size_in) >= 0, 'Size must be greater than or equal to 0'); + + -- Global settings + update logger_prefs + set pref_value = p_prefix + where 1=1 + and pref_type = logger.g_pref_type_logger_aa + and pref_name = gc_pref_logger_aa_prefix; + + $end + commit; + end set_AA_prefix; + + /** + * Sets the AA Size + * + * Notes: + * - + * + * @author Patrick Barel + * @created 2020-03-22 + * + * @param p_size_in Valid values: Numeric, 0 or greater + */ + procedure set_AA_suffix( + p_suffix in varchar2 default logger.g_default_logger_aa_size + ) + is + pragma autonomous_transaction; + begin + $if $$no_op $then + raise_application_error (-20000, + 'Either the NO-OP version of Logger is installed or it is compiled for NO-OP, so you cannot set the level.'); + $else +-- assert(is_number(p_size_in), 'Size must be numeric'); +-- assert(to_number(p_size_in) >= 0, 'Size must be greater than or equal to 0'); + + -- Global settings + update logger_prefs + set pref_value = p_suffix + where 1=1 + and pref_type = logger.g_pref_type_logger_aa + and pref_name = gc_pref_logger_aa_suffix; + + $end + commit; + end set_AA_suffix; /** * Sets all the contexts to null @@ -1240,6 +1669,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 @@ -1283,6 +1714,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; @@ -1322,6 +1761,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; @@ -1393,6 +1841,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; @@ -1466,6 +1923,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; diff --git a/source/packages/logger.pks b/source/packages/logger.pks index 688cdeb..f59da27 100644 --- a/source/packages/logger.pks +++ b/source/packages/logger.pks @@ -77,6 +77,10 @@ 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 + g_default_logger_aa_size constant varchar2(100) := '100'; -- PBA 20200316 + g_default_logger_aa_prefix constant varchar2(100) := ''; -- PBA 20200316 + g_default_logger_aa_suffix constant varchar2(100) := ''; -- PBA 20200316 -- Expose private functions only for testing during development $if $$logger_debug $then @@ -131,6 +135,18 @@ as -- PROCEDURES and FUNCTIONS + procedure set_AA_size( + p_size_in in varchar2 default logger.g_default_logger_aa_size + ); + + procedure set_AA_prefix( + p_prefix in varchar2 default logger.g_default_logger_aa_prefix + ); + + procedure set_AA_suffix( + p_suffix in varchar2 default logger.g_default_logger_aa_size + ); + procedure null_global_contexts; function convert_level_char_to_num( 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; +/ From f95c23200a355bf809dd4135458d515c34d42321 Mon Sep 17 00:00:00 2001 From: Patrick Barel Date: Wed, 14 Apr 2021 15:40:30 +0200 Subject: [PATCH 3/3] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c0d261f..25c9ac9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ releases/* temp/* #For IMDone Atom.io plugin .imdone/* +Sandbox/test1.tst