From 68c7b44b2f1da7524f33c358f8a7e13da1a09893 Mon Sep 17 00:00:00 2001 From: Patrick Barel Date: Fri, 31 May 2019 08:28:13 +0200 Subject: [PATCH] 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; +/