├── .gitignore ├── LICENSE ├── README.md ├── build └── build.sh ├── demos ├── demo_large_string.sql ├── demo_ok_to_log.sql ├── demo_params.sql ├── demo_plugin.sql └── demo_set_level.sql ├── docs ├── Addons.md ├── Best Practices.md ├── Change Logs.md ├── Development Guide.md ├── Installation.md ├── Logger API Template.md ├── Logger API.md ├── Plugins.md └── README.md ├── releases ├── logger_1.2.0.zip ├── logger_1.2.2.zip ├── logger_1.3.0.zip ├── logger_1.4.0.zip ├── logger_2.0.0.zip ├── logger_2.1.0.zip ├── logger_2.1.1.zip ├── logger_2.1.2.zip ├── logger_3.0.0.zip ├── logger_3.1.0.zip └── logger_3.1.1.zip ├── source ├── contexts │ └── logger_context.sql ├── gen_no_op.sql ├── install │ ├── create_user.sql │ ├── drop_logger.sql │ ├── logger_install_prereqs.sql │ └── post_install_configuration.sql ├── jobs │ ├── logger_purge_job.sql │ └── logger_unset_prefs_by_client.sql ├── packages │ ├── logger.pkb │ ├── logger.pks │ ├── logger_test.pkb │ └── logger_test.pks ├── procedures │ └── logger_configure.plb ├── scripts │ ├── create_logger_synonyms.sql │ └── grant_logger_to_user.sql ├── tables │ ├── logger_logs.sql │ ├── logger_logs_apex_items.sql │ ├── logger_prefs.sql │ └── logger_prefs_by_client_id.sql └── views │ ├── logger_logs_5_min.sql │ ├── logger_logs_60_min.sql │ └── logger_logs_terse.sql └── tests ├── logger_log_load_test.sql └── logger_params.sql /.gitignore: -------------------------------------------------------------------------------- 1 | *.orig 2 | _tmp* 3 | source/packages/logger_no_op.pkb 4 | videos 5 | #Only accept the zip files in the release directory 6 | releases/* 7 | !releases/*.zip 8 | #For developers 9 | temp/* 10 | #For IMDone Atom.io plugin 11 | .imdone/* 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 OraOpenSource 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Donate to Logger 2 | 3 | - [What is Logger?](#what-is-logger) 4 | - [Documentation](#documentation) 5 | - [Download](#download) 6 | - [Change Log](#change-log) 7 | - [License](#license) 8 | 9 | # What is Logger? 10 | 11 | Logger is a PL/SQL logging and debugging framework. The goal of logger is to be as simple as possible to install and use. The primary use cases for this utility include: 12 | 13 | * **Debugging**: It's often difficult to track down the source of an error without some form of debugging instrumentation. This is particularly true in multi-tier, stateless architectures such as Application Express. 14 | * **Error Logging**: While most experts agree that it's important not to mask errors, it's also nice to have a persistent record of them. 15 | * **Timing: Logger** has a very simple timing framework built-in that makes it easy to benchmark sections of code. 16 | * **Instrumentation**: Because it's easy to "turn-off" logger globally with virtually no performance impact, it's easy to get in the habit of leaving debug calls in production code. Now, when something does go wrong, you simply flip the switch and logger is enabled making it much quicker to debug errors. 17 | 18 | ## Feedback/Issues 19 | Please submit any feedback, suggestions, or issues on the project's [issue page](https://github.com/oraopensource/logger/issues). 20 | 21 | ## Demo 22 | ```sql 23 | exec logger.log('hello world'); 24 | 25 | select * from logger_logs; 26 | -- This will display all the logged logs 27 | ``` 28 | 29 | See the [Logger API](docs/Logger API.md) documentation for complete set of procedures. 30 | 31 | # Documentation 32 | In order to keep this page relatively small and for ease of use, the documentation has been moved to the [Logger Docs](docs). In there you you will find the following sections: 33 | 34 | - [Installation](docs/Installation.md) 35 | - [Logger API](docs/Logger%20API.md) 36 | - [Plugins](docs/Plugins.md) 37 | - [Best Practices](docs/Best%20Practices.md) 38 | - [Development Guide](docs/Development%20Guide.md) 39 | - [3rd Party Addons](docs/Addons.md) 40 | 41 | # Download 42 | It is recommended that you download a certified release (from the [releases](https://github.com/OraOpenSource/Logger/tree/master/releases) folder). The files in the current repository are for the next release and should be considered unstable. 43 | 44 | # Change Log 45 | The [Change Logs](docs/Change Logs.md) contain all the major updates for each release. Complete set of issues can be found on [Milestones](https://github.com/OraOpenSource/Logger/milestones?state=closed) page. 46 | 47 | # History 48 | Logger was originally created by [Tyler Muth](https://twitter.com/tmuth) and is now maintained by [OraOpenSource](http://www.oraopensource.com). 49 | 50 | # License 51 | This project is uses the [MIT license](LICENSE). 52 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | ##!/cygdrive/c/cygwin/bin/bash 2 | #!/bin/bash 3 | 4 | #*** PARAMETERS *** 5 | # $1 = VERSION_NUMBER x.x.x (ex 1.0.5) 6 | # This will be used to generate the release folder etc 7 | # OLD: #read -p "New Version Number x.x.x ?" VERSION_NUMBER 8 | 9 | VERSION_NUMBER=$1 10 | SQL_CONNECTION=$2 11 | INCLUDE_RELEASE_FOLDER=$3 12 | 13 | { 14 | if [ -z "${VERSION_NUMBER}" ]; then 15 | echo "VERSION_NUMBER (parameter 1) is not defined" 16 | exit 0 17 | fi 18 | if [ -z "${SQL_CONNECTION}" ]; then 19 | echo "SQL_CONNECTION (parameter 2) is not defined" 20 | exit 0 21 | fi 22 | 23 | #91: Allow option to include release folder or not. Useful for developers, but should be excluded by default 24 | #Upper value 25 | INCLUDE_RELEASE_FOLDER=$(echo $INCLUDE_RELEASE_FOLDER | awk '{print toupper($0)}') 26 | if [ "$INCLUDE_RELEASE_FOLDER" = "" ]; then 27 | INCLUDE_RELEASE_FOLDER=N 28 | fi 29 | echo "INCLUDE_RELEASE_FOLDER: $INCLUDE_RELEASE_FOLDER" 30 | } 31 | 32 | echo "Building release $VERSION_NUMBER" 33 | 34 | 35 | #Generate no_op code 36 | START_DIR="$PWD" 37 | cd ../source/packages 38 | sqlplus $SQL_CONNECTION @../gen_no_op.sql 39 | cd $START_DIR 40 | 41 | 42 | #*** VARIABLES *** 43 | RELEASE_FOLDER=../releases/$VERSION_NUMBER 44 | INSTALL=$RELEASE_FOLDER/"logger_install.sql" 45 | NO_OP=$RELEASE_FOLDER/"logger_no_op.sql" 46 | 47 | 48 | #Clear release folder (if it exists) and make directory 49 | rm -rf ../releases/$VERSION_NUMBER 50 | mkdir ../releases/$VERSION_NUMBER 51 | 52 | 53 | #Build files 54 | 55 | #rm -f ../build/logger_install.sql 56 | #rm -f ../build/logger_latest.zip 57 | #rm -f ../build/logger_no_op.sql 58 | 59 | #PREINSTALL 60 | cat ../source/install/logger_install_prereqs.sql > $INSTALL 61 | printf '\n' >> $INSTALL 62 | 63 | #NO OP Code 64 | printf "\x2d\x2d This file installs a NO-OP version of the logger package that has all of the same procedures and functions,\n" > $NO_OP 65 | printf "\x2d\x2d but does not actually write to any tables. Additionally, it has no other object dependencies.\n" >> $NO_OP 66 | printf "\x2d\x2d You can review the documentation at https://github.com/OraOpenSource/Logger for more information.\n" >> $NO_OP 67 | printf '\n' >> $NO_OP 68 | #Seeting compilation flag so that tables are just skeletons (no triggers, sequences, etc) 69 | printf "alter session set plsql_ccflags='logger_no_op_install:true' \n/ \n\n\n" >> $NO_OP 70 | 71 | 72 | 73 | #78: Need to build tables for no_op to reference 74 | #TABLES - Output for both regular and NO_OP 75 | printf 'PROMPT tables/logger_logs.sql \n' | tee -a $INSTALL $NO_OP > /dev/null 76 | cat ../source/tables/logger_logs.sql | tee -a $INSTALL $NO_OP > /dev/null 77 | printf '\n' | tee -a $INSTALL $NO_OP > /dev/null 78 | printf 'PROMPT tables/logger_prefs.sql \n' | tee -a $INSTALL $NO_OP > /dev/null 79 | cat ../source/tables/logger_prefs.sql | tee -a $INSTALL $NO_OP > /dev/null 80 | printf '\n' | tee -a $INSTALL $NO_OP > /dev/null 81 | printf 'PROMPT tables/logger_logs_apex_items.sql \n' | tee -a $INSTALL $NO_OP > /dev/null 82 | cat ../source/tables/logger_logs_apex_items.sql | tee -a $INSTALL $NO_OP > /dev/null 83 | printf '\n' | tee -a $INSTALL $NO_OP > /dev/null 84 | printf 'PROMPT tables/logger_prefs_by_client_id.sql \n' | tee -a $INSTALL $NO_OP > /dev/null 85 | cat ../source/tables/logger_prefs_by_client_id.sql | tee -a $INSTALL $NO_OP > /dev/null 86 | printf '\n' | tee -a $INSTALL $NO_OP > /dev/null 87 | 88 | #JOBS 89 | printf 'PROMPT jobs/logger_purge_job.sql \n' >> $INSTALL 90 | cat ../source/jobs/logger_purge_job.sql >> $INSTALL 91 | printf '\n' >> $INSTALL 92 | printf 'PROMPT jobs/logger_unset_prefs_by_client.sql \n' >> $INSTALL 93 | cat ../source/jobs/logger_unset_prefs_by_client.sql >> $INSTALL 94 | printf '\n' >> $INSTALL 95 | 96 | #VIEWS - Output for both regular and NO_OP 97 | printf 'PROMPT views/logger_logs_5_min.sql \n' | tee -a $INSTALL $NO_OP > /dev/null 98 | cat ../source/views/logger_logs_5_min.sql | tee -a $INSTALL $NO_OP > /dev/null 99 | printf '\n' | tee -a $INSTALL $NO_OP > /dev/null 100 | printf 'PROMPT views/logger_logs_60_min.sql \n' | tee -a $INSTALL $NO_OP > /dev/null 101 | cat ../source/views/logger_logs_60_min.sql | tee -a $INSTALL $NO_OP > /dev/null 102 | printf '\n' | tee -a $INSTALL $NO_OP > /dev/null 103 | printf 'PROMPT views/logger_logs_terse.sql\n' | tee -a $INSTALL $NO_OP > /dev/null 104 | cat ../source/views/logger_logs_terse.sql | tee -a $INSTALL $NO_OP > /dev/null 105 | printf '\n' | tee -a $INSTALL $NO_OP > /dev/null 106 | 107 | #PACKAGES 108 | printf 'PROMPT packages/logger.pks \n' >> $INSTALL 109 | cat ../source/packages/logger.pks >> $INSTALL 110 | printf '\n' >> $INSTALL 111 | printf 'PROMPT packages/logger.pkb \n' >> $INSTALL 112 | cat ../source/packages/logger.pkb >> $INSTALL 113 | printf '\n' >> $INSTALL 114 | 115 | 116 | #Recompile logger_prefs trigger as it has dependencies on logger.pks 117 | printf 'PROMPT Recompile biu_logger_prefs after logger.pkb \n' >> $INSTALL 118 | printf '\nalter trigger biu_logger_prefs compile;\n' | tee -a $INSTALL $NO_OP > /dev/null 119 | 120 | #CONTEXTS 121 | printf 'PROMPT contexts/logger_context.sql \n' >> $INSTALL 122 | cat ../source/contexts/logger_context.sql >> $INSTALL 123 | printf '\n' >> $INSTALL 124 | 125 | #PROCEDURES 126 | printf 'PROMPT procedures/logger_configure.plb \n' >> $INSTALL 127 | cat ../source/procedures/logger_configure.plb >> $INSTALL 128 | printf '\n' >> $INSTALL 129 | 130 | 131 | #Post install 132 | printf 'PROMPT install/post_install_configuration.sql \n' >> $INSTALL 133 | cat ../source/install/post_install_configuration.sql >> $INSTALL 134 | printf '\n' >> $INSTALL 135 | 136 | 137 | 138 | #NO OP Code 139 | printf '\n\nprompt *** logger.pks *** \n\n' >> $NO_OP 140 | cat ../source/packages/logger.pks >> $NO_OP 141 | printf '\n\nprompt *** logger.pkb *** \n\n' >> $NO_OP 142 | cat ../source/packages/logger_no_op.pkb >> $NO_OP 143 | printf '\n\nprompt\n' >> $NO_OP 144 | printf 'prompt *************************************************\n' >> $NO_OP 145 | printf 'prompt Now executing LOGGER.STATUS...\n' >> $NO_OP 146 | printf 'prompt ' >> $NO_OP 147 | printf '\nbegin \n\tlogger.status; \nend;\n/\n\n' >> $NO_OP 148 | printf 'prompt *************************************************\n' >> $NO_OP 149 | printf '\n\n' >> $NO_OP 150 | 151 | 152 | 153 | #Recompile logger_logs_terse since it depends on logger 154 | printf '\nalter view logger_logs_terse compile;\n' | tee -a $INSTALL $NO_OP > /dev/null 155 | 156 | #Copy "other" scripts 157 | cp -f ../source/install/create_user.sql $RELEASE_FOLDER 158 | cp -f ../source/install/drop_logger.sql $RELEASE_FOLDER 159 | 160 | #Copy Scripts 161 | cp -r ../source/scripts $RELEASE_FOLDER 162 | 163 | #Copy main package file for developers to easily review 164 | cp -f ../source/packages/logger.* $RELEASE_FOLDER 165 | 166 | #Copy README 167 | cp -f ../README.md $RELEASE_FOLDER 168 | 169 | #Copy demo scripts 170 | cp -fr ../demos $RELEASE_FOLDER 171 | 172 | #Copy docs #89 173 | cp -fr ../docs $RELEASE_FOLDER 174 | 175 | #Copy License 176 | cp -f ../LICENSE $RELEASE_FOLDER 177 | 178 | 179 | chmod 777 $RELEASE_FOLDER/*.* 180 | 181 | 182 | #Replace any references for the version number 183 | sed -i.del "s/x\.x\.x/$VERSION_NUMBER/g" $RELEASE_FOLDER/logger_install.sql 184 | sed -i.del "s/x\.x\.x/$VERSION_NUMBER/g" $RELEASE_FOLDER/logger.pks 185 | #need to remove the backup file required for sed call 186 | rm -rf $RELEASE_FOLDER/*.del 187 | 188 | 189 | 190 | #Old windows zip7za a -tzip $/logger_$VERSION_NUMBER.zip ../build/*.sql ../build/*.html 191 | #By CDing into the release_folder we don't get the full path in the zip file 192 | cd $RELEASE_FOLDER 193 | zip -r logger_$VERSION_NUMBER.zip . 194 | 195 | #91: Copy zip to release root 196 | cp -f logger_$VERSION_NUMBER.zip ../. 197 | 198 | #Remove release folder if appliable 199 | if [ "$INCLUDE_RELEASE_FOLDER" != "Y" ]; then 200 | echo Removing release folder 201 | cd $START_DIR 202 | rm -rf $RELEASE_FOLDER 203 | else 204 | echo Keeping release folder 205 | fi 206 | -------------------------------------------------------------------------------- /demos/demo_large_string.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_scope logger_logs.scope%type := 'demo.large_string'; 3 | 4 | l_str varchar2(32767); 5 | l_str_length pls_integer := 4001; 6 | begin 7 | 8 | -- Build string size 9 | for i in 1..l_str_length loop 10 | l_str := l_str || 'a'; 11 | end loop; 12 | 13 | logger.log(l_str, l_scope); 14 | 15 | end; 16 | / 17 | 18 | select * 19 | from logger_logs 20 | where 1=1 21 | and scope = 'demo.large_string' 22 | order by 1 desc; 23 | -------------------------------------------------------------------------------- /demos/demo_ok_to_log.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_log boolean; 3 | l_log_str varchar2(255); 4 | begin 5 | 6 | l_log := logger.ok_to_log(logger.g_warning); -- Just change this with the level you want to test 7 | 8 | if l_log then 9 | l_log_str := 'TRUE'; 10 | else 11 | l_log_str := 'FALSE'; 12 | end if; 13 | 14 | dbms_output.put_line('Ok to log? ' || l_log_str); 15 | 16 | end; 17 | / 18 | -------------------------------------------------------------------------------- /demos/demo_params.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_scope logger_logs.scope%type := 'demo.params'; 3 | 4 | -- Simulates parameters 5 | l_num number := 1; 6 | l_date date := sysdate; 7 | l_boolean boolean := false; 8 | 9 | l_params logger.tab_param; 10 | begin 11 | logger.append_param(l_params, 'l_num', l_num); 12 | logger.append_param(l_params, 'l_date', l_date); 13 | logger.append_param(l_params, 'l_boolean', l_boolean); 14 | 15 | logger.log('START', l_scope, null, l_params); 16 | 17 | 18 | logger.log('END', l_scope); 19 | end; 20 | / 21 | 22 | 23 | 24 | -- Look at the EXTRA column in the Start log 25 | select * 26 | from logger_logs 27 | where 1=1 28 | and scope = 'demo.params' 29 | order by 1 desc; 30 | 31 | 32 | -------------------------------------------------------------------------------- /demos/demo_plugin.sql: -------------------------------------------------------------------------------- 1 | set serveroutput on 2 | 3 | create or replace procedure log_test_plugin( 4 | p_rec in logger.rec_logger_log) 5 | as 6 | l_text logger_logs.text%type; 7 | begin 8 | dbms_output.put_line('In Plugin'); 9 | 10 | logger.log_error('Wont call plugin since recursion / infinite loop would occur'); 11 | end; 12 | / 13 | 14 | exec logger.set_level(p_level => logger.g_debug); 15 | 16 | update logger_prefs 17 | set pref_value = 'log_test_plugin' 18 | where 1=1 19 | and pref_type = 'LOGGER' 20 | and pref_name = 'PLUGIN_FN_ERROR'; 21 | 22 | exec logger_configure; 23 | 24 | 25 | declare 26 | begin 27 | logger.log_error('test'); 28 | end; 29 | / 30 | -------------------------------------------------------------------------------- /demos/demo_set_level.sql: -------------------------------------------------------------------------------- 1 | -- Logger demo file 2 | 3 | exec logger.set_level(logger.g_debug); 4 | 5 | exec logger.log('test, this should show up'); 6 | 7 | select * 8 | from logger_logs_5_min 9 | order by id desc; 10 | 11 | exec logger.set_level (logger.g_error); 12 | 13 | exec logger.log('test, this should not show up'); 14 | 15 | select * 16 | from logger_logs_5_min 17 | order by id desc; 18 | 19 | -- In a different client run the following 20 | 21 | exec dbms_session.set_identifier('logger_demo_session'); 22 | 23 | exec logger.set_level(logger.g_debug, sys_context('userenv','client_identifier')); 24 | 25 | exec logger.log('test, this should show up for client_id: ' || sys_context('userenv','client_identifier')); 26 | 27 | select * 28 | from logger_logs_5_min 29 | order by id desc; 30 | 31 | -- In main client run the following 32 | 33 | exec logger.log('this should not show up since the global config is error'); 34 | 35 | select * 36 | from logger_logs_5_min 37 | order by id desc; 38 | 39 | 40 | -- In other client clear identifier to return to global config 41 | 42 | exec dbms_session.clear_identifier; 43 | 44 | 45 | -- Unset all client specific level settings 46 | exec logger.unset_client_level_all; 47 | 48 | 49 | 50 | -- TODO move to seperate file? 51 | -- How does this work for batches? 52 | 53 | create or replace procedure run_long_batch( 54 | p_client_id in varchar2, 55 | p_iterations in pls_integer) 56 | as 57 | l_params logger.tab_param; 58 | l_scope logger_logs.scope%type := 'run_long_batch'; 59 | begin 60 | logger.append_param(l_params, 'p_client_id', p_client_id); 61 | logger.append_param(l_params, 'p_iterations', p_iterations); 62 | logger.log('START', l_scope, null, l_params); 63 | 64 | dbms_session.set_identifier(p_client_id); 65 | 66 | for i in 1..p_iterations loop 67 | logger.log('i: ' || i, l_scope); 68 | dbms_lock.sleep(1); 69 | end loop; 70 | 71 | logger.log('END'); 72 | 73 | end run_long_batch; 74 | / 75 | 76 | 77 | -- Setup 78 | begin 79 | delete from logger_logs; 80 | logger.set_level(logger.g_error); -- Simulates Production 81 | logger.unset_client_level_all; 82 | commit; 83 | end; 84 | / 85 | 86 | -- In SQL Plus 87 | begin 88 | run_long_batch(p_client_id => 'in_sqlplus', p_iterations => 50); 89 | end; 90 | / 91 | 92 | 93 | -- In SQL Dev 94 | exec logger.set_level(logger.g_debug, 'in_sqlplus'); 95 | 96 | exec logger.unset_client_level('in_sqlplus'); 97 | 98 | exec logger.set_level(logger.g_debug, 'in_sqlplus'); 99 | 100 | exec logger.unset_client_level('in_sqlplus'); 101 | 102 | select logger_level, line_no, text, time_stamp, scope 103 | from logger_logs 104 | order by id 105 | ; 106 | 107 | -- Reset Logging Level 108 | exec logger.set_level(logger.g_debug); 109 | -------------------------------------------------------------------------------- /docs/Addons.md: -------------------------------------------------------------------------------- 1 | # 3rd Party Addons 2 | 3 | Though we try to pack Logger full of features we can't do everything. The community has really grown around the product and have started to develop their own projects related around Logger. 4 | 5 | This page will serve as a simple listing for such projects. To add your project to the list just update this page and make a pull request. 6 | 7 | 8 | 9 | Name | URL | Description 10 | --- | --- | --- 11 | Template Generator | [github.com/alexnuijten/loggerutil](https://github.com/alexnuijten/loggerutil) | Generates logger code for prebuilt procedures. [Blog article](http://nuijten.blogspot.nl/2015/04/speed-up-development-with-logger.html). 12 | -------------------------------------------------------------------------------- /docs/Best Practices.md: -------------------------------------------------------------------------------- 1 | - [PL/SQL Procedure / Function Template](#plsql-example) 2 | - [Logger Levels Guide](#logger-level-guide) 3 | 4 | 5 | ## PL/SQL Procedure / Function Template 6 | 7 | For packages the recommended practice is as follows: 8 | 9 | ```sql 10 | create or replace package body pkg_example 11 | as 12 | 13 | gc_scope_prefix constant varchar2(31) := lower($$plsql_unit) || '.'; 14 | 15 | /** 16 | * TODO_Comments 17 | * 18 | * Notes: 19 | * - 20 | * 21 | * Related Tickets: 22 | * - 23 | * 24 | * @author TODO 25 | * @created TODO 26 | * @param TODO 27 | * @return TODO 28 | */ 29 | procedure todo_proc_name( 30 | p_param1_todo in varchar2) 31 | as 32 | l_scope logger_logs.scope%type := gc_scope_prefix || 'todo_proc_name'; 33 | l_params logger.tab_param; 34 | 35 | begin 36 | logger.append_param(l_params, 'p_param1_todo', p_param1_todo); 37 | logger.log('START', l_scope, null, l_params); 38 | 39 | ... 40 | -- All calls to logger should pass in the scope 41 | ... 42 | 43 | logger.log('END', l_scope); 44 | exception 45 | when others then 46 | logger.log_error('Unhandled Exception', l_scope, null, l_params); 47 | raise; 48 | end todo_proc_name; 49 | 50 | ... 51 | 52 | end pkg_example; 53 | ``` 54 | 55 | 56 | ## Logger Levels Guide 57 | Logger supports multiple logging levels. This section will provide an outline of recommended situations for calling each level. The procedures are ordered in most frequently used to least frequently used. 58 | 59 | ### Summary 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 |
LevelActionableTarget Audience~%Reference
DebugNoDevelopers90%
InformationNoDevelopers/Business1%
WarningYesDevelopers/Business1%
ErrorYesDevelopers/IT/DBA5%
PermanentNoDevelopers/Business0.5%
99 | 100 | *Actionable* means issues that require follow up by a business unit. 101 | 102 | 103 | ### Debug / Log 104 | 105 | `logger.log` should be used for all developer related content. This can really be anything and everything except for items that require additional investigation. In those situations use the other logging options. 106 | 107 | By default, Logger is configured to delete all `debug` level calls after 7 days. As such, developers are encouraged to log as much as they need to with this option. Using other logging levels may result (depending on the settings) in permanent storage and should not be used as frequently. 108 | 109 | ### Information 110 | `logger.log_info[rmation]` should be used for messages that need to be retained at a higher level than `debug` but are not actionable issues. 111 | 112 | Information logging will vary in each organization but should fall between the rules for `debug` and `warning`. An example is to use it for a long running process to highlight some of the following items: 113 | 114 | - When did the process start 115 | - Major steps/milestones in the process 116 | - Number of rows processed 117 | - When did the process end 118 | 119 | ### Warning 120 | `logger.log_warn[ing]` should be used for non-critical system level / business logic issues that are actionable. If it is a critical issue than an error should be raised and `logger.log_error` should be called. An example would be when a non-critical configuration item is missing. In this case a warning message should be logged stating that the configuration option was not set / mssing and the deafult value that the code is using in place. 121 | 122 | ### Error 123 | `logger.log_error` should be used when a PL/SQL error has occurred. In most cases this is in an exception block. Regardless of any other configuration, `log_error` will store the callstack. Errors are considered actionalble items as an error has occurred and something (code, configuration, server down, etc) needs attention. 124 | 125 | ### Permanent 126 | `logger.log_permanent` should be used for messages that need to be permanently retained. `logger.purge` and `logger.purge_all` will not delete these messages regardless of the `PURGE_MIN_LEVEL` configuration option. Only an implicit delete to `logger_logs` will delete these messages. 127 | 128 | An example would be to use this procedure when updating your application to a new version. At the end of the update you can log that the upgrade was successful and the new version number. This way you can find exactly when all the upgrades occurred on your system. 129 | -------------------------------------------------------------------------------- /docs/Change Logs.md: -------------------------------------------------------------------------------- 1 | This page contains all of Logger's Change Logs. Starting in version 3.0.0 onwards, only major tickets will be listed here. To see a complete list of all the issues for each version, review the appropriate release page. 2 | 3 | 4 | ## Change Log 3.1.1 5 | [Download](https://github.com/OraOpenSource/Logger/raw/master/releases/logger_3.1.1.zip)
6 | [Release Page](https://github.com/OraOpenSource/Logger/issues?utf8=%E2%9C%93&q=milestone%3A%22Release+3.1.1%22)
7 | Release Articles 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Issue#FeatureArticles
136ORA-1031 on install
21 | 22 | 23 | ## Change Log 3.1.0 24 | [Download](https://github.com/OraOpenSource/Logger/raw/master/releases/logger_3.1.0.zip)
25 | [Release Page](https://github.com/OraOpenSource/Logger/issues?utf8=%E2%9C%93&q=milestone%3A%22Release+3.1.0%22+)
26 | Release Articles 27 | - [Logger 3.1.0: Beta](http://www.oraopensource.com/blog/2015/6/1/logger-310-beta) 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
Issue#FeatureArticles
29Allow level to be passed to other functionsAdding Level to Additional Procedures
50Deprecate levels Timing, APEX, and Context.Deprecate Levels Timing, APEX, and Sys Context
54Enhance log_apex_itemslog_apex_items Enhancements
57Secure Logger schema setupRestrict Logger's Access to Your Data
73, 75Bug fix: Timing issues
78Bug fix: NO_OP installer references Logger objectsNO_OP Install Changes
94Best practices guideLogger Level Best Practices
103Custom Preferences
set_cust_pref
del_cust_pref
Custom Preferences
108Grants and Synonyms scriptRestrict Logger's Access to Your Data
109Bug fix: Length doesn't factor multi byte characters
110Bug fix: Level changes don't propagate to sessions with client_identifer
91 | 92 | 93 | 94 | ## Change Log 3.0.0 95 | [Download](https://github.com/OraOpenSource/Logger/raw/master/releases/logger_3.0.0.zip)
96 | [Release Page](https://github.com/OraOpenSource/Logger/issues?q=milestone%3A%22Release+3.0.0%22+)
97 | Release Articles 98 | 99 | - [Logger 3.0.0 EA1](http://www.oraopensource.com/blog/2015/3/14/logger-300-ea1): this contains a list of the top ten features of 3.0.0. 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 |
Issue#FeatureArticles
25Add CLIENT_INFO to logger_logsNew fields CLIENT_INFO and SID
32printf type support for substitution strings (printf)printf support
42ok_to_log takes in both name or number of logger level
43Reference new global constants for names
44Global Constants for Logger Level NamesGlobal Level Names
47Support for both logger level name and number
46Plugins for LoggerPlugins
51Add SID to logger_logsNew fields CLIENT_INFO and SID
56Updated documentation format to use FlatdocFlatdoc Documentation
57Modify build script so that no_op code is auto generated as part of build
59Allow security check to be bypassed for client specific logging level
68Added TOCHAR functionExplicit Conversions Using logger.tochar
80Added log_warn and log_info shortcutslog_warn and log_info shortcuts
88Developer GuideDeveloper's Guide
89Include docs with each release
90Change LicenseNew License
190 | 191 | 192 | ## Change Log 2.1.2 193 | [Download](https://github.com/oraopensource/logger/tree/master/releases/2.1.2)
194 | [Release Page](https://github.com/oraopensource/logger/issues?milestone=6&state=closed)
195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 |
Issue#FeatureArticles
39Bug setting client level multiple times.
41Bug viewing logger.status when client_id is defined.
214 | 215 | 216 | ## Change Log 2.1.1 217 | [Download](https://github.com/oraopensource/logger/tree/master/releases/2.1.1)
218 | [Release Page](https://github.com/oraopensource/logger/issues?milestone=5&state=closed)
219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 |
Issue#FeatureArticles
37Bug compiling logger_configure fixed.
232 | 233 | 234 | 235 | ## Change Log 2.1.0 236 | [Download](https://github.com/oraopensource/logger/tree/master/releases/2.1.0)
237 | [Release Page](https://github.com/oraopensource/logger/issues?milestone=3&state=closed)
238 | Release Articles 239 | - [Logger 2.1.0 - Released!](http://www.talkapex.com/2013/08/logger-210-released.html) 240 | 241 | **Note: Review Issue [#31](https://github.com/oraopensource/logger/issues/31) as it may impact your application if you manually inserted into the *LOGGER\_LOGS* table.** 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 |
Issue#FeatureArticles
17Allow for large text (> 4000 chars) to be passed into p_text parameter. If > 4000 then will be appended to the EXTRA column. In future may not be necessary with 12c large varchar2 column support (see issue #30)Text Length > 4000 Characters
20Restructured documentation: 257 |
    258 |
  • Moving or removing most content from README.md to various wiki pages. This now frees up the main project page with a brief overview of what Logger is.
  • 259 |
  • Generate API document. This will make it easier for developers to quickly see all the different commands Logger has.
  • 260 |
261 |
Documentation Overhaul
24Open ok_to_log as public.ok_to_log
26Fixed error compiling in 10g (or any 11gR1 and below).10g Sequence Fixed
27Generate demo scripts and add them to build. More demo scripts to come during future releases. Avoids the need to regenerate for presentations and learning.Demo Scripts
28Include wiki documents as part of build. Wiki is technically part of a different git repository. By adding documentation with release developers will maintain history on API documentation etc.
31Remove trigger on LOGGER_LOGS. For performance issues and to resolve the large text issue the trigger on LOGGER_LOGS has been dropped. In its place, all insert commands should now use ins_logger_logs.

Performance improvement as part of disabling the trigger. Initial tests show ~15% performance improvement for instances that are using APEX. Part of the speed improvement is how the USER is logged.
ins_logger_logs
290 | 291 | 292 | ## Change Log 2.0.0 293 | [Download](https://github.com/oraopensource/logger/tree/master/releases/2.0.0)
294 | [Release Page](https://github.com/oraopensource/logger/issues?milestone=1&state=closed)
295 | Release Articles 296 | - [Logger 2.0.0 Alpha](http://www.talkapex.com/2013/06/logger-200-released.html) 297 | - [Logger 2.0.0 Beta](http://www.talkapex.com/2013/05/logger-200-beta.html) 298 | - [Logger 2.0.0 - Released!](http://www.talkapex.com/2013/06/logger-200-released.html) 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 |
Issue#FeatureArticles
NewMoved to GitHub and restructured / updated documentation
NewAdded p_params support and append_params functions.Logging Parameters
4Client specific level setting (to enable logging based on client_id)Enable Session Specific Logging
Session Specific Logging - Advanced Features
8New build script which will allow for future versions of logger to be updated. This was built off a 1.4.0 release.
327 | 328 | 329 | ## Change Log 1.4.0 330 | [Download](https://github.com/oraopensource/logger/tree/master/releases/1.4.0) 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 |
Issue#FeatureArticles
FixFixed an issue detecting 11.2 RAC installations
NewAPEX no longer supported from a web-only installation if the schema was provisioned by APEX. Essentially the APEX team removed the "create any context" privelege when provisioning a new workspace, likely for security reasons. I (Tyler) agree with their choice, it unfortunately impacts logger.
349 | 350 | 351 | 352 | ## Change Log 1.3.0 353 | [Download](https://github.com/oraopensource/logger/tree/master/releases/1.3.0) 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 |
Issue#FeatureArticles
FixFixed major flaw in time calculation used in time_start/time_stop
NewChanged implementation of LOG_APEX_ITEMS to use the APEX views so explicit privs on wwv_flow_data are not required. Thanks to Scott Spendolini for this suggestion.
372 | 373 | 374 | ## Change Log 1.2.2 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 |
Issue#FeatureArticles
FixFixed an error with the admin security check reported by John Flack
NewIt is now possible to install logger in multiple schemas as the global context is now prefixed with the schema name. So, the global context name in the LOGGER schema would be LOGGER_LOGCTX and the SCOTT schema would be SCOTT_LOGCTX. Thanks to Bill Wheeling for reporting this one.
393 | 394 | 395 | ## Change Log 1.2.0 396 | [Download](https://github.com/oraopensource/logger/tree/master/releases/1.2.0) 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 |
429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 |
Issue#FeatureArticles
NewPROTECT_ADMIN_PROCS preference which is TRUE by default, protects set_level, purge and purge_all. This means that only someone logged into the schema where logger is installed can call these procedures. The idea is that you could grant execute on logger to other schemas, but want to prevent them from changing the levels or purging the logs.
NewPreference called INCLUDE_CALL_STACK allows you to enable / disable logging of the full call stack for LEVELS greater than ERROR (such as debug). Logging the call stack does take additional resources and also requires additional storage per row. So, you can still read your debug messages, but you simply won't see the full call stack. The value is TRUE by default.
NewCLOB parameter of "P_EXTRA" was added to call LOG... procedures. This populates a CLOB column in LOGGER_LOGS called "EXTRA". This column is also used by several new functions / procedures where the values are relatively large.
Newlogger.log_userenv procedure logs information obtained through sys_context('userenv'...), such as IP Address, NLS info, schema / user information. It's use is documented here.
Newlogger.log_cgi_env procedure grabs all output from owa_util.print_cgi_env and logs it to logger_logs.extra. Useful in debugging some APEX issues. It's use is documented here.
Newlogger.log_character_codes procedure supplements the output of the SQL DUMP() function, great for finding hidden carriage return / line feeds or other non-printable characters.It's use is documented here.
Fixset_level, purge and purge_all so they are now autonomous transactions (thanks Tony).
440 | 441 | 442 | # Template (ignore) 443 | 444 | 445 | ## Change Log TODOVERSION 446 | [Download](TODO_URL)
447 | [Release Page](TODO_URL)
448 | Release Articles 449 | - [TODO_NAME](TOOD_URL) 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 |
Issue#FeatureArticles
TODO_TITLETODOTODO_TITLE
TODO_TITLETODOTODO_TITLE
TODO_TITLETODOTODO_TITLE
TODO_TITLETODOTODO_TITLE
TODO_TITLETODOTODO_TITLE
483 | -------------------------------------------------------------------------------- /docs/Development Guide.md: -------------------------------------------------------------------------------- 1 | ***This document is best viewed in [flatdoc format](http://oraopensource.github.io/flatdoc?repo=logger&path=docs%2FDevelopment+Guide.md)*** 2 | # Logger Developer Guide 3 | This document describes how to develop and build Logger. 4 | 5 | # Developing 6 | When developing with Logger you should work on the main source files, but never in the `releases` folder. The content in there is automatically generated as part of the [build process](#build-process). 7 | 8 | ## Conditional Compilation 9 | Logger uses [conditional compilation](http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/fundamentals.htm#LNPLS00210) to enable/disable features. It is highly recommended that you understand how conditional compilation works before working on Logger. 10 | 11 | When installing Logger, the `plsql_ccflags` are automatically defined in the `logger_configure` procedure. It is not reasonable to constantly run `logger_configure` after each change to the source code. 12 | 13 | An easy way to control the `plsql_ccflags` is manually set them in your current session. This will allow you to quickly test various situations. The following is an example of how to set your session's conditional compilation flags: 14 | 15 | ```sql 16 | alter session set plsql_ccflags = 'no_op:false, logger_debug:true, APEX:true, logger_plugin_error: true'; 17 | ``` 18 | 19 | ## Tables 20 | Logger has one installation script that will either update or install Logger. Because of this notion, any changes to tables must assume that the user can re-run the build script at any time and it will not fail. 21 | 22 | A good example of this, is when adding a new column to a table. The script will check if that column exists. The column will be created only if it does not currently exist. 23 | 24 | ### $$logger_debug PLSQL_CCFLAG 25 | They're some times when you want to debug some code in Logger. There is a catch since you may not want to use Logger to test. Instead, you can add `dbms_output.put_line` statements. If you do add any debug code be sure to wrap in the conditional compilation flag `logger_debug`. Example: 26 | 27 | ```sql 28 | ... 29 | 30 | $if $$logger_debug $then 31 | dbms_output.put_line('testing...'); 32 | $end 33 | ... 34 | ``` 35 | 36 | ### $$no_op 37 | Logger supports the concept of a `no_op` build. For various reasons, primarily performance releated, you may not want to run Logger on a production system. The `no_op` version allows all the references to Logger however nothing will be executed since each method either returns a minimal result or the procedure is just one `null;` statement. 38 | 39 | If developing a new method you must support the `no_op` conditional compilation flag. For examples, look at any of the existing methods. 40 | 41 | When [building](#build-process) a version of logger the `logger_no_op.pkb` installation file will automatically be generated based on the results of the `no_op` conditional compilation flag. 42 | 43 | The generated version of `logger_no_op.pkb` is stored in `source/packages/` and then the build script copies the file over to the `releases` folder as part of the build. There is no need to commit `logger_no_op.pkb` to Git for version control. By default there is a reference to `source/packages/logger_no_op.pkb` in the `.gitignore` file to ignore this from Git checkins. 44 | 45 | 46 | ## Issues 47 | Unless an change is very small, please register an [issue](https://github.com/OraOpenSource/Logger/issues) for it in Github. This way it is easy to reference this issue in the code and we can keep track of all the features in a given release. 48 | 49 | 50 | ## Testing 51 | We plan to implement a test suite in the future which each build must pass in order to be certified. 52 | 53 | 54 | # Building Logger 55 | Logger has a build script which will take all the source files and merge them into installation files in the `releases` folder. The following demonstrates how to build Logger: 56 | 57 | ```bash 58 | #The build script assumes that you run it directly in its folder 59 | cd Logger/build 60 | 61 | #This will create version 3.0.0 and create a 3.0.0 release folder 62 | #More on parameters below 63 | ./build.sh 3.0.0 giffy/giffy@localhost:1521/xe Y 64 | 65 | ``` 66 | 67 | `build.sh` has a few parameters: `./build.sh ` 68 | 69 | - **Version**: Logger uses [Semantic Versioning](http://semver.org/) that follows the `major.minor.patch` numbering system. 70 | - **Connection**: connection string to database that current version is installed on. 71 | - This is required to generate the `logger_no_op` package. 72 | - **Include release folder**: When set to Y, this optional Y/N paramter will create a folder in the `releases` folder with the contents of the release. This is useful when testing builds to see what is included in the `.zip` files. 73 | - You should not commit these subfolders into git as their contents are already found in the `.zip` files. 74 | -------------------------------------------------------------------------------- /docs/Installation.md: -------------------------------------------------------------------------------- 1 | - [Installation](#installation)
2 | - [Restrict Logger Access (Grants & Synonyms)](#restrict-access)
3 | - [Configuration](#configuration)
4 | - [Maintenance](#maintenance) 5 | 6 | 7 | # Installation 8 | 9 | If you're new to Logger it's recommended you simply [install into an existing schema](#install-into-existing-schema) on a development environment to get up and running as quickly as possible. You are encouraged to review the rest of the installation sections after you're more familiar with Logger. Once you are comfortable using Logger it is recommended that you read the [Best Practices](Best Practices.md) section 10 | 11 | ## Important Notes 12 | 13 | ### Previous Installations 14 | Version 2.0.0 build scripts were completely re-written to make it easier for future development. The new build scripts were built off Logger 1.4.0. As such, **if your current version is lower than 1.4.0 you need to run the uninstall script for your specific version**. If you're currently 1.4.0 or above the installation script will automatically update your current version. The following query will identify your current version. 15 | 16 | ```sql 17 | select pref_value 18 | from logger_prefs 19 | where pref_name = 'LOGGER_VERSION'; 20 | ``` 21 | 22 | To uninstall an older version of logger, see the [Uninstall](#uninstall) instructions. If necessary, you can download the correct version from the [releases](https://github.com/oraopensource/logger/tree/master/releases) folder. 23 | 24 | ### Install Through APEX 25 | Logger is no longer supported from a web-only installation if the schema was provisioned by APEX. Essentially the APEX team removed the "create any context" privilege when provisioning a new workspace, likely for security reasons. I agree with their choice, it unfortunately impacts logger. 26 | 27 | 28 | ## Install into a new schema 29 | 30 | 1. Using sql*plus or SQL Developer, connect to the database as system or a user with the DBA role. 31 | 32 | 1. Run: 33 | ```sql 34 | @create_user.sql 35 | ``` 36 | 37 | 1. Enter the username, tablespace, temporary tablespace and password for the new schema. 38 | 39 | 1. Connect to the database as the newly created user. 40 | 41 | 1. Follow the steps to install into an existing schema (below). 42 | 43 | 44 | ## Install into an existing schema: 45 | 1. If possible, connect as a privileged user and issue the following grants to your "existing_user": 46 | ```sql 47 | grant connect,create view, create job, create table, create sequence, 48 | create trigger, create procedure, create any context to existing_user; 49 | ``` 50 | 1. Run: 51 | ```sql 52 | @logger_install.sql 53 | ``` 54 | 55 | 1. Once installed, Logger is automatically set to **DEBUG** level. View the [configurations](#configuration) section to modify its settings. 56 | 57 | 58 | ## NO-OP Option for Production Environments 59 | To make sure there is no fear of leaving debug statements in production code, Logger comes with a [NO-OP](http://en.wikipedia.org/wiki/NOP) (No Operation) installation file (logger_no_op.sql). This installs only a shell of the Logger package. All procedures are essentially NO-OPs. It does not even create the tables so there is absolutely no chance it is doing any logging. It is recommended that you leave the full version installed and simply [set the Logger level](Logger API.md#procedure-set_level) to ERROR as the performance hit is exceptionally small. 60 | 61 | ## Objects 62 | The following database objects are installed with Logger: 63 | 64 | ```sql 65 | OBJECT_TYPE OBJECT_NAME 66 | ------------------- ------------------------------ 67 | JOB LOGGER_PURGE_JOB 68 | LOGGER_UNSET_PREFS_BY_CLIENT 69 | PACKAGE LOGGER 70 | PROCEDURE LOGGER_CONFIGURE 71 | SEQUENCE LOGGER_APX_ITEMS_SEQ 72 | LOGGER_LOGS_SEQ 73 | TABLE LOGGER_LOGS 74 | LOGGER_LOGS_APEX_ITEMS 75 | LOGGER_PREFS 76 | LOGGER_PREFS_BY_CLIENT_ID 77 | VIEW LOGGER_LOGS_5_MIN 78 | LOGGER_LOGS_60_MIN 79 | LOGGER_LOGS_TERSE 80 | LOGGER_GLOBAL_CTX CONTEXT -- Global Application Contexts are owned by SYS 81 | ``` 82 | 83 | 84 | ## Uninstall 85 | To uninstall Logger simple run the following script in the schema that Logger was installed in: 86 | 87 | ```sql 88 | @drop_logger.sql 89 | ``` 90 | 91 | 92 | ## Restrict Access (Grants & Synonyms) 93 | You may want to [install Logger into it's own schema](#install-into-new-schema) for various reasons. Some of the most common ones are: 94 | 95 | - DBA does not want to give `CREATE ANY CONTEXT` access to your user. 96 | - If this is the case, the DBA can then lock the Logger schema after running the grant scripts (below) to prevent any access to the privileged user. 97 | - Restrict Logger to never be able to access your data. *Note: Logger does not try to reference any of your data. Some security policies require that 3rd party solutions can not reside in the same schema as your data. This follows the concept that Logger doesn't need to see your data, your schema just needs access to Logger.* 98 | 99 | Once you have installed Logger into it's own schema they're two additional scripts that need to be run. The first grants the appropriate privileges to your schema and the second will create synonyms in your schema. 100 | 101 | Run as the user with Logger installed: 102 | 103 | ```sql 104 | @scripts/grant_logger_to_user.sql 105 | ``` 106 | 107 | If you want to restrict access to the "Logger Schema" (since it has `CREATE ANY CONTEXT` privilege) you can simple lock it as `SYSTEM`: 108 | 109 | ```sql 110 | alter user account lock; 111 | ``` 112 | 113 | Run as the user that needs to access Logger: 114 | 115 | ```sql 116 | @scripts/create_logger_synonyms.sql 117 | ``` 118 | 119 | 120 | 121 | # Configuration 122 | 123 | 124 | ### Logger Levels 125 | They're various logger levels. To see the complete list, go to the [Constants](Logger API.md#constants) section in the Logger API. 126 | 127 | ### Enable 128 | To enable logging for the entire schema: 129 | ```sql 130 | exec logger.set_level(logger.g_debug); 131 | ``` 132 | 133 | ### Disable 134 | To disable logging: 135 | ```sql 136 | exec logger.set_level(logger.g_off); 137 | ``` 138 | 139 | Instead of disabling all logging, setting the level to "ERROR" might be a better approach: 140 | 141 | ```sql 142 | exec logger.set_level(logger.g_error); 143 | ``` 144 | If you never want logger to run in an environment you can install the [NO-OP](#install-no-op) version. 145 | 146 | 147 | 148 | ### Client Specific Configuration 149 | Logger now supports client specific configuration. For more information and examples view the [Set Logging Level](Logger API.md#set-logging-level) section in the Logger API documentation. 150 | 151 | ### Status 152 | To view the status/configuration of the Logger: 153 | 154 | ```sql 155 | set serveroutput on 156 | exec logger.status 157 | 158 | Project Home Page : https://github.com/oraopensource/logger/ 159 | Logger Version : 2.0.0.a01 160 | Debug Level : DEBUG 161 | Capture Call Stack : TRUE 162 | Protect Admin Procedures : TRUE 163 | APEX Tracing : Disabled 164 | SCN Capture : Disabled 165 | Min. Purge Level : DEBUG 166 | Purge Older Than : 7 days 167 | Pref by client_id expire : 12 hours 168 | For all client info see : logger_prefs_by_client_id 169 | 170 | PL/SQL procedure successfully completed. 171 | ``` 172 | 173 | ### Preferences 174 | Logger stores its configuration settings in LOGGER_PREFS. These are the following preferences: 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 |
PreferenceDescription
GLOBAL_CONTEXT_NAMEContext that Logger uses to save values. It is not recommended to modify this setting.
INCLUDE_CALL_STACKStore the call stack. Note client specific settings can override this.
INSTALL_SCHEMASchema that Logger is installed in. Do not modify.
LEVELThe current schema Logger level.
LOGGER_VERSIONCurrent version of Logger. Do no modify this as it may affect future migrations.
PREF_BY_CLIENT_ID_EXPIRE_HOURSDefault time (in hours) that client specific logging levels are set for.
PROTECT_ADMIN_PROCSIf TRUE then only user, defined in INSTALL_SCHEMA, can run privilidged procedures.
PURGE_AFTER_DAYSPurge logs, equal to or higher than PURGE_MIN_LEVEL, after this many days. A purge job is run each night to clean up logger.
PURGE_MIN_LEVELMin level to purge logs used in auto Logger cleanup job.
218 | 219 | ### Other Options 220 | 221 | Once you perform the following described steps for the Flashback or APEX option, simply run the *logger_configure* procedure, then run *logger.status* to check validate your changes. 222 | 223 | ```sql 224 | exec logger_configure; 225 | exec logger.status; 226 | ``` 227 | 228 | #### Flashback 229 | To enable this option, grant execute on *dbms_flashback* to the user that owns the logger packages. Every insert into *logger_logs* will include the SCN (System Commit Number). This allows you to flashback a session to the time when the error occurred to help debug it or even undo any data corruption. As SYS from sql*plus: 230 | 231 | ```sql 232 | grant execute on dbms_flashback to logger; 233 | ``` 234 | 235 | #### APEX 236 | This option allows you to call logger.log_apex_items which grabs the names and values of all APEX items from the current session and stores them in the logger_logs_apex_items table. This is extremely useful in debugging APEX issues. This option is enabled automatically by logger_configure if APEX is installed in the database. 237 | 238 | 239 | 240 | # Maintenance 241 | 242 | By default, the DBMS\_SCHEDULER job "LOGGER\_PURGE\_JOB" runs every night at 1:00am and deletes any logs older than 7 days that are of error level *g_debug* or higher which includes *g_debug* and *g_timing*. This means logs with any lower level such as *g_error* or *g_permanent* will never be purged. You can also manually purge all logs using *logger.purge_all*, but this will not delete logs of error level *g_permanent*. 243 | 244 | Starting in 2.0.0 a new job was *LOGGER\_UNSET\_PREFS\_BY\_CLIENT* introduced to remove [client specific logging](Logger-API#set-logging-level). By default this job is run every hour on the hour. 245 | -------------------------------------------------------------------------------- /docs/Logger API Template.md: -------------------------------------------------------------------------------- 1 | This is a snippet template to quickly add documentation to the Logger API library. 2 | 3 | - [TODO_PROC_NAME](#procedure-TODO_ANCHOR_LINK) 4 | - [TODO_PROC_NAME](#procedure-TODO_ANCHOR_LINK) 5 | - [TODO_PROC_NAME](#procedure-TODO_ANCHOR_LINK) 6 | 7 | 8 | #### TODO_NAME 9 | TODO_DESC 10 | 11 | ##### Syntax 12 | ```sql 13 | TODO 14 | ``` 15 | 16 | ##### Parameters 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
AttributeDescription
TODOTODO
TODOTODO
TODOTODO
TODOTODO
TODOTODO
43 | 44 | ##### Example 45 | ```sql 46 | TODO 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/Logger API.md: -------------------------------------------------------------------------------- 1 | ***This document is best viewed in [flatdoc format](http://oraopensource.github.io/flatdoc?repo=logger&path=docs%2FLogger+API.md)*** 2 | 3 | # Constants 4 | 5 | 6 | ## General 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
NameDescription
g_logger_versionVersion of Logger.
g_context_nameContext Logger uses for storing attributes.
gc_empty_tab_paramEmpty param used for default value in logger main procedures.
25 | 26 | 27 | ## Logger Levels 28 | For historical purposes, logger levels supports both integers and names which are intergchangble when calling a function that requires a logger level. 29 | 30 | Note: If setting the Logger level to a deprecated level, it will automatically default to `g_debug`. 31 | ### Numeric 32 | This is the preferred method 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
NameDescription
g_offLogger level off (0).
g_permanentLogger level permanent (1).
g_errorLogger level error (2).
g_warningLogger level warning (4).
g_informationLogger level information (8).
g_debugLogger level debug (16).
g_timing*Deprecated* Logger level timing (32).
g_sys_context*Deprecated* Logger level sys context (64). This is applicable for logging system variables.
g_apex*Deprecated* Logger level apex (128).
75 | 76 | ### Name 77 | This will still work, however it is recommended that you use the numeric values. 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
g_off_nameLogger level name: OFF
g_permanent_nameLogger level name: PERMANENT
g_error_nameLogger level name: ERROR
g_warning_nameLogger level name: WARNING
g_information_nameLogger level name: INFORMATION
g_debug_nameLogger level name: DEBUG
g_timing_name*Deprecated* Logger level name: TIMING
g_sys_context_name*Deprecated* Logger level name: SYS_CONTEXT
g_apex_name*Deprecated* Logger level name: APEX
116 | 117 | 118 | ## APEX Item Types 119 | `log_apex_items` takes in an optional variable `p_item_scope`. This determines which items to log in APEX. Use the following global variables as valid vaules. 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |
NameDescription
g_apex_item_type_allLog both application and page level items
g_apex_item_type_appOnly application level items
g_apex_item_type_pageOnly page level items
<page_number>All page items corresponding to the given page will be logged
142 | 143 | 144 | # Types 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 169 | 170 |
NameDescription
rec_param 153 | Consists of:
154 | name (varchar2)
155 | value (varchar2) 156 |
tab_paramArray of rec_param
rec_logger_logConsists of:
165 | id (number)
166 | logger_level (number)
167 |
168 | See Plugins documentation for more information and examples.
171 | 172 | 173 | # Subprograms 174 | 175 | 176 | ## Main Logger Procedures 177 | Since the main Logger procedures all have the same syntax and behavior (except for the procedure names) the documentation has been combined to avoid replication. 178 | 179 | 180 | ### Best Practices 181 | The [Best Practices](Best%20Practices.md#logger-level-guide) guide covers which Logger procedure to use in different circumstances. 182 | 183 | 184 | ### Syntax 185 | The syntax for the main Logger procedures are all the same. 186 | 187 | ```sql 188 | logger.procedure_name( 189 | p_text in varchar2, 190 | p_scope in varchar2 default null, 191 | p_extra in clob default null, 192 | p_params in tab_param default logger.gc_empty_tab_param); 193 | ``` 194 | 195 | 196 | ### Parameters 197 | All of the main Logger procedures have the same parameters 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 222 | 223 |
ParameterDescription
p_textp_text maps to the TEXT column in LOGGER_LOGS. It can handle up to 32767 characters. If p_text exceeds 4000 characters its content will be moved appended to the EXTRA column. If you need to store large blocks of text (i.e. clobs) you can use the p_extra parameter. 207 |
p_scopep_scope is optional but highly recommend. The idea behind scope is to give some context to the log message, such as the application, package.procedure where it was called from. Logger captures the call stack, as well as module and action which are great for APEX logging as they are app number / page number. However, none of these options gives you a clean, consistent way to group messages. The p_scope parameter performs a lower() on the input and stores it in the SCOPE column.
p_extraWhen logging large (over 4000 characters) blocks of text, use the third parameter: p_extra. p_extra is a clob field and thus isn't restricted to the 4000 character limit.
p_paramsp_params is for storing the parameters object. The goal of this parameter is to allow for a simple and consistent method to log the parameters to a given function. The values are explicitly converted to a string so there is no need to convert them when appending a parameter. 220 |

The data from the parameters array will be appended to the EXTRA column.

221 | Since most production instances set the logging level to error, it is highly recommended that you leverage this 4th parameter when calling logger.log_error so that developers know the input that triggered the error.
224 | 225 | 226 | ### Examples 227 | The following code snippet highlights the main Logger procedures. Since they all have the same parameters, this will serve as the general example for all the main Logger procedures. 228 | ```sql 229 | begin 230 | logger.log('This is a debug message. (level = DEBUG)'); 231 | logger.log_information('This is an informational message. (level = INFORMATION)'); 232 | logger.log_warning('This is a warning message. (level = WARNING)'); 233 | logger.log_error('This is an error message (level = ERROR)'); 234 | logger.log_permanent('This is a permanent message, good for upgrades and milestones. (level = PERMANENT)'); 235 | end; 236 | / 237 | 238 | select id, logger_level, text 239 | from logger_logs_5_min 240 | order by id; 241 | 242 | ID LOGGER_LEVEL TEXT 243 | ---- ------------ ------------------------------------------------------------------------------------------ 244 | 10 16 This is a debug message. (level = DEBUG) 245 | 11 8 This is an informational message. (level = INFORMATION) 246 | 12 4 This is a warning message. (level = WARNING) 247 | 13 2 This is an error message (level = ERROR) 248 | 14 1 This is a permanent message, good for upgrades and milestones. (level = PERMANENT) 249 | ``` 250 | 251 | The following example shows how to use the p_params parameter. The parameter values are stored in the EXTRA column. 252 | 253 | ```sql 254 | create or replace procedure p_demo_procedure( 255 | p_empno in emp.empno%type, 256 | p_ename in emp.ename%type) 257 | as 258 | l_scope logger_logs.scope%type := 'p_demo_function'; 259 | l_params logger.tab_param; 260 | begin 261 | logger.append_param(l_params, 'p_empno', p_empno); -- Parameter name and value just stored in PL/SQL array and not logged yet 262 | logger.append_param(l_params, 'p_ename', p_ename); -- Parameter name and value just stored in PL/SQL array and not logged yet 263 | logger.log('START', l_scope, null, l_params); -- All parameters are logged at this point 264 | -- ... 265 | exception 266 | when others then 267 | logger.log_error('Unhandled Exception', l_scope, null, l_params); 268 | end p_demo_procedure; 269 | / 270 | ``` 271 | 272 | 273 | 274 | ### LOG 275 | This procedure will log an entry into the LOGGER\_LOGS table when the logger_level is set to *debug*. See [Main Logger Procedures](#main-logger-procedures) for syntax, parameters, and examples. 276 | 277 | 278 | ### LOG_INFORMATION / LOG_INFO 279 | This procedure will log an entry into the LOGGER\_LOGS table when the logger_level is set to *information*. See [Main Logger Procedures](#main-logger-procedures) for syntax, parameters, and examples. 280 | 281 | ```log_info``` is a shortcut wrapper for ```log_information```. 282 | 283 | 284 | ### LOG_WARNING / LOG_WARN 285 | This procedure will log an entry into the LOGGER\_LOGS table when the logger_level is set to *warning*. See [Main Logger Procedures](#main-logger-procedures) for syntax, parameters, and examples. 286 | 287 | ```log_warn``` is a shortcut wrapper for ```log_warning```. 288 | 289 | 290 | ### LOG_ERROR 291 | This procedure will log an entry into the LOGGER\_LOGS table when the logger_level is set to *error*. See [Main Logger Procedures](#main-logger-procedures) for syntax, parameters, and examples. 292 | 293 | 294 | ### LOG_PERMANENT 295 | This procedure will log an entry into the LOGGER\_LOGS table when the logger_level is set to *permanent*. See [Main Logger Procedures](#main-logger-procedures) for syntax, parameters, and examples. 296 | 297 | 298 | ## Other Logger Procedures 299 | 300 | 301 | ### LOG_USERENV 302 | 303 | There are many occasions when the value of one of the USERENV session variables (Documentation: [Overview](http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/functions172.htm), [list of variables](http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/functions172.htm#g1513460)) is a big step in the right direction of finding a problem. A simple call to the *logger.log_userenv* procedure is all it takes to save them in the EXTRA column of logger_logs. 304 | 305 | *log-userenv* will be logged using the *g\_sys\_context* level. 306 | 307 | #### Syntax 308 | 309 | ```sql 310 | log_userenv( 311 | p_detail_level in varchar2 default 'USER',-- ALL, NLS, USER, INSTANCE, 312 | p_show_null in boolean default false, 313 | p_scope in logger_logs.scope%type default null, 314 | p_level in logger_logs.logger_level%type default null); 315 | ``` 316 | 317 | #### Parameters 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 |
ParameterDescription
p_detail_levelValid values are: ALL, NLS, USER (default), or INSTANCE
p_show_nullIf true, then variables that have no value will still be displayed.
p_scopeScope to log variables under.
p_levelHighest level to run at (default logger.g_debug). Example. If you set to logger.g_error it will work when both in DEBUG and ERROR modes. However if set to logger.g_debug (default) will not store values when level is set to ERROR.
341 | 342 | 343 | #### Example 344 | ```sql 345 | exec logger.log_userenv('NLS'); 346 | 347 | select text,extra from logger_logs_5_min; 348 | 349 | TEXT EXTRA 350 | ---------------------------------------------- ----------------------------------------------------------------- 351 | USERENV values stored in the EXTRA column NLS_CALENDAR : GREGORIAN 352 | NLS_CURRENCY : $ 353 | NLS_DATE_FORMAT : DD-MON-RR 354 | NLS_DATE_LANGUAGE : AMERICAN 355 | NLS_SORT : BINARY 356 | NLS_TERRITORY : AMERICA 357 | LANG : US 358 | LANGUAGE : AMERICAN_AMERICA.WE8MSWIN1252 359 | ``` 360 | 361 | ```sql 362 | exec logger.log_userenv('USER'); 363 | 364 | select text,extra from logger_logs_5_min; 365 | TEXT EXTRA 366 | -------------------------------------------------- ------------------------------------------------------- 367 | USERENV values stored in the EXTRA column CURRENT_SCHEMA : LOGGER 368 | SESSION_USER : LOGGER 369 | OS_USER : tmuth 370 | IP_ADDRESS : 192.168.1.7 371 | HOST : WORKGROUP\TMUTH-LAP 372 | TERMINAL : TMUTH-LAP 373 | AUTHENTICATED_IDENTITY : logger 374 | AUTHENTICATION_METHOD : PASSWORD 375 | ``` 376 | 377 | 378 | ### LOG_CGI_ENV 379 | This option only works within a web session, but it's a great way to quickly take a look at an APEX environment. 380 | 381 | #### Syntax 382 | 383 | ```sql 384 | logger.log_cgi_env( 385 | p_show_null in boolean default false, 386 | p_scope in logger_logs.scope%type default null, 387 | p_level in logger_logs.logger_level%type default null); 388 | ``` 389 | 390 | #### Parameters 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 |
ParameterDescription
p_show_nullIf true, then variables that have no value will still be displayed.
p_scopeScope to log CGI variables under.
p_levelHighest level to run at (default logger.g_debug). Example. If you set to logger.g_error it will work when both in DEBUG and ERROR modes. However if set to logger.g_debug (default) will not store values when level is set to ERROR.
409 | 410 | #### Example 411 | ```sql 412 | exec logger.log_cgi_env; 413 | 414 | select extra from logger_logs where text like '%CGI%'; 415 | TEXT EXTRA 416 | -------------------------------------------------- ------------------------------------------------------- 417 | ... 418 | SERVER_SOFTWARE : Oracle-Application-Server-10g/10.1.3.1.0 Oracle-HTTP-Server 419 | GATEWAY_INTERFACE : CGI/1.1 420 | SERVER_PORT : 80 421 | SERVER_NAME : 11g 422 | REQUEST_METHOD : POST 423 | PATH_INFO : /wwv_flow.show 424 | SCRIPT_NAME : /pls/apex 425 | REMOTE_ADDR : 192.168.1.7 426 | ... 427 | ``` 428 | 429 | 430 | ### LOG_CHARACTER_CODES 431 | Have you ever run into an issue with a string that contains control characters such as carriage returns, line feeds and tabs that are difficult to debug? The sql [dump()](http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/functions048.htm#sthref1340) function is great for this, but the output is a bit hard to read as it outputs the character codes for each character, so you end up comparing the character code to an [ascii table](http://www.asciitable.com/) to figure out what it is. The function get_character_codes and the procedure log_character_codes make it much easier as they line up the characters in the original string under the corresponding character codes from dump. Additionally, all tabs are replaced with "^" and all other control characters such as carriage returns and line feeds are replaced with "~". 432 | 433 | #### Syntax 434 | 435 | ```sql 436 | logger.log_character_codes( 437 | p_text in varchar2, 438 | p_scope in logger_logs.scope%type default null, 439 | p_show_common_codes in boolean default true, 440 | p_level in logger_logs.logger_level%type default null); 441 | ``` 442 | 443 | #### Parameters 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 |
ParameterDescription
p_textText to retrieve character codes for.
p_scopeScope to log text under.
p_show_common_codesProvides legend of common character codes in output.
p_levelHighest level to run at (default logger.g_debug). Example. If you set to logger.g_error it will work when both in DEBUG and ERROR modes. However if set to logger.g_debug (default) will not store values when level is set to ERROR.
466 | 467 | #### Example 468 | ```sql 469 | exec logger.log_character_codes('Hello World'||chr(9)||'Foo'||chr(13)||chr(10)||'Bar'); 470 | 471 | select extra from logger_logs_5_min; 472 | 473 | EXTRA 474 | ---------------------------------------------------------------------------------- 475 | Common Codes: 13=Line Feed, 10=Carriage Return, 32=Space, 9=Tab 476 | 72,101,108,108,111, 32, 87,111,114,108,100, 9, 70,111,111, 13, 10, 66, 97,114 477 | H, e, l, l, o, , W, o, r, l, d, ^, F, o, o, ~, ~, B, a, r 478 | ``` 479 | 480 | 481 | ### LOG_APEX_ITEMS 482 | This feature is useful in debugging issues in an APEX application that are related session state. The developers toolbar in APEX provides a place to view session state, but it won't tell you the value of items midway through page rendering or right before and after an AJAX call to an application process. 483 | 484 | #### Syntax 485 | ```sql 486 | logger.log_apex_items( 487 | p_text in varchar2 default 'Log APEX Items', 488 | p_scope in logger_logs.scope%type default null, 489 | p_item_type in varchar2 default logger.g_apex_item_type_all, 490 | p_log_null_items in boolean default true, 491 | p_level in logger_logs.logger_level%type default null); 492 | ``` 493 | 494 | #### Parameters 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 |
ParameterDescription
p_textText to be added to TEXT column.
p_scopeScope to log text under.
p_item_typeDetermines what type of APEX items are logged (all, application, page). See the corresponding global variables that it can reference. Alternatively it can reference a page_id which will then only log items on the defined page.
p_log_null_itemsIf set to false, null values won't be logged.
p_levelHighest level to run at (default logger.g_debug). Example. If you set to logger.g_error it will work when both in DEBUG and ERROR modes. However if set to logger.g_debug (default) will not store values when level is set to ERROR.
521 | 522 | #### Example 523 | ```sql 524 | -- Include in your APEX code 525 | begin 526 | logger.log_apex_items('Debug Edit Customer'); 527 | end; 528 | ``` 529 | 530 | ```sql 531 | select id,logger_level,text,module,action,client_identifier 532 | from logger_logs 533 | where logger_level = 128; 534 | 535 | ID LOGGER_LEVEL TEXT MODULE ACTION CLIENT_IDENTIFIER 536 | ------- ------------ -------------------- ---------------------- --------- -------------------- 537 | 47 128 Debug Edit Customer APEX:APPLICATION 100 PAGE 7 ADMIN:45588554040361 538 | 539 | select * 540 | from logger_logs_apex_items 541 | where log_id = 47; --log_id relates to logger_logs.id 542 | 543 | ID LOG_ID APP_SESSION ITEM_NAME ITEM_VALUE 544 | ------- ------- ---------------- ------------------------- --------------------------------------------- 545 | 136 47 45588554040361 P1_QUOTA 546 | 137 47 45588554040361 P1_TOTAL_SALES 547 | 138 47 45588554040361 P6_PRODUCT_NAME 3.2 GHz Desktop PC 548 | 139 47 45588554040361 P6_PRODUCT_DESCRIPTION All the options, this machine is loaded! 549 | 140 47 45588554040361 P6_CATEGORY Computer 550 | 141 47 45588554040361 P6_PRODUCT_AVAIL Y 551 | 142 47 45588554040361 P6_LIST_PRICE 1200 552 | 143 47 45588554040361 P6_PRODUCT_IMAGE 553 | 144 47 45588554040361 P4_CALENDAR_DATE 20091103 554 | 145 47 45588554040361 P7_CUSTOMER_ID 6 555 | 146 47 45588554040361 P7_BRANCH 2 556 | 147 47 45588554040361 P29_ORDER_ID_NEXT 557 | 148 47 45588554040361 P29_ORDER_ID_PREV 558 | 149 47 45588554040361 P29_ORDER_ID_COUNT 0 of 0 559 | 150 47 45588554040361 P7_CUST_FIRST_NAME Albert 560 | 151 47 45588554040361 P7_CUST_LAST_NAME Lambert 561 | 152 47 45588554040361 P7_CUST_STREET_ADDRESS1 10701 Lambert International Blvd. 562 | 153 47 45588554040361 P7_CUST_STREET_ADDRESS2 563 | 154 47 45588554040361 P7_CUST_CITY St. Louis 564 | 155 47 45588554040361 P7_CUST_STATE MO 565 | 156 47 45588554040361 P7_CUST_POSTAL_CODE 63145 566 | 157 47 45588554040361 P7_CUST_EMAIL 567 | 158 47 45588554040361 P7_PHONE_NUMBER1 314-555-4022 568 | 159 47 45588554040361 P7_PHONE_NUMBER2 569 | 160 47 45588554040361 P7_CREDIT_LIMIT 1000 570 | 161 47 45588554040361 P6_PRODUCT_ID 1 571 | 162 47 45588554040361 P29_ORDER_ID 9 572 | ``` 573 | 574 | 575 | ## Utility Functions 576 | 577 | 578 | 579 | ### TOCHAR 580 | 581 | TOCHAR will convert the value to a string (varchar2). It is useful when wanting to log items, such as booleans, without having to explicitly convert them. 582 | 583 | **Note: ```tochar ``` does not use the *no_op* conditional compilation so it will always execute.** This means that you can use outside of Logger (i.e. within your own application business logic). 584 | 585 | #### Syntax 586 | ```sql 587 | logger.tochar( 588 | p_val in number | date | timestamp | timestamp with time zone | timestamp with local time zone | boolean 589 | return varchar2); 590 | ``` 591 | 592 | #### Parameters 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 |
PrameterDescription
p_valValue (in original data type)
returnVarchar2 value of p_val
607 | 608 | #### Example 609 | ```sql 610 | select logger.tochar(sysdate) 611 | from dual; 612 | 613 | LOGGER.TOCHAR(SYSDATE) 614 | ----------------------- 615 | 13-JUN-2014 21:20:34 616 | 617 | 618 | -- In PL/SQL highlighting conversion from boolean to varchar2 619 | SQL> exec dbms_output.put_line(logger.tochar(true)); 620 | TRUE 621 | 622 | PL/SQL procedure successfully completed. 623 | 624 | ``` 625 | 626 | 627 | ### SPRINTF 628 | 629 | ```sprintf``` is similar to the common procedure [```printf```](http://en.wikipedia.org/wiki/Printf_format_string) found in many programming languages. It replaces substitution strings for a given string. Substitution strings can be either ```%s``` or ```%s``` where `````` is a number 1~10. 630 | 631 | The following rules are used to handle substitution strings (in order): 632 | - Replaces ```%s``` with ```p_s```, regardless of order that they appear in ```p_str``` 633 | - Occurrences of ```%s``` (no number) are replaced with ```p_s1..p_s10``` in order that they appear in ```p_str``` 634 | - ```%%``` is escaped to ```%``` 635 | 636 | **Note: ```sprintf ``` does not use the *no_op* conditional compilation so it will always execute.** This means that you can use outside of Logger (i.e. within your own application business logic). 637 | 638 | 639 | #### Syntax 640 | ```sql 641 | function sprintf( 642 | p_str in varchar2, 643 | p_s1 in varchar2 default null, 644 | p_s2 in varchar2 default null, 645 | p_s3 in varchar2 default null, 646 | p_s4 in varchar2 default null, 647 | p_s5 in varchar2 default null, 648 | p_s6 in varchar2 default null, 649 | p_s7 in varchar2 default null, 650 | p_s8 in varchar2 default null, 651 | p_s9 in varchar2 default null, 652 | p_s10 in varchar2 default null) 653 | return varchar2; 654 | ``` 655 | 656 | #### Parameters 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 |
PrameterDescription
p_strString to apply substitution strings to
p_s<1..10>Substitution strings
returnFormated string
675 | 676 | #### Example 677 | ```sql 678 | select logger.sprintf('hello %s, how are you %s', 'martin', 'today') msg 679 | from dual; 680 | 681 | MSG 682 | ------------------------------- 683 | hello martin, how are you today 684 | 685 | -- Advance features 686 | 687 | -- Escaping % 688 | select logger.sprintf('hello, %% (escape) %s1', 'martin') msg 689 | from dual; 690 | 691 | MSG 692 | ------------------------- 693 | hello, % (escape) martin 694 | 695 | -- %s replacement 696 | select logger.sprintf('%s1, %s2, %s', 'one', 'two') msg 697 | from dual; 698 | 699 | MSG 700 | ------------------------- 701 | one, two, one 702 | ``` 703 | 704 | 705 | 706 | ### GET_CGI_ENV 707 | TODO Description 708 | 709 | #### Syntax 710 | ```sql 711 | logger.get_cgi_env( 712 | p_show_null in boolean default false) 713 | return clob; 714 | ``` 715 | #### Parameters 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 |
PrameterDescription
p_show_nullShow null variables.
returnFormatted list of CGI variables
730 | 731 | #### Example 732 | ```sql 733 | TODO 734 | ``` 735 | 736 | 737 | 738 | ### GET_PREF 739 | Returns the preference from LOGGER_PREFS. If `p_pref_type` is not defined then the system level preferences will be returned. 740 | 741 | #### Syntax 742 | ```sql 743 | logger.get_pref( 744 | p_pref_name in logger_prefs.pref_name%type, 745 | p_pref_type in logger_prefs.pref_type%type default logger.g_pref_type_logger) 746 | return varchar2 747 | ``` 748 | 749 | #### Parameters 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 |
PrameterDescription
p_pref_namePreference to get value for.
p_pref_typePreference type
returnPrefence value.
768 | 769 | #### Example 770 | ```sql 771 | dbms_output.put_line('Logger level: ' || logger.get_pref('LEVEL')); 772 | ``` 773 | 774 | 775 | 776 | ### SET_PREF 777 | In some cases you may want to store custom preferences in the `LOGGER_PREFS` table. A use case for this would be when creating a plugin that needs to reference some parameters. 778 | 779 | This procedure allows you to leverage the `LOGGER_PREFS` table to store your custom preferences. To avoid any naming conflicts with Logger, you must use a type (defined in `p_pref_type`). You can not use the type `LOGGER` as it is reserved for Logger system preferences. 780 | 781 | `SET_PREF` will either create or udpate a value. Values must contain data. If not, use [`DEL_PREF`](#procedure-del_pref) to delete unused preferences. 782 | 783 | #### Syntax 784 | ```sql 785 | logger.set_pref( 786 | p_pref_type in logger_prefs.pref_type%type, 787 | p_pref_name in logger_prefs.pref_name%type, 788 | p_pref_value in logger_prefs.pref_value%type); 789 | ``` 790 | 791 | #### Parameters 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 |
PrameterDescription
p_pref_typeType of preference. Use your own name space to avoid conflicts with Logger. Types will automatically be converted to uppercase
p_pref_namePreference to get value for. Must be prefixed with "CUST_". Value will be created or updated. This value will be stored as uppercase.
p_pref_valuePrefence value.
810 | 811 | #### Example 812 | ```sql 813 | logger.set_pref( 814 | p_pref_type => 'CUSTOM' 815 | p_pref_name => 'MY_PREF', 816 | p_pref_value => 'some value'); 817 | ``` 818 | 819 | 820 | 821 | ### DEL_PREF 822 | Deletes a preference except for system level preferences. 823 | 824 | #### Syntax 825 | ```sql 826 | logger.del_pref( 827 | p_pref_type in logger_prefs.pref_type%type, 828 | p_pref_name in logger_prefs.pref_name%type); 829 | ``` 830 | 831 | #### Parameters 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 |
PrameterDescription
p_pref_typeNamepsace / type of preference.
p_pref_nameCustom preference to delete.
846 | 847 | #### Example 848 | ```sql 849 | logger.del_pref( 850 | p_pref_type => 'CUSTOM' 851 | p_pref_name => 'MY_PREF'); 852 | ``` 853 | 854 | 855 | 856 | ### PURGE 857 | TODO_DESC 858 | 859 | #### Syntax 860 | ```sql 861 | logger.purge( 862 | p_purge_after_days in varchar2 default null, 863 | p_purge_min_level in varchar2 default null); 864 | ``` 865 | #### Parameters 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 |
PrameterDescription
p_purge_after_daysPurge entries older than n days.
p_purge_min_levelMinimum level to purge entries. For example if set to *logger.g\_information* then information, debug, timing, sys_context, and apex logs will be deleted.
880 | 881 | #### Example 882 | ```sql 883 | TODO 884 | ``` 885 | 886 | 887 | 888 | ### PURGE_ALL 889 | Purges all non-permanent entries in LOGGER_LOGS. 890 | 891 | 892 | #### Syntax 893 | ```sql 894 | logger.purge_all; 895 | ``` 896 | 897 | #### Parameters 898 | No Parameters 899 | 900 | #### Example 901 | ```sql 902 | TODO 903 | -- For this one show a count before of logger_logs. Then run, then show what's left in the table. 904 | ``` 905 | 906 | 907 | 908 | ### STATUS 909 | Prints the Logger's current status and configuration settings. 910 | 911 | #### Syntax 912 | ```sql 913 | logger.status( 914 | p_output_format in varchar2 default null); -- SQL-DEVELOPER | HTML | DBMS_OUPUT 915 | ``` 916 | 917 | #### Parameters 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 |
PrameterDescription
p_output_formatWhat type of output. Accepted values are SQL-DEVELOPER, HTML, and DBMS_OUPUT.
928 | 929 | 930 | #### Example 931 | ```sql 932 | set serveroutput on 933 | exec logger.status 934 | 935 | Project Home Page : https://github.com/oraopensource/logger/ 936 | Logger Version : 2.0.0.a01 937 | Debug Level : DEBUG 938 | Capture Call Stack : TRUE 939 | Protect Admin Procedures : TRUE 940 | APEX Tracing : Disabled 941 | SCN Capture : Disabled 942 | Min. Purge Level : DEBUG 943 | Purge Older Than : 7 days 944 | Pref by client_id expire : 12 hours 945 | For all client info see : logger_prefs_by_client_id 946 | 947 | PL/SQL procedure successfully completed. 948 | ``` 949 | 950 | 951 | 952 | ### SQLPLUS_FORMAT 953 | TODO_DESC 954 | 955 | #### Syntax 956 | ```sql 957 | logger.sqlplus_format; 958 | ``` 959 | 960 | #### Parameters 961 | No Parameters 962 | 963 | #### Example 964 | ```sql 965 | TODO 966 | ``` 967 | 968 | 969 | ### NULL_GLOBAL_CONTEXTS 970 | TODO_DESC 971 | 972 | #### Syntax 973 | ```sql 974 | logger.null_global_contexts; 975 | ``` 976 | 977 | #### Parameters 978 | No Parameters. 979 | 980 | #### Example 981 | ```sql 982 | TODO 983 | ``` 984 | 985 | 986 | ### CONVERT_LEVEL_CHAR_TO_NUM 987 | Returns the number representing the given level (string). 988 | 989 | #### Syntax 990 | ```sql 991 | logger.convert_level_char_to_num( 992 | p_level in varchar2) 993 | return number; 994 | ``` 995 | 996 | #### Parameters 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 |
PrameterDescription
p_levelLevel name.
returnLevel number
1011 | 1012 | #### Example 1013 | ```sql 1014 | select logger.convert_level_char_to_num(p_level => 'DEBUG') level_number 1015 | from dual; 1016 | 1017 | LEVEL_NUMBER 1018 | ------------ 1019 | 16 1020 | ``` 1021 | 1022 | 1023 | ### DATE_TEXT_FORMAT 1024 | Returns the time difference (in nicely formatted string) of *p\_date* compared to now (sysdate). 1025 | 1026 | #### Syntax 1027 | ```sql 1028 | logger.date_text_format (p_date in date) 1029 | return varchar2; 1030 | ``` 1031 | 1032 | #### Parameters 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 |
PrameterDescription
p_dateDate to compare
returnTime difference between p_date and now.
1047 | 1048 | #### Example 1049 | ```sql 1050 | select logger.date_text_format(sysdate-1) date_diff 1051 | from dual; 1052 | 1053 | DATE_DIFF 1054 | ----------- 1055 | 1 days ago 1056 | ``` 1057 | 1058 | 1059 | ### GET_CHARACTER_CODES 1060 | Similar to [log_character_codes](#procedure-log_character_codes) except will return the character codes instead of logging them. 1061 | 1062 | #### Syntax 1063 | ```sql 1064 | logger.get_character_codes( 1065 | p_string in varchar2, 1066 | p_show_common_codes in boolean default true) 1067 | return varchar2; 1068 | ``` 1069 | 1070 | #### Parameters 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 |
PrameterDescription
p_stringString to get codes for.
p_show_common_codesDisplay legend of common character codes.
returnString with character codes.
1089 | 1090 | #### Example 1091 | ```sql 1092 | select logger.get_character_codes('Hello World') char_codes 1093 | from dual; 1094 | 1095 | CHAR_CODES 1096 | -------------------------------------------------------------------------------- 1097 | Common Codes: 13=Line Feed, 10=Carriage Return, 32=Space, 9=Tab 1098 | 72,101,108,108,111, 32, 87,111,114,108,100 1099 | H, e, l, l, o, , W, o, r, l, d 1100 | ``` 1101 | 1102 | 1103 | 1104 | ### APPEND_PARAM 1105 | Logger has wrapper functions to quickly and easily log parameters. All primary log procedures take in a fourth parameter to support logging a parameter array. The values are explicitly converted to strings so you don't need to convert them. The parameter values will be stored n the *extra* column. 1106 | 1107 | #### Syntax 1108 | ```sql 1109 | logger.append_param( 1110 | p_params in out nocopy logger.tab_param, 1111 | p_name in varchar2, 1112 | p_val in ); 1113 | ``` 1114 | 1115 | #### Parameters 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 |
PrameterDescription
p_paramsParam array to append parameter value to.
p_nameName of the parameter.
p_valValue (in original data type).
1134 | 1135 | #### Example 1136 | ```sql 1137 | create or replace procedure p_demo_function( 1138 | p_empno in emp.empno%type, 1139 | p_ename in emp.ename%type) 1140 | as 1141 | l_scope logger_logs.scope%type := 'p_demo_function'; 1142 | l_params logger.tab_param; 1143 | begin 1144 | logger.append_param(l_params, 'p_empno', p_empno); -- Parameter name and value just stored in PL/SQL array and not logged yet 1145 | logger.append_param(l_params, 'p_ename', p_ename); -- Parameter name and value just stored in PL/SQL array and not logged yet 1146 | logger.log('START', l_scope, null, l_params); -- All parameters are logged at this point 1147 | -- ... 1148 | exception 1149 | when others then 1150 | logger.log_error('Unhandled Exception', l_scope, null, l_params); 1151 | end p_demo_function; 1152 | / 1153 | ``` 1154 | 1155 | 1156 | ### OK_TO_LOG 1157 | Though Logger internally handles when a statement is stored in the LOGGER_LOGS table there may be situations where you need to know if logger will log a statement before calling logger. This is useful when doing an expensive operation just to log the data. 1158 | 1159 | A classic example is looping over an array for the sole purpose of logging the data. In this case, there's no reason why the code should perform the additional computations when logging is disabled for a certain level. 1160 | 1161 | *ok\_to\_log* will also factor in client specific logging settings. 1162 | 1163 | *Note*: *ok\_to\_log* is not something that should be used frequently. All calls to logger run this command internally. 1164 | 1165 | #### Syntax 1166 | ```sql 1167 | logger.ok_to_log(p_level in varchar2) 1168 | return boolean; 1169 | ``` 1170 | 1171 | #### Parameters 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 |
PrameterDescription
p_levelLevel (name) to test for.
returnWether or not level will be logged.
1186 | 1187 | #### Example 1188 | ```sql 1189 | declare 1190 | type typ_array is table of number index by pls_integer; 1191 | l_array typ_array; 1192 | begin 1193 | -- Load test data 1194 | for x in 1..100 loop 1195 | l_array(x) := x; 1196 | end loop; 1197 | 1198 | -- Only log if logging is enabled 1199 | if logger.ok_to_log(logger.g_debug) then 1200 | for x in 1..l_array.count loop 1201 | logger.log(l_array(x)); 1202 | end loop; 1203 | end if; 1204 | end; 1205 | / 1206 | ``` 1207 | 1208 | Note: ok\_to\_log should not be used for one-off log commands. This defeats the whole purpose of having the various log commands. For example ok\_to\_log should *not* be used in the following way: 1209 | 1210 | ```sql 1211 | -- Reminder: This is an example of how not to use ok_to_log 1212 | ... 1213 | if logger.ok_to_log(logger.g_debug) then 1214 | logger.log('test'); 1215 | end if; 1216 | ... 1217 | ``` 1218 | 1219 | 1220 | 1221 | ### INS_LOGGER_LOGS 1222 | Similar to ```ok_to_log```, this procedure should be used very infrequently as the main Logger procedures should handle everything that is required for quickly logging information. 1223 | 1224 | As part of the 2.1.0 release, the trigger on ```LOGGER_LOGS``` was removed for both performance and other issues. Though inserting directly to the ```LOGGER_LOGS``` table is not a supported feature of Logger, you may have some code that does a direct insert. The primary reason that a manual insert into ```LOGGER_LOGS``` was done was to obtain the ```ID``` column for the log entry. 1225 | 1226 | To help prevent any issues with backwards compatibility, ```ins_logger_logs``` has been made publicly accessible to handle any inserts into ```LOGGER_LOGS```. This is a supported procedure and any manual insert statements will need to be modified to use this procedure instead. 1227 | 1228 | Important things to now about ```ins_logger_logs```: 1229 | 1230 | - It does not check the Logger level. This means it will always insert into the ```LOGGER_LOGS``` table. It is also an Autonomous Transaction procedure so a commit is always performed, however it will not affect the current session. 1231 | - [Plugins](Plugins.md) will not be executed when calling this procedure. If you have critical processes which leverage plugin support you should use the proper log function instead. 1232 | 1233 | #### Syntax 1234 | ```sql 1235 | logger.ins_logger_logs( 1236 | p_logger_level in logger_logs.logger_level%type, 1237 | p_text in varchar2 default null, 1238 | p_scope in logger_logs.scope%type default null, 1239 | p_call_stack in logger_logs.call_stack%type default null, 1240 | p_unit_name in logger_logs.unit_name%type default null, 1241 | p_line_no in logger_logs.line_no%type default null, 1242 | p_extra in logger_logs.extra%type default null, 1243 | po_id out nocopy logger_logs.id%type); 1244 | ``` 1245 | 1246 | #### Parameters 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 |
PrameterDescription
p_logger_levelLogger level. See Constants section for list of variables to chose from.
p_textText column.
p_scopeScope.
p_call_stackPL/SQL call stack.
p_unit_nameUnit name (this is usually the calling procedure).
p_line_noLine number
p_extraExtra CLOB.
po_idLogger ID (out).
1285 | 1286 | #### Example 1287 | ```sql 1288 | set serveroutput on 1289 | 1290 | declare 1291 | l_id logger_logs.id%type; 1292 | begin 1293 | -- Note: Commented out parameters not used for this demo (but still accessible via API) 1294 | logger.ins_logger_logs( 1295 | p_logger_level => logger.g_debug, 1296 | p_text => 'Custom Insert', 1297 | p_scope => 'demo.logger.custom_insert', 1298 | -- p_call_stack => '' 1299 | p_unit_name => 'Dynamic PL/SQL', 1300 | -- p_line_no => , 1301 | -- p_extra => , 1302 | po_id => l_id 1303 | ); 1304 | 1305 | dbms_output.put_line('ID: ' || l_id); 1306 | end; 1307 | / 1308 | 1309 | ID: 2930650 1310 | ``` 1311 | 1312 | 1313 | 1314 | ## Set Logging Level 1315 | 1316 | Logger allows you to configure both system logging levels and client specific logging levels. If a client specific logging level is defined, it will override the system level configuration. If no client level is defined Logger will defautl to the system level configuration. 1317 | 1318 | Prior to version 2.0.0 Logger only supported one logger level. The primary goal of this approach was to enable Logger at Debug level for development environments, then change it to Error levels in production environments so the logs did not slow down the system. Over time developers start to find that in some situations they needed to see what a particular user / session was doing in production. Their only option was to enable Logger for the entire system which could potentially slow everyone down. 1319 | 1320 | Starting in version 2.0.0 you can now specify the logger level along with call stack setting by specifying the *client_identifier*. If not explicitly unset, client specific configurations will expire after a set period of time. 1321 | 1322 | The following query shows all the current client specific log configurations: 1323 | ```sql 1324 | select * 1325 | from logger_prefs_by_client_id; 1326 | 1327 | CLIENT_ID LOGGER_LEVEL INCLUDE_CALL_STACK CREATED_DATE EXPIRY_DATE 1328 | ------------------- ------------- ------------------ -------------------- -------------------- 1329 | logger_demo_session ERROR TRUE 24-APR-2013 02:48:13 24-APR-2013 14:48:13 1330 | ``` 1331 | 1332 | 1333 | 1334 | ### SET_LEVEL 1335 | Set both system and client logging levels. 1336 | 1337 | #### Syntax 1338 | ```sql 1339 | logger.set_level( 1340 | p_level in varchar2 default logger.g_debug_name, 1341 | p_client_id in varchar2 default null, 1342 | p_include_call_stack in varchar2 default null, 1343 | p_client_id_expire_hours in number default null 1344 | ); 1345 | ``` 1346 | 1347 | #### Parameters 1348 | 1349 | 1350 | 1351 | 1352 | 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | 1368 | 1369 |
PrameterDescription
p_levelUse logger.g_<level>_name variables. See [Constants](#constants-logger-levels). If the level is deprecated it will automatically be set to DEBUG.
p_client_idOptional: If defined, will set the level for the given client identifier. If null will affect global settings.
p_include_call_stackOptional: Only valid if p_client_id is defined Valid values: TRUE, FALSE. If not set will use the default system pref in logger_prefs.
p_client_id_expire_hoursIf p_client_id, expire after number of hours. If not defined, will default to system preference PREF_BY_CLIENT_ID_EXPIRE_HOURS.
1370 | 1371 | #### Example 1372 | Set system level logging level: 1373 | ```sql 1374 | exec logger.set_level(logger.g_debug_name); 1375 | ``` 1376 | 1377 | Client Specific Configuration: 1378 | ```sql 1379 | -- In Oracle Session-1 1380 | exec logger.set_level(logger.g_debug_name); 1381 | 1382 | exec logger.log('Session-1: this should show up'); 1383 | 1384 | select id, logger_level, text, client_identifier, call_stack 1385 | from logger_logs_5_min 1386 | order by id; 1387 | 1388 | ID LOGGER_LEVEL TEXT CLIENT_IDENTIFIER CALL_STACK 1389 | ---- ------------ ----------------------------------- ----------------- ---------------------------- 1390 | 31 16 Session-1: this should show up object line object 1391 | 1392 | exec logger.set_level (logger.g_error_name); 1393 | 1394 | exec logger.log('Session-1: this should NOT show up'); 1395 | 1396 | -- The previous line does not get logged since the logger level is set to ERROR and it made a .log call 1397 | 1398 | 1399 | -- In Oracle Session-2 (i.e. a different session) 1400 | exec dbms_session.set_identifier('my_identifier'); 1401 | 1402 | -- This sets the logger level for current identifier 1403 | exec logger.set_level(logger.g_debug_name, sys_context('userenv','client_identifier')); 1404 | 1405 | exec logger.log('Session-2: this should show up'); 1406 | 1407 | select id, logger_level, text, client_identifier, call_stack 1408 | from logger_logs_5_min 1409 | order by id; 1410 | 1411 | ID LOGGER_LEVEL TEXT CLIENT_IDENTIFIER CALL_STACK 1412 | ---- ------------ ----------------------------------- ----------------- ---------------------------- 1413 | 31 16 Session-1: this should show up object line object 1414 | 32 16 Session-2: this should show up my_identifier object line object 1415 | 1416 | -- Notice how the CLIENT_IDENTIFIER field also contains the current client_identifer 1417 | ``` 1418 | 1419 | In APEX the *client\_identifier* is 1420 | ```sql 1421 | :APP_USER || ':' || :APP_SESSION 1422 | ``` 1423 | 1424 | 1425 | 1426 | ### UNSET_CLIENT_LEVEL 1427 | Unset logger level by specific *client_id*. 1428 | 1429 | #### Syntax 1430 | ```sql 1431 | logger.unset_client_level(p_client_id in varchar2); 1432 | ``` 1433 | 1434 | #### Parameters 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | 1442 | 1443 | 1444 |
PrameterDescription
p_client_idClient identifier to unset logging level.
1445 | 1446 | #### Example 1447 | ```sql 1448 | exec logger.unset_client_level('my_client_id'); 1449 | ``` 1450 | 1451 | 1452 | ### UNSET_CLIENT_LEVEL 1453 | Unset all expired *client_id*s. Note this run automatically each hour by the *LOGGER\_UNSET\_PREFS\_BY\_CLIENT* job. 1454 | 1455 | #### Syntax 1456 | ```sql 1457 | logger.unset_client_level; 1458 | ``` 1459 | 1460 | #### Parameters 1461 | No parameters. 1462 | 1463 | #### Example 1464 | ```sql 1465 | exec logger.unset_client_level; 1466 | ``` 1467 | 1468 | 1469 | ### UNSET_CLIENT_LEVEL_ALL 1470 | Unset all client configurations (regardless of expiry time). 1471 | 1472 | #### Syntax 1473 | ```sql 1474 | logger.unset_client_level_all; 1475 | ``` 1476 | 1477 | #### Parameters 1478 | No Parameters. 1479 | 1480 | #### Example 1481 | ```sql 1482 | exec logger.unset_client_level_all; 1483 | ``` 1484 | 1485 | 1486 | ## Timing Procedures 1487 | 1488 | TODO description? 1489 | 1490 | 1491 | ### Example 1492 | Since all the timing procedures are tightly coupled, the following example will be used to cover all of them: 1493 | 1494 | ```sql 1495 | declare 1496 | l_number number; 1497 | begin 1498 | logger.time_reset; 1499 | logger.time_start('foo'); 1500 | logger.time_start('bar'); 1501 | for i in 1..500000 loop 1502 | l_number := power(i,15); 1503 | l_number := sqrt(1333); 1504 | end loop; --i 1505 | logger.time_stop('bar'); 1506 | for i in 1..500000 loop 1507 | l_number := power(i,15); 1508 | l_number := sqrt(1333); 1509 | end loop; --i 1510 | logger.time_stop('foo'); 1511 | end; 1512 | / 1513 | 1514 | select text from logger_logs_5_min; 1515 | 1516 | TEXT 1517 | --------------------------------- 1518 | START: foo 1519 | > START: bar 1520 | > STOP : bar - 1.000843 seconds 1521 | STOP : foo - 2.015953 seconds 1522 | ``` 1523 | 1524 | 1525 | ### TIME_RESET 1526 | Resets all timers. 1527 | 1528 | #### Syntax 1529 | ```sql 1530 | logger.time_reset; 1531 | ``` 1532 | 1533 | #### Parameters 1534 | No Parameters. 1535 | 1536 | #### Example 1537 | ```sql 1538 | logger.time_reset; 1539 | ``` 1540 | 1541 | 1542 | ### TIME_START 1543 | Starts a timer. 1544 | 1545 | #### Syntax 1546 | ```sql 1547 | logger.time_start( 1548 | p_unit IN VARCHAR2, 1549 | p_log_in_table IN boolean default true) 1550 | ``` 1551 | 1552 | #### Parameters 1553 | 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1560 | 1561 | 1562 | 1563 | 1564 | 1565 | 1566 |
PrameterDescription
p_unitName for timing unit
p_log_in_tableIf true, will log the start event in LOGGER_LOGS.
1567 | 1568 | #### Example 1569 | See [Timing Procedures Example](#timing-procedures-example). 1570 | 1571 | 1572 | ### TIME_STOP 1573 | Stops a timing event and logs in LOGGER_LOGS using level = logger.g_timing. 1574 | 1575 | #### Syntax 1576 | ```sql 1577 | logger.time_stop( 1578 | p_unit in varchar2, 1579 | p_scope in varchar2 default null); 1580 | ``` 1581 | 1582 | #### Parameters 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | 1589 | 1590 | 1591 | 1592 | 1593 | 1594 | 1595 | 1596 |
PrameterDescription
p_unitTimer to stop.
p_scopeScope to log timer under.
1597 | 1598 | #### Example 1599 | See [Timing Procedures Example](#timing-procedures-example). 1600 | 1601 | 1602 | ### TIME_STOP 1603 | Similar to [TIME_STOP](#time_stop) procedure, this function will stop a timer. Logging into LOGGER_LOGS is configurable. Returns the stop time string. 1604 | 1605 | #### Syntax 1606 | ```sql 1607 | logger.time_stop( 1608 | p_unit in varchar2, 1609 | p_scope in varchar2 default null, 1610 | p_log_in_table in boolean default true) 1611 | return varchar2; 1612 | ``` 1613 | 1614 | #### Parameters 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | 1622 | 1623 | 1624 | 1625 | 1626 | 1627 | 1628 | 1629 | 1630 | 1631 | 1632 | 1633 | 1634 | 1635 | 1636 |
PrameterDescription
p_unitTimer to stop.
p_scopeScope to log timer under.
p_log_in_tableStore result in LOGGER_LOGS.
returnTimer results.
1637 | 1638 | #### Example 1639 | ```sql 1640 | TODO 1641 | ``` 1642 | 1643 | 1644 | ### TIME_STOP_SECONDS 1645 | TODO_DESC 1646 | 1647 | #### Syntax 1648 | ```sql 1649 | logger.time_stop_seconds( 1650 | p_unit in varchar2, 1651 | p_scope in varchar2 default null, 1652 | p_log_in_table in boolean default true) 1653 | return number; 1654 | ``` 1655 | 1656 | #### Parameters 1657 | 1658 | 1659 | 1660 | 1661 | 1662 | 1663 | 1664 | 1665 | 1666 | 1667 | 1668 | 1669 | 1670 | 1671 | 1672 | 1673 | 1674 |
PrameterDescription
p_unitTimer to stop.
p_scopeScope to log timer under.
p_log_in_tableStore result in LOGGER_LOGS.
1675 | 1676 | #### Example 1677 | ```sql 1678 | TODO 1679 | ``` 1680 | -------------------------------------------------------------------------------- /docs/Plugins.md: -------------------------------------------------------------------------------- 1 | ***This document is best viewed in [flatdoc format](http://oraopensource.github.io/flatdoc?repo=logger&path=docs%2FPlugins.md)*** 2 | 3 | 4 | # About 5 | Plugins are a new feature introduced in Logger 3.0.0. They allow developers to run custom code after a log has been inserted. This can be very useful for things such as custom notifications after an error. 6 | 7 | To help with performance, the plugin architecture uses conditional compilation which will only execute one a plugin has been properly configured. 8 | 9 | 10 | ## Plugin Methods 11 | 12 | The following types of plugins are currently supported: 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
NameAssociated ProcedureDescription
PLUGIN_FN_ERRORlogger.log_errorAllows you to take a logger record type returned as part of the logger.log_error method and use the attributes of it for subsequent actions
26 | 27 | 28 | # Configuration 29 | There are two steps to configure a plugin. The first is to register a custom function ([more on this below](#plugin-interface)) in the logger prefs table. The following examples shows how to register a custom plugin procedure (in this example called ```custom_plugin_method```) to be run after calls to ```logger.log_error```: 30 | ```sql 31 | update logger_prefs 32 | set pref_value = 'custom_plugin_method' 33 | where 1=1 34 | and pref_type = logger.g_pref_type_logger 35 | and pref_name = 'PLUGIN_FN_ERROR' 36 | ``` 37 | 38 | Once the custom method has been set in the `logger_prefs table`, you must run the `logger_configure` procedure which will recompile Logger. 39 | 40 | ```sql 41 | exec logger_configure; 42 | ``` 43 | 44 | To deregister a plugin, set the appropriate `logger_prefs.pref_value` to `null` and re-run the `logger_configure` procedure. *Note: since `pref_value` is not a nullable column, null values will be automatically converted to "NONE".* 45 | 46 | 47 | # Plugin Interface 48 | Plugins can either be standalone procedures or a procedure in a package. Plugins must implement the following interface: 49 | 50 | ```sql 51 | procedure ( 52 | p_rec in logger.rec_logger_log) 53 | ``` 54 | 55 | For more information about the `logger.rec_logger_log` type please see the [Types documentation](Logger%20API.md#types). 56 | 57 | 58 | # Example 59 | 60 | The following example shows how to create a custom plugin, configure, and run it. 61 | 62 | 63 | ## Plugin Procedure 64 | The first thing to do is create a method that will be called when an error is logged: 65 | 66 | ```sql 67 | create or replace procedure log_test_plugin( 68 | p_rec in logger.rec_logger_log) 69 | as 70 | l_text logger_logs.text%type; 71 | begin 72 | dbms_output.put_line('In Plugin'); 73 | dbms_output.put_line('p_rec.id: ' || p_rec.id); 74 | 75 | select text 76 | into l_text 77 | from logger_logs_5_min 78 | where id = p_rec.id; 79 | 80 | dbms_output.put_line('Text: ' || l_text); 81 | 82 | end; 83 | / 84 | ``` 85 | 86 | 87 | ## Register Plugin and Configure 88 | 89 | ```sql 90 | -- Register new plugin procedure for errors 91 | update logger_prefs 92 | set pref_value = 'log_test_plugin' 93 | where 1=1 94 | and pref_type = logger.g_pref_type_logger 95 | and pref_name = 'PLUGIN_FN_ERROR'; 96 | 97 | -- Configure with Logger 98 | exec logger_configure; 99 | ``` 100 | 101 | 102 | ## Run 103 | 104 | ```sql 105 | set serveroutput on 106 | 107 | exec logger.log_error('hello'); 108 | 109 | In Plugin 110 | p_rec.id: 811 111 | Text: hello 112 | ``` 113 | 114 | 115 | # Other 116 | There are several important things to know about plugins. 117 | 118 | ## Recursion 119 | Plugins do not support recursing for the same type of plugin. I.e. when in an error plugin and the plugin code calls ```logger.log_error```, the error plugin will not execute for the recursive call (but the second error record is still stored in ```logger_logs```. This is to avoid infinite loops in the plugin. 120 | 121 | The following example highlights this (note that ```logger.log_error``` is called in the plugin). 122 | 123 | ```sql 124 | create or replace procedure log_test_plugin( 125 | p_rec in logger.rec_logger_log) 126 | as 127 | l_text logger_logs.text%type; 128 | begin 129 | dbms_output.put_line('In Plugin'); 130 | 131 | -- This will not trigger the plugin to be 132 | -- run again since called inside the plugin 133 | logger.log_error('will not trigger plugin'); 134 | end; 135 | / 136 | 137 | exec logger.log_error('regular log_error call'); 138 | 139 | In Plugin 140 | ``` 141 | 142 | The output shows that the plugin was only run once, despite ```logger.log_error``` being called a second time inside the plugin. 143 | 144 | ## Errors in Plugin 145 | 146 | When an error occurs inside a plugin, it is logged (using ```logger.log_error```) and the error is then raised. 147 | 148 | Example: 149 | 150 | ```sql 151 | create or replace procedure log_test_plugin( 152 | p_rec in logger.rec_logger_log) 153 | as 154 | l_text logger_logs.text%type; 155 | begin 156 | dbms_output.put_line('In Plugin'); 157 | 158 | raise_application_error(-20001, 'test error'); 159 | end; 160 | / 161 | 162 | -- Highlight error being raised 163 | exec logger.log_error('testing plugin error'); 164 | 165 | * 166 | ERROR at line 1: 167 | ORA-20001: test error 168 | ORA-06512: at "GIFFY.LOGGER", line 809 169 | ORA-06512: at "GIFFY.LOGGER", line 1234 170 | ORA-06512: at line 1 171 | 172 | -- Show error that was logged 173 | 174 | select id, text 175 | from logger_logs_5_min 176 | order by id asc; 177 | 178 | ID TEXT 179 | ---- ----------------------------------------------- 180 | 818 testing plugin error 181 | 819 Exception in plugin procedure: LOG_TEST_PLUGIN ORA-20001: test error 182 | ``` 183 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | The documents have been broken up into the following pages for easier navigation: 2 | 3 | - [Installation](Installation.md) 4 | - All the information to install and configure Logger. 5 | - [Logger API](Logger%20API.md) 6 | - Complete Logger API documentation. 7 | - [Plugins](Plugins.md) 8 | - Plugin configuration and examples. 9 | - [Best Practices](Best%20Practices.md) 10 | - Set of best practices and suggested code templates to use when leveraging Logger. 11 | - [Change Logs](Change%20Logs.md) 12 | - Set of best practices and suggested code templates to use when leveraging Logger. 13 | - [Development Guide](Development%20Guide.md) 14 | - For people who want to help develop Logger. 15 | - [3rd Party Addons](Addons.md) 16 | - Listing of 3rd party products for Logger. 17 | -------------------------------------------------------------------------------- /releases/logger_1.2.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_1.2.0.zip -------------------------------------------------------------------------------- /releases/logger_1.2.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_1.2.2.zip -------------------------------------------------------------------------------- /releases/logger_1.3.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_1.3.0.zip -------------------------------------------------------------------------------- /releases/logger_1.4.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_1.4.0.zip -------------------------------------------------------------------------------- /releases/logger_2.0.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_2.0.0.zip -------------------------------------------------------------------------------- /releases/logger_2.1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_2.1.0.zip -------------------------------------------------------------------------------- /releases/logger_2.1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_2.1.1.zip -------------------------------------------------------------------------------- /releases/logger_2.1.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_2.1.2.zip -------------------------------------------------------------------------------- /releases/logger_3.0.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_3.0.0.zip -------------------------------------------------------------------------------- /releases/logger_3.1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_3.1.0.zip -------------------------------------------------------------------------------- /releases/logger_3.1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OraOpenSource/Logger/600f0c44cd0cc7f064776d31de50e9006d3476c4/releases/logger_3.1.1.zip -------------------------------------------------------------------------------- /source/contexts/logger_context.sql: -------------------------------------------------------------------------------- 1 | declare 2 | -- the following line is also used in a constant declaration in logger.pkb 3 | l_ctx_name varchar2(35) := substr(sys_context('USERENV','CURRENT_SCHEMA'),1,23)||'_LOGCTX'; 4 | begin 5 | execute immediate 'create or replace context '||l_ctx_name||' using logger accessed globally'; 6 | 7 | merge into logger_prefs p 8 | using (select 'GLOBAL_CONTEXT_NAME' pref_name, l_ctx_name pref_value, logger.g_pref_type_logger pref_type from dual) d 9 | on (1=1 10 | and p.pref_type = d.pref_type 11 | and p.pref_name = d.pref_name) 12 | when matched then 13 | update set p.pref_value = d.pref_value 14 | when not matched then 15 | insert (p.pref_name, p.pref_value, p.pref_type) 16 | values (d.pref_name, d.pref_value, d.pref_type); 17 | end; 18 | / 19 | -------------------------------------------------------------------------------- /source/gen_no_op.sql: -------------------------------------------------------------------------------- 1 | set sqlprompt '' 2 | set feedback off 3 | set linesize 200 4 | set serverout on 5 | set termout off 6 | 7 | alter package logger compile body PLSQL_CCFLAGS='NO_OP:TRUE'; 8 | 9 | spool logger_no_op.pkb 10 | 11 | prompt create or replace 12 | 13 | begin 14 | dbms_preprocessor.print_post_processed_source ( 15 | object_type => 'PACKAGE BODY', 16 | schema_name => USER, 17 | object_name => 'LOGGER'); 18 | end; 19 | / 20 | 21 | prompt / 22 | 23 | spool off 24 | 25 | alter package logger compile body PLSQL_CCFLAGS='NO_OP:FALSE'; 26 | 27 | exit 28 | -------------------------------------------------------------------------------- /source/install/create_user.sql: -------------------------------------------------------------------------------- 1 | Rem NAME 2 | Rem create_user.sql 3 | Rem 4 | Rem DESCRIPTION 5 | Rem Use this file to create a user / schema in which to install the logger packages 6 | Rem 7 | Rem NOTES 8 | Rem Assumes the SYS / SYSTEM user is connected. 9 | Rem 10 | Rem REQUIREMENTS 11 | Rem - Oracle 10.2+ 12 | Rem 13 | Rem 14 | Rem MODIFIED (MM/DD/YYYY) 15 | Rem tmuth 11/02/2006 - Created 16 | 17 | set define '&' 18 | 19 | set verify off 20 | prompt 21 | prompt 22 | prompt Logger create schema script. 23 | prompt You will be prompted for a username, tablespace, temporary tablespace and password. 24 | prompt 25 | 26 | 27 | define LOGGER_USER=LOGGER_USER 28 | accept LOGGER_USER char default &LOGGER_USER prompt 'Name of the new logger schema to create [&LOGGER_USER] :' 29 | 30 | define LOGGER_TABLESPACE=USERS 31 | accept LOGGER_TABLESPACE char default &LOGGER_TABLESPACE prompt 'Tablespace for the new logger schema [&LOGGER_TABLESPACE] :' 32 | 33 | define TEMP_TABLESPACE=TEMP 34 | accept TEMP_TABLESPACE char default &TEMP_TABLESPACE prompt 'Temporary Tablespace for the new logger schema [&TEMP_TABLESPACE] :' 35 | 36 | accept PASSWD CHAR prompt 'Enter a password for the logger schema [] :' HIDE 37 | 38 | create user &LOGGER_USER identified by &PASSWD default tablespace &LOGGER_TABLESPACE temporary tablespace &TEMP_TABLESPACE 39 | / 40 | 41 | alter user &LOGGER_USER quota unlimited on &LOGGER_TABLESPACE 42 | / 43 | 44 | grant connect,create view, create job, create table, create sequence, create trigger, create procedure, create any context to &LOGGER_USER 45 | / 46 | 47 | prompt 48 | prompt 49 | prompt &LOGGER_USER user successfully created. 50 | prompt Important!!! Connect as the &LOGGER_USER user and run the logger_install.sql script. 51 | prompt 52 | prompt 53 | 54 | exit 55 | -------------------------------------------------------------------------------- /source/install/drop_logger.sql: -------------------------------------------------------------------------------- 1 | drop package logger 2 | / 3 | 4 | drop procedure logger_configure 5 | / 6 | 7 | drop table logger_logs_apex_items cascade constraints 8 | / 9 | 10 | drop table logger_prefs cascade constraints 11 | / 12 | 13 | drop table logger_logs cascade constraints 14 | / 15 | 16 | drop table logger_prefs_by_client_id cascade constraints 17 | / 18 | 19 | drop sequence logger_logs_seq 20 | / 21 | 22 | drop sequence logger_apx_items_seq 23 | / 24 | 25 | 26 | begin 27 | dbms_scheduler.drop_job('LOGGER_PURGE_JOB'); 28 | end; 29 | / 30 | 31 | begin 32 | dbms_scheduler.drop_job('LOGGER_UNSET_PREFS_BY_CLIENT'); 33 | end; 34 | / 35 | 36 | drop view logger_logs_5_min 37 | / 38 | 39 | drop view logger_logs_60_min 40 | / 41 | 42 | drop view logger_logs_terse 43 | / 44 | 45 | -------------------------------------------------------------------------------- /source/install/logger_install_prereqs.sql: -------------------------------------------------------------------------------- 1 | -- This file contains the start and pre installation requirements for Logger 2 | whenever sqlerror exit 3 | set serveroutput on 4 | 5 | -- SESSION PRIVILEGES 6 | declare 7 | type t_sess_privs is table of pls_integer index by varchar2(50); 8 | l_sess_privs t_sess_privs; 9 | l_req_privs t_sess_privs; 10 | l_priv varchar2(50); 11 | l_dummy pls_integer; 12 | l_priv_error boolean := false; 13 | begin 14 | l_req_privs('CREATE SESSION') := 1; 15 | l_req_privs('CREATE TABLE') := 1; 16 | l_req_privs('CREATE VIEW') := 1; 17 | l_req_privs('CREATE SEQUENCE') := 1; 18 | l_req_privs('CREATE PROCEDURE') := 1; 19 | l_req_privs('CREATE TRIGGER') := 1; 20 | l_req_privs('CREATE ANY CONTEXT') := 1; 21 | l_req_privs('CREATE JOB') := 1; 22 | 23 | 24 | for c1 in (select privilege from session_privs) 25 | loop 26 | l_sess_privs(c1.privilege) := 1; 27 | end loop; --c1 28 | 29 | dbms_output.put_line('_____________________________________________________________________________'); 30 | 31 | l_priv := l_req_privs.first; 32 | loop 33 | exit when l_priv is null; 34 | begin 35 | l_dummy := l_sess_privs(l_priv); 36 | exception when no_data_found then 37 | dbms_output.put_line('Error, the current schema is missing the following privilege: '||l_priv); 38 | l_priv_error := true; 39 | end; 40 | l_priv := l_req_privs.next(l_priv); 41 | end loop; 42 | 43 | if not l_priv_error then 44 | dbms_output.put_line('User has all required privileges, installation will continue.'); 45 | end if; 46 | 47 | dbms_output.put_line('_____________________________________________________________________________'); 48 | 49 | if l_priv_error then 50 | raise_application_error (-20000, 'One or more required privileges are missing.'); 51 | end if; 52 | end; 53 | / 54 | 55 | whenever sqlerror continue 56 | 57 | -------------------------------------------------------------------------------- /source/install/post_install_configuration.sql: -------------------------------------------------------------------------------- 1 | -- Post installation configuration tasks 2 | PROMPT Calling logger_configure 3 | begin 4 | logger_configure; 5 | end; 6 | / 7 | 8 | 9 | -- Only set level if not in DEBUG mode 10 | PROMPT Setting Logger Level 11 | declare 12 | l_current_level logger_prefs.pref_value%type; 13 | begin 14 | 15 | select pref_value 16 | into l_current_level 17 | from logger_prefs 18 | where 1=1 19 | and pref_type = logger.g_pref_type_logger 20 | and pref_name = 'LEVEL'; 21 | 22 | -- Note: Probably not necessary but pre 1.4.0 code had this in place 23 | logger.set_level(l_current_level); 24 | end; 25 | / 26 | 27 | prompt 28 | prompt ************************************************* 29 | prompt Now executing LOGGER.STATUS... 30 | prompt 31 | 32 | begin 33 | logger.status; 34 | end; 35 | / 36 | 37 | prompt ************************************************* 38 | begin 39 | logger.log_permanent('Logger version '||logger.get_pref('LOGGER_VERSION')||' installed.'); 40 | end; 41 | / 42 | -------------------------------------------------------------------------------- /source/jobs/logger_purge_job.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_count pls_integer; 3 | l_job_name user_scheduler_jobs.job_name%type := 'LOGGER_PURGE_JOB'; 4 | begin 5 | 6 | select count(1) 7 | into l_count 8 | from user_scheduler_jobs 9 | where job_name = l_job_name; 10 | 11 | if l_count = 0 then 12 | dbms_scheduler.create_job( 13 | job_name => l_job_name, 14 | job_type => 'PLSQL_BLOCK', 15 | job_action => 'begin logger.purge; end; ', 16 | start_date => systimestamp, 17 | repeat_interval => 'FREQ=DAILY; BYHOUR=1', 18 | enabled => TRUE, 19 | comments => 'Purges LOGGER_LOGS using default values defined in logger_prefs.'); 20 | end if; 21 | end; 22 | / -------------------------------------------------------------------------------- /source/jobs/logger_unset_prefs_by_client.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_count pls_integer; 3 | l_job_name user_scheduler_jobs.job_name%type := 'LOGGER_UNSET_PREFS_BY_CLIENT'; 4 | begin 5 | 6 | select count(1) 7 | into l_count 8 | from user_scheduler_jobs 9 | where job_name = l_job_name; 10 | 11 | if l_count = 0 then 12 | dbms_scheduler.create_job( 13 | job_name => l_job_name, 14 | job_type => 'PLSQL_BLOCK', 15 | job_action => 'begin logger.unset_client_level; end; ', 16 | start_date => systimestamp, 17 | repeat_interval => 'FREQ=HOURLY; BYHOUR=1', 18 | enabled => TRUE, 19 | comments => 'Clears logger prefs by client_id'); 20 | end if; 21 | end; 22 | / -------------------------------------------------------------------------------- /source/packages/logger.pks: -------------------------------------------------------------------------------- 1 | create or replace package logger 2 | authid definer 3 | as 4 | -- This project uses the following MIT License: 5 | -- 6 | -- The MIT License (MIT) 7 | -- 8 | -- Copyright (c) 2015 OraOpenSource 9 | -- 10 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 11 | -- of this software and associated documentation files (the "Software"), to deal 12 | -- in the Software without restriction, including without limitation the rights 13 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | -- copies of the Software, and to permit persons to whom the Software is 15 | -- furnished to do so, subject to the following conditions: 16 | -- 17 | -- The above copyright notice and this permission notice shall be included in all 18 | -- copies or substantial portions of the Software. 19 | -- 20 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | -- SOFTWARE. 27 | 28 | 29 | -- TYPES 30 | type rec_param is record( 31 | name varchar2(255), 32 | val varchar2(4000)); 33 | 34 | type tab_param is table of rec_param index by binary_integer; 35 | 36 | type rec_logger_log is record( 37 | id logger_logs.id%type, 38 | logger_level logger_logs.logger_level%type 39 | ); 40 | 41 | 42 | -- VARIABLES 43 | g_logger_version constant varchar2(10) := 'x.x.x'; -- Don't change this. Build script will replace with right version number 44 | g_context_name constant varchar2(35) := substr(sys_context('USERENV','CURRENT_SCHEMA'),1,23)||'_LOGCTX'; 45 | 46 | g_off constant number := 0; 47 | g_permanent constant number := 1; 48 | g_error constant number := 2; 49 | g_warning constant number := 4; 50 | g_information constant number := 8; 51 | g_debug constant number := 16; 52 | g_timing constant number := 32; 53 | g_sys_context constant number := 64; 54 | g_apex constant number := 128; 55 | 56 | -- #44 57 | g_off_name constant varchar2(30) := 'OFF'; 58 | g_permanent_name constant varchar2(30) := 'PERMANENT'; 59 | g_error_name constant varchar2(30) := 'ERROR'; 60 | g_warning_name constant varchar2(30) := 'WARNING'; 61 | g_information_name constant varchar2(30) := 'INFORMATION'; 62 | g_debug_name constant varchar2(30) := 'DEBUG'; 63 | g_timing_name constant varchar2(30) := 'TIMING'; 64 | g_sys_context_name constant varchar2(30) := 'SYS_CONTEXT'; 65 | g_apex_name constant varchar2(30) := 'APEX'; 66 | 67 | gc_empty_tab_param tab_param; 68 | 69 | -- #54: Types for log_apex_items 70 | g_apex_item_type_all constant varchar2(30) := 'ALL'; -- Application items and page items 71 | g_apex_item_type_app constant varchar2(30) := 'APP'; -- All application items 72 | g_apex_item_type_page constant varchar2(30) := 'PAGE'; -- All page items 73 | -- To log items on a particular page, just enter the page number 74 | 75 | -- #127 76 | -- Note to developers: This is only for internal Logger code. Do not use this as part of your code. 77 | 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. 78 | 79 | -- Expose private functions only for testing during development 80 | $if $$logger_debug $then 81 | function is_number(p_str in varchar2) 82 | return boolean; 83 | 84 | procedure assert( 85 | p_condition in boolean, 86 | p_message in varchar2); 87 | 88 | function get_param_clob(p_params in logger.tab_param) 89 | return clob; 90 | 91 | procedure save_global_context( 92 | p_attribute in varchar2, 93 | p_value in varchar2, 94 | p_client_id in varchar2 default null); 95 | 96 | function set_extra_with_params( 97 | p_extra in logger_logs.extra%type, 98 | p_params in tab_param) 99 | return logger_logs.extra%type; 100 | 101 | function get_sys_context( 102 | p_detail_level in varchar2 default 'USER', -- ALL, NLS, USER, INSTANCE 103 | p_vertical in boolean default false, 104 | p_show_null in boolean default false) 105 | return clob; 106 | 107 | function admin_security_check 108 | return boolean; 109 | 110 | function get_level_number 111 | return number; 112 | 113 | function include_call_stack 114 | return boolean; 115 | 116 | function date_text_format_base ( 117 | p_date_start in date, 118 | p_date_stop in date) 119 | return varchar2; 120 | 121 | procedure log_internal( 122 | p_text in varchar2, 123 | p_log_level in number, 124 | p_scope in varchar2, 125 | p_extra in clob default null, 126 | p_callstack in varchar2 default null, 127 | p_params in tab_param default logger.gc_empty_tab_param); 128 | $end 129 | 130 | -- PROCEDURES and FUNCTIONS 131 | 132 | procedure null_global_contexts; 133 | 134 | function convert_level_char_to_num( 135 | p_level in varchar2) 136 | return number; 137 | 138 | function convert_level_num_to_char( 139 | p_level in number) 140 | return varchar2; 141 | 142 | function date_text_format (p_date in date) 143 | return varchar2; 144 | 145 | function get_character_codes( 146 | p_string in varchar2, 147 | p_show_common_codes in boolean default true) 148 | return varchar2; 149 | 150 | procedure log_error( 151 | p_text in varchar2 default null, 152 | p_scope in varchar2 default null, 153 | p_extra in clob default null, 154 | p_params in tab_param default logger.gc_empty_tab_param); 155 | 156 | procedure log_permanent( 157 | p_text in varchar2, 158 | p_scope in varchar2 default null, 159 | p_extra in clob default null, 160 | p_params in tab_param default logger.gc_empty_tab_param); 161 | 162 | procedure log_warning( 163 | p_text in varchar2, 164 | p_scope in varchar2 default null, 165 | p_extra in clob default null, 166 | p_params in tab_param default logger.gc_empty_tab_param); 167 | 168 | procedure log_warn( 169 | p_text in varchar2, 170 | p_scope in varchar2 default null, 171 | p_extra in clob default null, 172 | p_params in tab_param default logger.gc_empty_tab_param); 173 | 174 | procedure log_information( 175 | p_text in varchar2, 176 | p_scope in varchar2 default null, 177 | p_extra in clob default null, 178 | p_params in tab_param default logger.gc_empty_tab_param); 179 | 180 | procedure log_info( 181 | p_text in varchar2, 182 | p_scope in varchar2 default null, 183 | p_extra in clob default null, 184 | p_params in tab_param default logger.gc_empty_tab_param); 185 | 186 | procedure log( 187 | p_text in varchar2, 188 | p_scope in varchar2 default null, 189 | p_extra in clob default null, 190 | p_params in tab_param default logger.gc_empty_tab_param); 191 | 192 | function get_cgi_env( 193 | p_show_null in boolean default false) 194 | return clob; 195 | 196 | procedure log_userenv( 197 | p_detail_level in varchar2 default 'USER',-- ALL, NLS, USER, INSTANCE, 198 | p_show_null in boolean default false, 199 | p_scope in logger_logs.scope%type default null, 200 | p_level in logger_logs.logger_level%type default null); 201 | 202 | procedure log_cgi_env( 203 | p_show_null in boolean default false, 204 | p_scope in logger_logs.scope%type default null, 205 | p_level in logger_logs.logger_level%type default null); 206 | 207 | procedure log_character_codes( 208 | p_text in varchar2, 209 | p_scope in logger_logs.scope%type default null, 210 | p_show_common_codes in boolean default true, 211 | p_level in logger_logs.logger_level%type default null); 212 | 213 | procedure log_apex_items( 214 | p_text in varchar2 default 'Log APEX Items', 215 | p_scope in logger_logs.scope%type default null, 216 | p_item_type in varchar2 default logger.g_apex_item_type_all, 217 | p_log_null_items in boolean default true, 218 | p_level in logger_logs.logger_level%type default null); 219 | 220 | procedure time_start( 221 | p_unit in varchar2, 222 | p_log_in_table in boolean default true); 223 | 224 | procedure time_stop( 225 | p_unit in varchar2, 226 | p_scope in varchar2 default null); 227 | 228 | function time_stop( 229 | p_unit in varchar2, 230 | p_scope in varchar2 default null, 231 | p_log_in_table in boolean default true) 232 | return varchar2; 233 | 234 | function time_stop_seconds( 235 | p_unit in varchar2, 236 | p_scope in varchar2 default null, 237 | p_log_in_table in boolean default true) 238 | return number; 239 | 240 | procedure time_reset; 241 | 242 | function get_pref( 243 | p_pref_name in logger_prefs.pref_name%type, 244 | p_pref_type in logger_prefs.pref_type%type default logger.g_pref_type_logger) 245 | return varchar2 246 | $if not dbms_db_version.ver_le_10_2 $then 247 | result_cache 248 | $end; 249 | 250 | -- #103 251 | procedure set_pref( 252 | p_pref_type in logger_prefs.pref_type%type, 253 | p_pref_name in logger_prefs.pref_name%type, 254 | p_pref_value in logger_prefs.pref_value%type); 255 | 256 | -- #103 257 | procedure del_pref( 258 | p_pref_type in logger_prefs.pref_type%type, 259 | p_pref_name in logger_prefs.pref_name%type); 260 | 261 | procedure purge( 262 | p_purge_after_days in varchar2 default null, 263 | p_purge_min_level in varchar2 default null); 264 | 265 | procedure purge( 266 | p_purge_after_days in number default null, 267 | p_purge_min_level in number); 268 | 269 | procedure purge_all; 270 | 271 | procedure status( 272 | p_output_format in varchar2 default null); -- SQL-DEVELOPER | HTML | DBMS_OUPUT 273 | 274 | procedure sqlplus_format; 275 | 276 | procedure set_level( 277 | p_level in varchar2 default logger.g_debug_name, 278 | p_client_id in varchar2 default null, 279 | p_include_call_stack in varchar2 default null, 280 | p_client_id_expire_hours in number default null 281 | ); 282 | 283 | procedure unset_client_level(p_client_id in varchar2); 284 | 285 | procedure unset_client_level; 286 | 287 | procedure unset_client_level_all; 288 | 289 | 290 | procedure append_param( 291 | p_params in out nocopy logger.tab_param, 292 | p_name in varchar2, 293 | p_val in varchar2); 294 | 295 | procedure append_param( 296 | p_params in out nocopy logger.tab_param, 297 | p_name in varchar2, 298 | p_val in number); 299 | 300 | procedure append_param( 301 | p_params in out nocopy logger.tab_param, 302 | p_name in varchar2, 303 | p_val in date); 304 | 305 | procedure append_param( 306 | p_params in out nocopy logger.tab_param, 307 | p_name in varchar2, 308 | p_val in timestamp); 309 | 310 | procedure append_param( 311 | p_params in out nocopy logger.tab_param, 312 | p_name in varchar2, 313 | p_val in timestamp with time zone); 314 | 315 | procedure append_param( 316 | p_params in out nocopy logger.tab_param, 317 | p_name in varchar2, 318 | p_val in timestamp with local time zone); 319 | 320 | procedure append_param( 321 | p_params in out nocopy logger.tab_param, 322 | p_name in varchar2, 323 | p_val in boolean); 324 | 325 | function ok_to_log(p_level in number) 326 | return boolean; 327 | 328 | function ok_to_log(p_level in varchar2) 329 | return boolean; 330 | 331 | function tochar( 332 | p_val in number) 333 | return varchar2; 334 | 335 | function tochar( 336 | p_val in date) 337 | return varchar2; 338 | 339 | function tochar( 340 | p_val in timestamp) 341 | return varchar2; 342 | 343 | function tochar( 344 | p_val in timestamp with time zone) 345 | return varchar2; 346 | 347 | function tochar( 348 | p_val in timestamp with local time zone) 349 | return varchar2; 350 | 351 | function tochar( 352 | p_val in boolean) 353 | return varchar2; 354 | 355 | procedure ins_logger_logs( 356 | p_logger_level in logger_logs.logger_level%type, 357 | p_text in varchar2 default null, -- Not using type since want to be able to pass in 32767 characters 358 | p_scope in logger_logs.scope%type default null, 359 | p_call_stack in logger_logs.call_stack%type default null, 360 | p_unit_name in logger_logs.unit_name%type default null, 361 | p_line_no in logger_logs.line_no%type default null, 362 | p_extra in logger_logs.extra%type default null, 363 | po_id out nocopy logger_logs.id%type 364 | ); 365 | 366 | 367 | function sprintf( 368 | p_str in varchar2, 369 | p_s1 in varchar2 default null, 370 | p_s2 in varchar2 default null, 371 | p_s3 in varchar2 default null, 372 | p_s4 in varchar2 default null, 373 | p_s5 in varchar2 default null, 374 | p_s6 in varchar2 default null, 375 | p_s7 in varchar2 default null, 376 | p_s8 in varchar2 default null, 377 | p_s9 in varchar2 default null, 378 | p_s10 in varchar2 default null) 379 | return varchar2; 380 | 381 | function get_plugin_rec( 382 | p_logger_level in logger_logs.logger_level%type) 383 | return logger.rec_logger_log; 384 | end logger; 385 | / 386 | -------------------------------------------------------------------------------- /source/packages/logger_test.pkb: -------------------------------------------------------------------------------- 1 | create or replace package body logger_test 2 | as 3 | 4 | -- CONTANTS 5 | gc_line_feed constant varchar2(1) := chr(10); 6 | gc_unknown_err constant varchar2(50) := 'Unknown error'; 7 | gc_client_id constant varchar2(30) := 'test_client_id'; -- Consistent client id to use 8 | 9 | 10 | -- GLOBAL VARIABLES 11 | g_proc_name varchar2(30); -- current proc name being tested 12 | 13 | 14 | -- UTILITY PROCS 15 | procedure util_add_error( 16 | p_error in varchar2) 17 | as 18 | l_err logger_test.rec_error; 19 | begin 20 | l_err.proc_name := g_proc_name; 21 | l_err.error := p_error; 22 | g_errors(g_errors.count + 1) := l_err; 23 | end util_add_error; 24 | 25 | /** 26 | * Setups test 27 | * 28 | * Notes: 29 | * - 30 | * 31 | * Related Tickets: 32 | * - 33 | * 34 | * @author Martin D'Souza 35 | * @created 28-Feb-2015 36 | */ 37 | procedure util_test_setup 38 | as 39 | table_does_not_exist exception; 40 | pragma exception_init(table_does_not_exist, -942); 41 | begin 42 | -- Drop table if it still exists 43 | begin 44 | execute immediate 'drop table logger_prefs_tmp'; 45 | exception 46 | when table_does_not_exist then 47 | null; 48 | end; 49 | 50 | -- Create temp logger_prefs table 51 | execute immediate 'create table logger_prefs_tmp as select * from logger_prefs'; 52 | 53 | -- Reset client_id 54 | dbms_session.set_identifier(null); 55 | 56 | -- Reset all contexts 57 | logger.null_global_contexts; 58 | 59 | -- Reset timers 60 | logger.time_reset; 61 | end util_test_setup; 62 | 63 | 64 | /** 65 | * Setups test 66 | * 67 | * Notes: 68 | * - 69 | * 70 | * Related Tickets: 71 | * - 72 | * 73 | * @author Martin D'Souza 74 | * @created 28-Feb-2015 75 | */ 76 | procedure util_test_teardown 77 | as 78 | l_count pls_integer; 79 | begin 80 | -- Make sure logger_prefs_tmp table exists 81 | select count(1) 82 | into l_count 83 | from user_tables 84 | where table_name = 'LOGGER_PREFS_TMP'; 85 | 86 | if l_count = 1 then 87 | 88 | delete from logger_prefs; 89 | 90 | -- Need to do an execute immediate here since logger_prefs_tmp doesn't always exist 91 | execute immediate 'insert into logger_prefs select * from logger_prefs_tmp'; 92 | 93 | execute immediate 'drop table logger_prefs_tmp'; 94 | end if; 95 | 96 | dbms_session.set_identifier(null); 97 | 98 | -- Reset timers 99 | logger.time_reset; 100 | end util_test_teardown; 101 | 102 | 103 | /** 104 | * Displays errors 105 | * 106 | * Notes: 107 | * - 108 | * 109 | * Related Tickets: 110 | * - 111 | * 112 | * @author Martin D'Souza 113 | * @created 28-Feb-2015 114 | */ 115 | procedure util_display_errors 116 | as 117 | l_index pls_integer; 118 | begin 119 | 120 | if g_errors.count > 0 then 121 | dbms_output.put_line('*** ERRORS ***'); 122 | 123 | l_index := g_errors.first; 124 | 125 | while true loop 126 | dbms_output.put_line(g_errors(l_index).proc_name || ': ' || g_errors(l_index).error); 127 | 128 | l_index := g_errors.next(l_index); 129 | 130 | if l_index is null then 131 | exit; 132 | end if; 133 | end loop; 134 | else 135 | dbms_output.put_line('No errors.'); 136 | end if; 137 | end util_display_errors; 138 | 139 | 140 | /** 141 | * Returns unique scope 142 | * 143 | * Notes: 144 | * - This is useful when trying to back reference which log was just inserted 145 | * - Should look in logger_logs_5_mins since recent 146 | * 147 | * Related Tickets: 148 | * - 149 | * 150 | * @author Martin D'Souza 151 | * @created 2-Mar-2015 152 | */ 153 | function util_get_unique_scope 154 | return varchar2 155 | as 156 | begin 157 | return lower('logger_test_' || dbms_random.string('x',20)); 158 | end util_get_unique_scope; 159 | 160 | -- *** TESTS *** 161 | 162 | procedure is_number 163 | as 164 | begin 165 | g_proc_name := 'is_number'; 166 | 167 | if logger.is_number(p_str => 'a') then 168 | util_add_error('not failing on letter'); 169 | end if; 170 | 171 | if not logger.is_number(p_str => '1') then 172 | util_add_error('not failing on number'); 173 | end if; 174 | end is_number; 175 | 176 | 177 | 178 | 179 | procedure assert 180 | as 181 | begin 182 | g_proc_name := 'assert'; 183 | 184 | begin 185 | logger.assert(1=1, 'message'); 186 | exception 187 | when others then 188 | util_add_error('1=1 is failing when it shouldnt be'); 189 | end; 190 | 191 | -- Fail on purpose to ensure error is raised 192 | begin 193 | logger.assert(1=2, 'message'); 194 | 195 | -- If assert works, should never get to this point 196 | util_add_error('1=2 is not failing when it should'); 197 | 198 | exception 199 | when others then 200 | if sqlerrm != 'ORA-20000: message' then 201 | util_add_error('Invalid error message'); 202 | end if; 203 | end; 204 | end assert; 205 | 206 | 207 | procedure get_param_clob 208 | as 209 | l_params logger.tab_param; 210 | l_clob clob; 211 | begin 212 | g_proc_name := 'get_param_clob'; 213 | 214 | logger.append_param(l_params, 'p_test1', 'test1'); 215 | logger.append_param(l_params, 'p_test2', 'test2'); 216 | 217 | l_clob := logger.get_param_clob(p_params => l_params); 218 | 219 | if l_clob != 'p_test1: test1' || gc_line_feed || 'p_test2: test2' then 220 | util_add_error('Not displaying correctly'); 221 | end if; 222 | end get_param_clob; 223 | 224 | 225 | procedure save_global_context 226 | as 227 | begin 228 | g_proc_name := 'save_global_context'; 229 | 230 | -- Reset client_id 231 | dbms_session.set_identifier(null); 232 | logger.save_global_context( 233 | p_attribute => 'TEST', 234 | p_value => 'test_value', 235 | p_client_id => null); 236 | 237 | if sys_context(logger.g_context_name, 'TEST') != 'test_value' then 238 | util_add_error('Context not setting (globally);'); 239 | end if; 240 | 241 | -- Test for client_id 242 | dbms_session.set_identifier(gc_client_id); 243 | logger.save_global_context( 244 | p_attribute => 'TEST', 245 | p_value => 'test_client_id', 246 | p_client_id => gc_client_id); 247 | 248 | if sys_context(logger.g_context_name, 'TEST') != 'test_client_id' then 249 | util_add_error('Context not setting (client_id);'); 250 | end if; 251 | end save_global_context; 252 | 253 | procedure set_extra_with_params 254 | as 255 | l_clob logger_logs.extra%type; 256 | l_return logger_logs.extra%type; 257 | l_params logger.tab_param; 258 | begin 259 | g_proc_name := 'set_extra_with_params'; 260 | 261 | -- Test empty params 262 | l_clob := 'test'; 263 | l_return := logger.set_extra_with_params( 264 | p_extra => l_clob, 265 | p_params => l_params); 266 | 267 | if l_return != 'test' then 268 | util_add_error('empty params test failed'); 269 | end if; 270 | 271 | -- Test one param 272 | logger.append_param(l_params, 'p_test1', 'test1'); 273 | l_return := logger.set_extra_with_params( 274 | p_extra => l_clob, 275 | p_params => l_params); 276 | 277 | if l_return != 278 | 'test 279 | 280 | *** Parameters *** 281 | 282 | p_test1: test1' then 283 | util_add_error('failed with one param'); 284 | end if; 285 | 286 | -- Test 2 params 287 | logger.append_param(l_params, 'p_test2', 'test2'); 288 | l_return := logger.set_extra_with_params( 289 | p_extra => l_clob, 290 | p_params => l_params); 291 | 292 | if l_return != 293 | 'test 294 | 295 | *** Parameters *** 296 | 297 | p_test1: test1 298 | p_test2: test2' then 299 | util_add_error('failed with 2 params'); 300 | end if; 301 | 302 | end set_extra_with_params; 303 | 304 | 305 | procedure get_sys_context 306 | as 307 | l_clob clob; 308 | begin 309 | g_proc_name := 'get_sys_context'; 310 | 311 | l_clob := logger.get_sys_context( 312 | p_detail_level => 'USER', 313 | p_vertical => false, 314 | p_show_null => true); 315 | -- The output from this is very specific to the user/setup so just going to check for any errors raised 316 | 317 | exception 318 | when others then 319 | util_add_error(gc_unknown_err); 320 | end get_sys_context; 321 | 322 | 323 | procedure admin_security_check 324 | as 325 | l_bool boolean; 326 | begin 327 | g_proc_name := 'admin_security_check'; 328 | 329 | -- Test simple case 330 | update logger_prefs 331 | set pref_value = 'FALSE' 332 | where 1=1 333 | and pref_type = logger.g_pref_type_logger 334 | and pref_name = 'PROTECT_ADMIN_PROCS'; 335 | 336 | l_bool := logger.admin_security_check; 337 | 338 | if not l_bool then 339 | util_add_error('FALSE failing'); 340 | end if; 341 | 342 | -- Test when install schema is same as current schema. This should still pass 343 | update logger_prefs 344 | set pref_value = 'TRUE' 345 | where 1=1 346 | and pref_type = logger.g_pref_type_logger 347 | and pref_name = 'PROTECT_ADMIN_PROCS'; 348 | 349 | update logger_prefs 350 | set pref_value = sys_context('USERENV','SESSION_USER') 351 | where 1=1 352 | and pref_type = logger.g_pref_type_logger 353 | and pref_name = 'INSTALL_SCHEMA'; 354 | 355 | l_bool := logger.admin_security_check; 356 | 357 | if not l_bool then 358 | util_add_error('Failing when set to true and user is same as INSTALL_SCHEMA'); 359 | end if; 360 | 361 | -- Test when install schema is different as current schema (still set to TRUE) 362 | update logger_prefs 363 | set pref_value = 'DUMMY' 364 | where 1=1 365 | and pref_type = logger.g_pref_type_logger 366 | and pref_name = 'INSTALL_SCHEMA'; 367 | 368 | begin 369 | -- This should raise an exception 370 | l_bool := logger.admin_security_check; 371 | 372 | -- If got to this point then issue 373 | util_add_error('TRUE failing when different schema (not raising exception)'); 374 | exception 375 | when others then 376 | if sqlcode != -20000 then 377 | util_add_error('TRUE failing when differen schema (invalid error code)'); 378 | end if; 379 | end; 380 | 381 | end admin_security_check; 382 | 383 | 384 | procedure get_level_number 385 | as 386 | l_level number; 387 | begin 388 | g_proc_name := 'get_level_number'; 389 | 390 | update logger_prefs 391 | set pref_value = 'DEBUG' 392 | where 1=1 393 | and pref_type = logger.g_pref_type_logger 394 | and pref_name = 'LEVEL'; 395 | 396 | l_level := logger.get_level_number; 397 | 398 | if l_level != logger.g_debug then 399 | util_add_error('Level number not matching'); 400 | end if; 401 | 402 | -- Client level Test 403 | dbms_session.set_identifier(gc_client_id); 404 | logger.set_level( 405 | p_level => logger.g_error, 406 | p_client_id => sys_context('userenv','client_identifier') 407 | ); 408 | l_level := logger.get_level_number; 409 | 410 | if l_level != logger.g_error then 411 | util_add_error('Invalid clientid level'); 412 | end if; 413 | end get_level_number; 414 | 415 | 416 | procedure include_call_stack 417 | as 418 | begin 419 | g_proc_name := 'include_call_stack'; 420 | 421 | update logger_prefs 422 | set pref_value = 'TRUE' 423 | where 1=1 424 | and pref_type = logger.g_pref_type_logger 425 | and pref_name = 'INCLUDE_CALL_STACK'; 426 | 427 | if not logger.include_call_stack then 428 | util_add_error('Faling on true'); 429 | end if; 430 | 431 | update logger_prefs 432 | set pref_value = 'FALSE' 433 | where 1=1 434 | and pref_type = logger.g_pref_type_logger 435 | and pref_name = 'INCLUDE_CALL_STACK'; 436 | 437 | -- reset contexts so that it looks at new one (could have called Logger.configure but more than what I need here) 438 | logger.null_global_contexts; 439 | 440 | if logger.include_call_stack then 441 | util_add_error('Faling on false'); 442 | end if; 443 | 444 | -- Test with client 445 | dbms_session.set_identifier(gc_client_id); 446 | logger.set_level( 447 | p_level => logger.g_debug, 448 | p_client_id => gc_client_id, 449 | p_include_call_stack => 'TRUE' 450 | ); 451 | 452 | if not logger.include_call_stack then 453 | util_add_error('Faling on true (client_id)'); 454 | end if; 455 | 456 | end include_call_stack; 457 | 458 | 459 | procedure date_text_format_base 460 | as 461 | l_start date; 462 | l_stop date; 463 | begin 464 | g_proc_name := 'date_text_format_base'; 465 | 466 | -- Test Seconds 467 | l_start := to_date('10-Jan-2015 20:40:10', 'DD-MON-YYYY HH24:MI:SS'); 468 | l_stop := to_date('10-Jan-2015 20:40:20', 'DD-MON-YYYY HH24:MI:SS'); 469 | if logger.date_text_format_base ( 470 | p_date_start => l_start, 471 | p_date_stop => l_stop) != '10 seconds ago' then 472 | util_add_error('Error with seconds'); 473 | end if; 474 | 475 | -- Test Minutes 476 | l_start := to_date('10-Jan-2015 20:30', 'DD-MON-YYYY HH24:MI'); 477 | l_stop := to_date('10-Jan-2015 20:40', 'DD-MON-YYYY HH24:MI'); 478 | if logger.date_text_format_base ( 479 | p_date_start => l_start, 480 | p_date_stop => l_stop) != '10 minutes ago' then 481 | util_add_error('Error with minutes'); 482 | end if; 483 | 484 | -- Test Hours (and that it's 1 hour not 1 hours) 485 | l_start := to_date('10-Jan-2015 20:30', 'DD-MON-YYYY HH24:MI'); 486 | l_stop := to_date('10-Jan-2015 21:40', 'DD-MON-YYYY HH24:MI'); 487 | if logger.date_text_format_base ( 488 | p_date_start => l_start, 489 | p_date_stop => l_stop) != '1 hour ago' then 490 | util_add_error('Error with hours'); 491 | end if; 492 | 493 | -- Test Days 494 | l_start := to_date('10-Jan-2015 20:30', 'DD-MON-YYYY HH24:MI'); 495 | l_stop := to_date('12-Jan-2015 20:40', 'DD-MON-YYYY HH24:MI'); 496 | if logger.date_text_format_base ( 497 | p_date_start => l_start, 498 | p_date_stop => l_stop) != '2 days ago' then 499 | util_add_error('Error with days'); 500 | end if; 501 | 502 | -- Test Weeks 503 | l_start := to_date('10-Jan-2015 20:30', 'DD-MON-YYYY HH24:MI'); 504 | l_stop := to_date('30-Jan-2015 20:40', 'DD-MON-YYYY HH24:MI'); 505 | if logger.date_text_format_base ( 506 | p_date_start => l_start, 507 | p_date_stop => l_stop) != '2 weeks ago' then 508 | util_add_error('Error with weeks'); 509 | end if; 510 | 511 | -- Test Months 512 | l_start := to_date('10-Jan-2015 20:30', 'DD-MON-YYYY HH24:MI'); 513 | l_stop := to_date('11-Mar-2015 20:40', 'DD-MON-YYYY HH24:MI'); 514 | if logger.date_text_format_base ( 515 | p_date_start => l_start, 516 | p_date_stop => l_stop) != '2 months ago' then 517 | util_add_error('Error with months'); 518 | end if; 519 | 520 | -- Test Years 521 | l_start := to_date('10-Jan-2015 20:30', 'DD-MON-YYYY HH24:MI'); 522 | l_stop := to_date('11-Mar-2016 20:40', 'DD-MON-YYYY HH24:MI'); 523 | if logger.date_text_format_base ( 524 | p_date_start => l_start, 525 | p_date_stop => l_stop) != '1.2 years ago' then 526 | util_add_error('Error with years'); 527 | end if; 528 | 529 | end date_text_format_base; 530 | 531 | 532 | -- Will not test date_text_format since it's dependant on current date and uses date_text_format_base 533 | 534 | -- Will not test get_debug_info since it's too specific to where it's being called 535 | 536 | procedure log_internal 537 | as 538 | l_params logger.tab_param; 539 | l_scope logger_logs.scope%type; 540 | l_row logger_logs_5_min%rowtype; 541 | 542 | begin 543 | g_proc_name := 'log_internal'; 544 | 545 | logger.append_param(l_params, 'p_test1', 'test1'); 546 | 547 | -- Set the level to error then log at debug. 548 | -- Should still register since log_internal doesn't check ok_to_log (which is as expected) 549 | logger.set_level(p_level => logger.g_error); 550 | 551 | l_scope := util_get_unique_scope; 552 | logger.log_internal( 553 | p_text => 'test', 554 | p_log_level => logger.g_debug, 555 | p_scope => l_scope, 556 | p_extra => 'extra', 557 | p_callstack => null, 558 | p_params => l_params); 559 | 560 | select * 561 | into l_row 562 | from logger_logs_5_min 563 | where 1=1 564 | and scope = l_scope; 565 | 566 | if l_row.text != 'test' then 567 | util_add_error('text failed'); 568 | end if; 569 | 570 | if l_row.logger_level != logger.g_debug then 571 | util_add_error('Level failed'); 572 | end if; 573 | 574 | if l_row.extra != 575 | 'extra 576 | 577 | *** Parameters *** 578 | 579 | p_test1: test1' then 580 | util_add_error('Extra Failed'); 581 | end if; 582 | 583 | -- Add test to make sure other columns aren't null? 584 | 585 | 586 | end log_internal; 587 | 588 | 589 | 590 | -- *** PUBLIC *** -- 591 | 592 | 593 | procedure null_global_contexts 594 | as 595 | begin 596 | g_proc_name := 'null_global_contexts'; 597 | 598 | -- Null values 599 | logger.null_global_contexts; 600 | 601 | if 1=2 602 | or sys_context(logger.g_context_name,'level') is not null 603 | or sys_context(logger.g_context_name,'include_call_stack') is not null 604 | or sys_context(logger.g_context_name,'plugin_fn_error') is not null 605 | then 606 | util_add_error('Contexts still contain values when they shouldnt'); 607 | end if; 608 | 609 | 610 | end null_global_contexts; 611 | 612 | 613 | procedure convert_level_char_to_num 614 | as 615 | begin 616 | g_proc_name := 'convert_level_char_to_num'; 617 | 618 | if logger.convert_level_char_to_num(p_level => logger.g_error_name) != logger.g_error then 619 | util_add_error('Not converting properly'); 620 | end if; 621 | end convert_level_char_to_num; 622 | 623 | 624 | procedure convert_level_num_to_char 625 | as 626 | begin 627 | g_proc_name := 'convert_level_num_to_char'; 628 | 629 | if logger.convert_level_num_to_char(p_level => logger.g_information) != logger.g_information_name then 630 | util_add_error('Not converting properly'); 631 | end if; 632 | end convert_level_num_to_char; 633 | 634 | 635 | procedure get_character_codes 636 | as 637 | l_temp varchar2(1000); 638 | begin 639 | g_proc_name := 'get_character_codes'; 640 | 641 | l_temp := logger.get_character_codes( 642 | p_string => 643 | 'Test 644 | new line', 645 | p_show_common_codes => false); 646 | 647 | if l_temp != 648 | ' 84,101,115,116, 10,110,101,119, 32,108,105,110,101 649 | T, e, s, t, ~, n, e, w, , l, i, n, e' then 650 | util_add_error('Failed on show common codes false'); 651 | end if; 652 | 653 | l_temp := logger.get_character_codes( 654 | p_string => 655 | 'Test 656 | new line', 657 | p_show_common_codes => true); 658 | 659 | if l_temp != 660 | 'Common Codes: 13=Line Feed, 10=Carriage Return, 32=Space, 9=Tab 661 | 84,101,115,116, 10,110,101,119, 32,108,105,110,101 662 | T, e, s, t, ~, n, e, w, , l, i, n, e' then 663 | util_add_error('Failed on show common codes true'); 664 | end if; 665 | end get_character_codes; 666 | 667 | -- FUTURE mdsouza: Add test for get_debug_info 668 | 669 | procedure ok_to_log 670 | as 671 | l_bool boolean; 672 | test_type dbms_sql.varchar2_table; 673 | begin 674 | g_proc_name := 'ok_to_log'; 675 | 676 | test_type(1) := 'global'; 677 | test_type(2) := 'client'; 678 | 679 | for i in test_type.first .. test_type.last loop 680 | -- for client reset global to debug then set client to error 681 | if test_type(i) = 'global' then 682 | logger.set_level(p_level => logger.g_error); 683 | else 684 | -- Client 685 | -- Reset global level 686 | logger.set_level(p_level => logger.g_debug); 687 | 688 | dbms_session.set_identifier(gc_client_id); 689 | logger.set_level( 690 | p_level => logger.g_error, 691 | p_client_id => gc_client_id); 692 | end if; 693 | 694 | -- Tests 695 | -- Should be false since lower 696 | if logger.ok_to_log(p_level => logger.g_debug) then 697 | util_add_error('not registering lower levels. Test Type: ' || test_type(i)); 698 | end if; 699 | 700 | -- Should be true 701 | if not logger.ok_to_log(p_level => logger.g_error) then 702 | util_add_error('failing when same level. Test Type: ' || test_type(i)); 703 | end if; 704 | 705 | -- Should be true 706 | if not logger.ok_to_log(p_level => logger.g_permanent) then 707 | util_add_error('failing when higher level. Test Type: ' || test_type(i)); 708 | end if; 709 | 710 | 711 | end loop; 712 | 713 | end ok_to_log; 714 | 715 | -- ok_to_log (varchar2): Not running since it's a wrapper 716 | 717 | 718 | -- snapshot_apex_items not going to be tested for now 719 | 720 | procedure log_error 721 | as 722 | l_scope logger_logs.scope%type := util_get_unique_scope; 723 | l_count pls_integer; 724 | l_row logger_logs_5_min%rowtype; 725 | begin 726 | g_proc_name := 'log_error'; 727 | 728 | -- Should not log 729 | logger.set_level(p_level => logger.g_permanent); 730 | logger.log_error('test', l_scope); 731 | 732 | select count(1) 733 | into l_count 734 | from logger_logs_5_min 735 | where 1=1 736 | and scope = l_scope; 737 | 738 | if l_count > 0 then 739 | util_add_error('logging error when shouldnt'); 740 | end if; 741 | 742 | 743 | logger.set_level(p_level => logger.g_debug); 744 | logger.log_error('test', l_scope); 745 | 746 | -- Reset callstack context and set pref to false to ensure that callstack is still set even though this setting is false 747 | update logger_prefs 748 | set pref_value = 'FALSE' 749 | where 1=1 750 | and pref_type = logger.g_pref_type_logger 751 | and pref_name = 'INCLUDE_CALL_STACK'; 752 | 753 | -- Wipe the sys context so that it reloads 754 | logger.save_global_context( 755 | p_attribute => 'include_call_stack', 756 | p_value => null); 757 | 758 | begin 759 | select * 760 | into l_row 761 | from logger_logs_5_min 762 | where 1=1 763 | and scope = l_scope; 764 | 765 | if l_row.call_stack is null then 766 | util_add_error('Callstack is empty when it should always have a value'); 767 | end if; 768 | exception 769 | when no_data_found then 770 | util_add_error('not logging'); 771 | end; 772 | end log_error; 773 | 774 | -- Test all log functions (except for log_error) 775 | procedure log_all_logs 776 | as 777 | type rec_log_fn is record( 778 | fn_name varchar2(30), 779 | level_off number, 780 | level_self number, 781 | level_on number 782 | ); 783 | 784 | type tab_log_fn is table of rec_log_fn index by pls_integer; 785 | 786 | 787 | l_log_fns tab_log_fn; 788 | 789 | l_scope logger_logs.scope%type; 790 | l_count pls_integer; 791 | l_sql varchar2(255); 792 | 793 | function get_log_fn( 794 | p_fn_name varchar2, 795 | p_level_off number, 796 | p_level_self number, 797 | p_level_on number) 798 | return rec_log_fn 799 | as 800 | l_log_fn rec_log_fn; 801 | l_count pls_integer; 802 | begin 803 | l_log_fn.fn_name := p_fn_name; 804 | l_log_fn.level_off := p_level_off; 805 | l_log_fn.level_self := p_level_self; 806 | l_log_fn.level_on := p_level_on; 807 | 808 | return l_log_fn; 809 | end get_log_fn; 810 | 811 | begin 812 | 813 | 814 | l_log_fns(l_log_fns.count + 1) := get_log_fn('log_permanent', logger.g_off, logger.g_permanent, logger.g_debug); 815 | l_log_fns(l_log_fns.count + 1) := get_log_fn('log_warning', logger.g_error, logger.g_warning, logger.g_debug); 816 | l_log_fns(l_log_fns.count + 1) := get_log_fn('log_information', logger.g_warning, logger.g_information, logger.g_debug); 817 | l_log_fns(l_log_fns.count + 1) := get_log_fn('log', logger.g_warning, logger.g_debug, logger.g_debug); 818 | 819 | for i in l_log_fns.first .. l_log_fns.last loop 820 | g_proc_name := l_log_fns(i).fn_name; 821 | 822 | for x in ( 823 | select regexp_substr('off:self:on','[^:]+', 1, level) action 824 | from dual 825 | connect by regexp_substr('off:self:on', '[^:]+', 1, level) is not null 826 | ) loop 827 | 828 | if x.action = 'off' then 829 | -- Test off 830 | logger.set_level(l_log_fns(i).level_off); 831 | elsif x.action = 'self' then 832 | -- Test self 833 | logger.set_level(l_log_fns(i).level_self); 834 | elsif x.action = 'on' then 835 | -- Test on 836 | logger.set_level(l_log_fns(i).level_on); 837 | end if; 838 | 839 | l_scope := util_get_unique_scope; 840 | l_sql := 'begin logger.' || l_log_fns(i).fn_name || q'!('test', :scope); end;!'; 841 | execute immediate l_sql using l_scope; 842 | 843 | select count(1) 844 | into l_count 845 | from logger_logs_5_min 846 | where 1=1 847 | and scope = l_scope; 848 | 849 | if 1=2 850 | or (x.action = 'off' and l_count != 0) 851 | or (x.action in ('self', 'on') and l_count != 1) then 852 | util_add_error(l_log_fns(i).fn_name || ' failed test: ' || x.action); 853 | end if; 854 | end loop; -- x 855 | 856 | end loop; 857 | 858 | end log_all_logs; 859 | 860 | 861 | -- get_cgi_env requires http connection so no tests for now (can simulate in future) 862 | 863 | -- log_userenv: Dependant on get_sys_context which varies for each system 864 | 865 | -- log_cgi_env: Same as above 866 | 867 | -- log_character_codes: covered in get_character_codes 868 | 869 | -- log_apex_items: Future / dependant on APEX instance 870 | 871 | procedure time_start 872 | as 873 | l_unit_name logger_logs.unit_name%type := util_get_unique_scope; 874 | l_text logger_logs.text%type; 875 | begin 876 | g_proc_name := 'time_start'; 877 | 878 | logger.set_level(logger.g_timing); 879 | 880 | logger.time_start( 881 | p_unit => l_unit_name, 882 | p_log_in_table => true 883 | ); 884 | 885 | select max(text) 886 | into l_text 887 | from logger_logs_5_min 888 | where 1=1 889 | and unit_name = upper(l_unit_name); 890 | 891 | if l_text is null or l_text != 'START: ' || l_unit_name then 892 | util_add_error('Logged text invalid: ' || l_text); 893 | end if; 894 | 895 | end time_start; 896 | 897 | 898 | procedure time_stop 899 | as 900 | l_unit_name logger_logs.unit_name%type := util_get_unique_scope; 901 | l_scope logger_logs.scope%type := util_get_unique_scope; 902 | l_text logger_logs.text%type; 903 | l_sleep_time number := 1; 904 | begin 905 | g_proc_name := 'time_stop'; 906 | 907 | logger.set_level(logger.g_debug); -- Time stop only requires g_debug 908 | 909 | logger.time_start( 910 | p_unit => l_unit_name, 911 | p_log_in_table => false 912 | ); 913 | 914 | apex_util.pause(l_sleep_time + 0.1); 915 | 916 | logger.time_stop( 917 | p_unit => l_unit_name, 918 | p_scope => l_scope 919 | ); 920 | 921 | select max(text) 922 | into l_text 923 | from logger_logs_5_min 924 | where 1=1 925 | and scope = l_scope; 926 | 927 | dbms_output.put_line(l_text); 928 | if l_text is null or l_text not like 'STOP : ' || l_unit_name || ' - 00:00:0' || l_sleep_time || '%' then 929 | util_add_error('Issue with text: ' || l_text); 930 | end if; 931 | 932 | end time_stop; 933 | 934 | 935 | procedure time_stop_fn 936 | as 937 | l_unit_name logger_logs.unit_name%type := util_get_unique_scope; 938 | l_sleep_time number := 2; 939 | l_text varchar2(50); 940 | begin 941 | g_proc_name := 'time_stop (function)'; 942 | 943 | logger.set_level(logger.g_debug); 944 | 945 | logger.time_start( 946 | p_unit => l_unit_name, 947 | p_log_in_table => false 948 | ); 949 | 950 | apex_util.pause(l_sleep_time + 0.1); 951 | 952 | l_text := logger.time_stop(p_unit => l_unit_name); 953 | 954 | if l_text is null or l_text not like '00:00:0' || l_sleep_time || '%' then 955 | util_add_error('Issue with return: ' || l_text); 956 | end if; 957 | 958 | end time_stop_fn; 959 | 960 | 961 | procedure time_stop_seconds 962 | as 963 | l_unit_name logger_logs.unit_name%type := util_get_unique_scope; 964 | l_sleep_time number := 2; 965 | l_text varchar2(50); 966 | begin 967 | g_proc_name := 'time_stop_seconds'; 968 | 969 | logger.set_level(logger.g_debug); 970 | 971 | logger.time_start( 972 | p_unit => l_unit_name, 973 | p_log_in_table => false 974 | ); 975 | 976 | apex_util.pause(l_sleep_time + 0.05); 977 | 978 | l_text := logger.time_stop_seconds(p_unit => l_unit_name); 979 | 980 | if l_text is null or l_text not like l_sleep_time || '.0%' then 981 | util_add_error('Issue with return: ' || l_text); 982 | end if; 983 | 984 | end time_stop_seconds; 985 | 986 | 987 | -- time_reset: won't test for now 988 | 989 | procedure get_pref 990 | as 991 | l_pref logger_prefs.pref_value%type; 992 | begin 993 | g_proc_name := 'get_pref'; 994 | 995 | logger.set_level(p_level => logger.g_debug); 996 | 997 | l_pref := nvl(logger.get_pref('LEVEL'), 'a'); 998 | if l_pref != logger.g_debug_name then 999 | util_add_error('Global level not fetching correctly'); 1000 | end if; 1001 | 1002 | dbms_session.set_identifier(gc_client_id); 1003 | logger.set_level( 1004 | p_level => logger.g_warning, 1005 | p_client_id => gc_client_id); 1006 | l_pref := nvl(logger.get_pref('LEVEL'), 'a'); 1007 | if l_pref != logger.g_warning_name then 1008 | util_add_error('Client pref not correct'); 1009 | end if; 1010 | 1011 | end get_pref; 1012 | 1013 | -- purge 1014 | 1015 | procedure purge_all 1016 | as 1017 | l_count pls_integer; 1018 | begin 1019 | g_proc_name := 'purge_all'; 1020 | 1021 | logger.set_level(p_level => logger.g_debug); 1022 | logger.log('test'); 1023 | 1024 | logger.purge_all; 1025 | 1026 | select count(1) 1027 | into l_count 1028 | from logger_logs 1029 | where 1=1 1030 | and logger_level > logger.g_permanent; 1031 | 1032 | if l_count > 0 then 1033 | util_add_error('Non permanent records being kept.'); 1034 | end if; 1035 | end purge_all; 1036 | 1037 | -- status: Won't test since no real easy way to test output 1038 | 1039 | 1040 | procedure set_level 1041 | as 1042 | l_scope logger_logs.scope%type; 1043 | l_count pls_integer; 1044 | l_call_stack logger_logs.call_stack%type; 1045 | 1046 | procedure log_and_count 1047 | as 1048 | begin 1049 | l_scope := util_get_unique_scope; 1050 | logger.log('test', l_scope); 1051 | 1052 | select count(1) 1053 | into l_count 1054 | from logger_logs_5_min 1055 | where scope = l_scope; 1056 | end log_and_count; 1057 | 1058 | begin 1059 | g_proc_name := 'set_level'; 1060 | 1061 | logger.set_level(p_level => logger.g_debug); 1062 | 1063 | 1064 | log_and_count; 1065 | if l_count != 1 then 1066 | util_add_error('Not logging debug'); 1067 | end if; 1068 | 1069 | logger.set_level(p_level => logger.g_error); 1070 | log_and_count; 1071 | if l_count != 0 then 1072 | util_add_error('Logging when shouldnt be'); 1073 | end if; 1074 | 1075 | -- Test client specific 1076 | dbms_session.set_identifier(gc_client_id); 1077 | 1078 | 1079 | -- Disable logging globally then set on for client 1080 | logger.set_level(p_level => logger.g_error); 1081 | logger.set_level( 1082 | p_level => logger.g_debug, 1083 | p_client_id => gc_client_id, 1084 | p_include_call_stack => 'TRUE'); 1085 | 1086 | log_and_count; 1087 | if l_count != 1 then 1088 | util_add_error('Not logging for client'); 1089 | else 1090 | -- Test callstack 1091 | select call_stack 1092 | into l_call_stack 1093 | from logger_logs_5_min 1094 | where scope = l_scope; 1095 | 1096 | if l_call_stack is null then 1097 | util_add_error('Callstack not being logged when it should be'); 1098 | end if; 1099 | end if; 1100 | 1101 | 1102 | -- Test callstack off 1103 | logger.set_level( 1104 | p_level => logger.g_debug, 1105 | p_client_id => gc_client_id, 1106 | p_include_call_stack => 'FALSE'); 1107 | 1108 | log_and_count; 1109 | if l_count = 1 then 1110 | -- Test callstack 1111 | select call_stack 1112 | into l_call_stack 1113 | from logger_logs_5_min 1114 | where scope = l_scope; 1115 | 1116 | if l_call_stack is not null then 1117 | util_add_error('Callstack being logged when it should not be'); 1118 | end if; 1119 | end if; 1120 | 1121 | 1122 | -- Testing unset_client_level here since structure is in place 1123 | g_proc_name := 'unset_client_level'; 1124 | 1125 | logger.set_level(p_level => logger.g_error); 1126 | logger.set_level( 1127 | p_level => logger.g_debug, 1128 | p_client_id => gc_client_id, 1129 | p_include_call_stack => 'TRUE'); 1130 | 1131 | logger.unset_client_level(p_client_id => gc_client_id); 1132 | log_and_count; 1133 | if l_count != 0 then 1134 | util_add_error('unset not succesful'); 1135 | end if; 1136 | 1137 | end set_level; 1138 | 1139 | 1140 | -- unset_client_level (tested above) 1141 | 1142 | -- unset_client_level 1143 | 1144 | -- unset_client_level_all 1145 | 1146 | -- sqlplus_format 1147 | 1148 | -- Test all tochar commands 1149 | procedure tochar 1150 | as 1151 | l_val varchar2(255); 1152 | begin 1153 | g_proc_name := 'tochar'; 1154 | 1155 | if logger.tochar(1) != '1' then 1156 | util_add_error('number'); 1157 | end if; 1158 | 1159 | l_val := logger.tochar(to_date('1-Jan-2013')); 1160 | if l_val != '01-JAN-2013 00:00:00' then 1161 | util_add_error('date: ' || l_val); 1162 | end if; 1163 | 1164 | l_val := logger.tochar(to_timestamp ('10-sep-02 14:10:10.123000', 'dd-mon-rr hh24:mi:ss.ff')); 1165 | if l_val != '10-SEP-2002 14:10:10:123000000' then 1166 | util_add_error('timestamp: ' || l_val); 1167 | end if; 1168 | 1169 | l_val := logger.tochar(to_timestamp_tz('1999-12-01 11:00:00 -8:00', 'yyyy-mm-dd hh:mi:ss tzh:tzm')); 1170 | if l_val != '01-DEC-1999 11:00:00:000000000 -08:00' then 1171 | util_add_error('timezone: ' || l_val); 1172 | end if; 1173 | 1174 | -- Local timezone based on above and is dependant on each system 1175 | 1176 | l_val := logger.tochar(true) || ':' || logger.tochar(false); 1177 | if l_val != 'TRUE:FALSE' then 1178 | util_add_error('boolean: ' || l_val); 1179 | end if; 1180 | 1181 | end tochar; 1182 | 1183 | 1184 | procedure append_param 1185 | as 1186 | l_params logger.tab_param; 1187 | begin 1188 | g_proc_name := 'append_param'; 1189 | 1190 | logger.append_param( 1191 | p_params => l_params, 1192 | p_name => 'test', 1193 | p_val => 'val'); 1194 | 1195 | if l_params.count != 1 then 1196 | util_add_error('Did not add'); 1197 | end if; 1198 | 1199 | if l_params(1).name != 'test' then 1200 | util_add_error('Name invalid'); 1201 | end if; 1202 | 1203 | if l_params(1).val != 'val' then 1204 | util_add_error('Val Invalid'); 1205 | end if; 1206 | end append_param; 1207 | 1208 | -- TODO: ins_logger_logs (to test post functions) 1209 | 1210 | -- TODO: get_fmt_msg are we adding it in here? 1211 | 1212 | /** 1213 | * Runs all the tests and displays errors 1214 | * 1215 | * Notes: 1216 | * - 1217 | * 1218 | * Related Tickets: 1219 | * - 1220 | * 1221 | * @author Martin D'Souza 1222 | * @created 28-Feb-2015 1223 | */ 1224 | procedure util_run_tests 1225 | as 1226 | l_error_null logger_test.tab_error; 1227 | begin 1228 | -- Reset error array 1229 | g_errors := l_error_null; 1230 | 1231 | -- Run tests 1232 | 1233 | -- Private 1234 | util_test_setup; is_number; util_test_teardown; 1235 | util_test_setup; assert; util_test_teardown; 1236 | util_test_setup; get_param_clob; util_test_teardown; 1237 | util_test_setup; save_global_context; util_test_teardown; 1238 | util_test_setup; set_extra_with_params; util_test_teardown; 1239 | util_test_setup; get_sys_context; util_test_teardown; 1240 | util_test_setup; admin_security_check; util_test_teardown; 1241 | util_test_setup; get_level_number; util_test_teardown; 1242 | util_test_setup; include_call_stack; util_test_teardown; 1243 | util_test_setup; date_text_format_base; util_test_teardown; 1244 | util_test_setup; log_internal; util_test_teardown; 1245 | 1246 | 1247 | -- Public 1248 | util_test_setup; null_global_contexts; util_test_teardown; 1249 | util_test_setup; convert_level_char_to_num; util_test_teardown; 1250 | util_test_setup; convert_level_num_to_char; util_test_teardown; 1251 | util_test_setup; get_character_codes; util_test_teardown; 1252 | util_test_setup; ok_to_log; util_test_teardown; 1253 | util_test_setup; log_error; util_test_teardown; 1254 | util_test_setup; log_all_logs; util_test_teardown; 1255 | util_test_setup; time_start; util_test_teardown; 1256 | util_test_setup; time_stop; util_test_teardown; 1257 | util_test_setup; time_stop_fn; util_test_teardown; 1258 | util_test_setup; time_stop_seconds; util_test_teardown; 1259 | util_test_setup; get_pref; util_test_teardown; 1260 | util_test_setup; purge_all; util_test_teardown; 1261 | util_test_setup; set_level; util_test_teardown; 1262 | util_test_setup; tochar; util_test_teardown; 1263 | util_test_setup; append_param; util_test_teardown; 1264 | 1265 | 1266 | -- Display errors 1267 | util_display_errors; 1268 | 1269 | end util_run_tests; 1270 | 1271 | end logger_test; 1272 | / 1273 | -------------------------------------------------------------------------------- /source/packages/logger_test.pks: -------------------------------------------------------------------------------- 1 | create or replace package logger_test 2 | as 3 | 4 | type rec_error is record( 5 | proc_name varchar2(30), 6 | error varchar2(4000)); 7 | 8 | type tab_error is table of rec_error index by binary_integer; 9 | 10 | g_errors logger_test.tab_error; 11 | 12 | procedure util_run_tests; 13 | 14 | end logger_test; 15 | / 16 | -------------------------------------------------------------------------------- /source/procedures/logger_configure.plb: -------------------------------------------------------------------------------- 1 | create or replace procedure logger_configure 2 | is 3 | -- Note: The license is defined in the package specification of the logger package 4 | -- 5 | l_rac_lt_11_2 varchar2(50) := 'FALSE'; -- is this a RAC instance less than 11.2, no GAC support 6 | 7 | l_apex varchar2(50) := 'FALSE'; 8 | tbl_not_exist exception; 9 | pls_pkg_not_exist exception; 10 | 11 | l_text_data_length user_tab_columns.data_length%type; 12 | l_large_text_column varchar2(50); 13 | 14 | l_sql varchar2(32767); 15 | l_variables varchar2(1000) := ' '; 16 | l_dummy number; 17 | l_flashback varchar2(50) := 'FALSE'; 18 | l_utl_lms varchar2(5) := 'FALSE'; 19 | 20 | pragma exception_init(tbl_not_exist, -942); 21 | pragma exception_init(pls_pkg_not_exist, -06550); 22 | 23 | l_version constant number := dbms_db_version.version + (dbms_db_version.release / 10); 24 | l_pref_value logger_prefs.pref_Value%type; 25 | l_logger_debug boolean; 26 | 27 | l_pref_type_logger logger_prefs.pref_type%type; 28 | begin 29 | 30 | -- Check to see if we are in a RAC Database, 11.1 or lower. 31 | -- 32 | -- Tyler to check if this works 33 | if dbms_utility.is_cluster_database then 34 | l_rac_lt_11_2 := 'TRUE'; 35 | else 36 | l_rac_lt_11_2 := 'FALSE'; 37 | end if; 38 | 39 | if l_version >= 11.2 then 40 | l_rac_lt_11_2 := 'FALSE'; 41 | end if; 42 | 43 | l_variables := 'RAC_LT_11_2:'||l_rac_lt_11_2||','; 44 | 45 | 46 | -- Check lenth of TEXT size (this is for future 12c 32767 integration 47 | -- In support of Issue #17 and future proofing for #30 48 | select data_length 49 | into l_text_data_length 50 | from user_tab_columns 51 | where 1=1 52 | and table_name = 'LOGGER_LOGS' 53 | and column_name = 'TEXT'; 54 | 55 | if l_text_data_length > 4000 then 56 | l_large_text_column := 'TRUE'; 57 | else 58 | l_large_text_column := 'FALSE'; 59 | end if; 60 | l_variables := l_variables||'LARGE_TEXT_COLUMN:'||l_large_text_column||','; 61 | 62 | 63 | -- Is APEX installed ? 64 | -- 65 | begin 66 | execute immediate 'select 1 from apex_application_items where rownum = 1' into l_dummy; 67 | 68 | l_apex := 'TRUE'; 69 | exception 70 | when tbl_not_exist then 71 | l_apex := 'FALSE'; 72 | when no_data_found then 73 | l_apex := 'TRUE'; 74 | end; 75 | 76 | l_variables := l_variables||'APEX:'||l_apex||','; 77 | 78 | 79 | -- Can we call dbms_flashback to get the currect System Commit Number? 80 | -- 81 | begin 82 | execute immediate 'begin :d := dbms_flashback.get_system_change_number; end; ' using out l_dummy; 83 | 84 | l_flashback := 'TRUE'; 85 | exception when pls_pkg_not_exist then 86 | l_flashback := 'FALSE'; 87 | end; 88 | 89 | l_variables := l_variables||'FLASHBACK_ENABLED:'||l_flashback||','; 90 | 91 | 92 | -- #64: Support to run Logger in debug mode 93 | 94 | -- #127 95 | -- Since this procedure will recompile Logger, if it directly references a variable in Logger 96 | -- It will lock itself while trying to recompile 97 | -- Work around is to pre-store the variable using execute immediate 98 | execute immediate 'begin :x := logger.g_pref_type_logger; end;' using out l_pref_type_logger; 99 | 100 | select lp.pref_value 101 | into l_pref_value 102 | from logger_prefs lp 103 | where 1=1 104 | and lp.pref_type = upper(l_pref_type_logger) 105 | and lp.pref_name = 'LOGGER_DEBUG'; 106 | l_variables := l_variables || 'LOGGER_DEBUG:' || l_pref_value||','; 107 | 108 | l_logger_debug := false; 109 | if upper(l_pref_value) = 'TRUE' then 110 | l_logger_debug := true; 111 | end if; 112 | 113 | 114 | -- #46 115 | -- Handle plugin settings 116 | -- Set for each plugin type 117 | for x in ( 118 | select 119 | 'LOGGER_' || 120 | regexp_replace(lp.pref_name, '^PLUGIN_FN_', 'PLUGIN_') || ':' || 121 | decode(nvl(upper(lp.pref_value), 'NONE'), 'NONE', 'FALSE', 'TRUE') || 122 | ',' var 123 | from logger_prefs lp 124 | where 1=1 125 | and lp.pref_type = l_pref_type_logger 126 | and lp.pref_name like 'PLUGIN_FN%' 127 | ) loop 128 | l_variables := l_variables || x.var; 129 | end loop; 130 | 131 | 132 | l_variables := rtrim(l_variables,','); 133 | if l_logger_debug then 134 | dbms_output.put_line('l_variables: ' || l_variables); 135 | end if; 136 | 137 | 138 | -- Recompile Logger 139 | l_sql := q'!alter package logger compile body PLSQL_CCFLAGS='%VARIABLES%' reuse settings!'; 140 | l_sql := replace(l_sql, '%VARIABLES%', l_variables); 141 | execute immediate l_sql; 142 | 143 | -- #31: Dropped trigger 144 | -- l_sql := q'[alter trigger BI_LOGGER_LOGS compile PLSQL_CCFLAGS=']'||l_variables||q'[' reuse settings]'; 145 | -- execute immediate l_sql; 146 | 147 | -- -- TODO mdsouza: 3.1.1 org l_sql := q'!alter trigger biu_logger_prefs compile PLSQL_CCFLAGS='CURRENTLY_INSTALLING:FALSE'!'; 148 | l_sql := q'!alter trigger biu_logger_prefs compile!'; 149 | execute immediate l_sql; 150 | 151 | -- just in case this is a re-install / upgrade, the global contexts will persist so reset them 152 | logger.null_global_contexts; 153 | 154 | end logger_configure; 155 | / 156 | -------------------------------------------------------------------------------- /source/scripts/create_logger_synonyms.sql: -------------------------------------------------------------------------------- 1 | -- Creates synonyms from defined user for Logger objects 2 | 3 | 4 | -- Parameters 5 | define from_user = '&1' -- This is the user to reference Logger objects 6 | 7 | 8 | whenever sqlerror exit sql.sqlcode 9 | 10 | create or replace synonym logger for &from_user..logger; 11 | create or replace synonym logger_logs for &from_user..logger_logs; 12 | create or replace synonym logger_logs_apex_items for &from_user..logger_logs_apex_items; 13 | create or replace synonym logger_prefs for &from_user..logger_prefs; 14 | create or replace synonym logger_prefs_by_client_id for &from_user..logger_prefs_by_client_id; 15 | create or replace synonym logger_logs_5_min for &from_user..logger_logs_5_min; 16 | create or replace synonym logger_logs_60_min for &from_user..logger_logs_60_min; 17 | create or replace synonym logger_logs_terse for &from_user..logger_logs_terse; 18 | -------------------------------------------------------------------------------- /source/scripts/grant_logger_to_user.sql: -------------------------------------------------------------------------------- 1 | -- Grants privileges for logger objects from current user to a defined user 2 | 3 | 4 | -- Parameters 5 | define to_user = '&1' -- This is the user to grant the permissions to 6 | 7 | 8 | whenever sqlerror exit sql.sqlcode 9 | 10 | grant execute on logger to &to_user; 11 | grant select, delete on logger_logs to &to_user; 12 | grant select on logger_logs_apex_items to &to_user; 13 | grant select, update on logger_prefs to &to_user; 14 | grant select on logger_prefs_by_client_id to &to_user; 15 | grant select on logger_logs_5_min to &to_user; 16 | grant select on logger_logs_60_min to &to_user; 17 | grant select on logger_logs_terse to &to_user; 18 | -------------------------------------------------------------------------------- /source/tables/logger_logs.sql: -------------------------------------------------------------------------------- 1 | -- Initial table script built from 1.4.0 2 | declare 3 | l_count pls_integer; 4 | l_nullable user_tab_columns.nullable%type; 5 | 6 | type typ_required_columns is table of varchar2(30) index by pls_integer; 7 | l_required_columns typ_required_columns; 8 | 9 | 10 | type typ_tab_col is record ( 11 | column_name varchar2(30), 12 | data_type varchar2(100)); 13 | type typ_arr_tab_col is table of typ_tab_col index by pls_integer; 14 | 15 | l_new_col typ_tab_col; 16 | l_new_cols typ_arr_tab_col; 17 | 18 | begin 19 | -- Create Table 20 | select count(1) 21 | into l_count 22 | from user_tables 23 | where table_name = 'LOGGER_LOGS'; 24 | 25 | if l_count = 0 then 26 | execute immediate ' 27 | create table logger_logs( 28 | id number, 29 | logger_level number, 30 | text varchar2(4000), 31 | time_stamp timestamp, 32 | scope varchar2(1000), 33 | module varchar2(100), 34 | action varchar2(100), 35 | user_name varchar2(255), 36 | client_identifier varchar2(255), 37 | call_stack varchar2(4000), 38 | unit_name varchar2(255), 39 | line_no varchar2(100), 40 | scn number, 41 | extra clob, 42 | constraint logger_logs_pk primary key (id) enable, 43 | constraint logger_logs_lvl_ck check(logger_level in (1,2,4,8,16,32,64,128)) 44 | ) 45 | '; 46 | end if; 47 | 48 | -- 2.0.0 49 | l_required_columns(l_required_columns.count+1) := 'LOGGER_LEVEL'; 50 | l_required_columns(l_required_columns.count+1) := 'TIME_STAMP'; 51 | 52 | for i in l_required_columns.first .. l_required_columns.last loop 53 | 54 | select nullable 55 | into l_nullable 56 | from user_tab_columns 57 | where table_name = 'LOGGER_LOGS' 58 | and column_name = upper(l_required_columns(i)); 59 | 60 | if l_nullable = 'Y' then 61 | execute immediate 'alter table logger_logs modify ' || l_required_columns(i) || ' not null'; 62 | end if; 63 | end loop; 64 | 65 | 66 | -- 2.2.0 67 | -- Add additional columns 68 | -- #51 69 | l_new_col.column_name := 'SID'; 70 | l_new_col.data_type := 'NUMBER'; 71 | l_new_cols(l_new_cols.count+1) := l_new_col; 72 | 73 | -- #25 74 | l_new_col.column_name := 'CLIENT_INFO'; 75 | l_new_col.data_type := 'VARCHAR2(64)'; -- taken from v$session.client_info 76 | l_new_cols(l_new_cols.count+1) := l_new_col; 77 | 78 | 79 | for i in 1 .. l_new_cols.count loop 80 | select count(1) 81 | into l_count 82 | from user_tab_columns 83 | where 1=1 84 | and table_name = 'LOGGER_LOGS' 85 | and column_name = l_new_cols(i).column_name; 86 | 87 | if l_count = 0 then 88 | execute immediate 'alter table LOGGER_LOGS add (' || l_new_cols(i).column_name || ' ' || l_new_cols(i).data_type || ')'; 89 | end if; 90 | end loop; 91 | 92 | 93 | $if $$logger_no_op_install $then 94 | null; 95 | $else 96 | -- SEQUENCE 97 | select count(1) 98 | into l_count 99 | from user_sequences 100 | where sequence_name = 'LOGGER_LOGS_SEQ'; 101 | 102 | if l_count = 0 then 103 | execute immediate ' 104 | create sequence logger_logs_seq 105 | minvalue 1 106 | maxvalue 999999999999999999999999999 107 | start with 1 108 | increment by 1 109 | cache 20 110 | '; 111 | end if; 112 | 113 | -- INDEXES 114 | select count(1) 115 | into l_count 116 | from user_indexes 117 | where index_name = 'LOGGER_LOGS_IDX1'; 118 | 119 | if l_count = 0 then 120 | execute immediate 'create index logger_logs_idx1 on logger_logs(time_stamp,logger_level)'; 121 | end if; 122 | $end 123 | 124 | end; 125 | / 126 | 127 | 128 | -- TRIGGER (removed as part of 2.1.0 release) 129 | -- Drop trigger if still exists (from pre-2.1.0 releases) - Issue #31 130 | declare 131 | l_count pls_integer; 132 | l_trigger_name user_triggers.trigger_name%type := 'BI_LOGGER_LOGS'; 133 | begin 134 | select count(1) 135 | into l_count 136 | from user_triggers 137 | where 1=1 138 | and trigger_name = l_trigger_name; 139 | 140 | if l_count > 0 then 141 | execute immediate 'drop trigger ' || l_trigger_name; 142 | end if; 143 | end; 144 | / 145 | -------------------------------------------------------------------------------- /source/tables/logger_logs_apex_items.sql: -------------------------------------------------------------------------------- 1 | -- Initial table script built from 1.4.0 2 | declare 3 | l_count pls_integer; 4 | l_nullable user_tab_columns.nullable%type; 5 | 6 | type typ_required_columns is table of varchar2(30) index by pls_integer; 7 | l_required_columns typ_required_columns; 8 | 9 | begin 10 | 11 | -- Create Table 12 | select count(1) 13 | into l_count 14 | from user_tables 15 | where table_name = 'LOGGER_LOGS_APEX_ITEMS'; 16 | 17 | if l_count = 0 then 18 | execute immediate ' 19 | create table logger_logs_apex_items( 20 | id number not null, 21 | log_id number not null, 22 | app_session number not null, 23 | item_name varchar2(1000) not null, 24 | item_value clob, 25 | constraint logger_logs_apx_itms_pk primary key (id) enable, 26 | constraint logger_logs_apx_itms_fk foreign key (log_id) references logger_logs(id) ON DELETE CASCADE 27 | ) 28 | '; 29 | end if; 30 | 31 | 32 | $if $$logger_no_op_install $then 33 | null; 34 | $else 35 | -- SEQUENCE 36 | select count(1) 37 | into l_count 38 | from user_sequences 39 | where sequence_name = 'LOGGER_APX_ITEMS_SEQ'; 40 | 41 | if l_count = 0 then 42 | execute immediate ' 43 | create sequence logger_apx_items_seq 44 | minvalue 1 45 | maxvalue 999999999999999999999999999 46 | start with 1 47 | increment by 1 48 | cache 20 49 | '; 50 | end if; 51 | 52 | -- INDEXES 53 | select count(1) 54 | into l_count 55 | from user_indexes 56 | where index_name = 'LOGGER_APEX_ITEMS_IDX1'; 57 | 58 | if l_count = 0 then 59 | execute immediate 'create index logger_apex_items_idx1 on logger_logs_apex_items(log_id)'; 60 | end if; 61 | $end -- $$logger_no_op_install 62 | end; 63 | / 64 | 65 | 66 | create or replace trigger biu_logger_apex_items 67 | before insert or update on logger_logs_apex_items 68 | for each row 69 | begin 70 | $if $$logger_no_op_install $then 71 | null; 72 | $else 73 | :new.id := logger_apx_items_seq.nextval; 74 | $end 75 | end; 76 | / 77 | -------------------------------------------------------------------------------- /source/tables/logger_prefs.sql: -------------------------------------------------------------------------------- 1 | -- Initial table script built from 1.4.0 2 | declare 3 | l_count pls_integer; 4 | l_nullable user_tab_columns.nullable%type; 5 | 6 | type typ_required_columns is table of varchar2(30) index by pls_integer; 7 | l_required_columns typ_required_columns; 8 | 9 | begin 10 | -- Create Table 11 | select count(1) 12 | into l_count 13 | from user_tables 14 | where table_name = 'LOGGER_PREFS'; 15 | 16 | if l_count = 0 then 17 | execute immediate ' 18 | create table logger_prefs( 19 | pref_name varchar2(255), 20 | pref_value varchar2(255) not null, 21 | constraint logger_prefs_pk primary key (pref_name) enable 22 | ) 23 | '; 24 | end if; 25 | 26 | end; 27 | / 28 | 29 | 30 | -- #TODO:130 mdsouza: logger 3.1.1 fix. Removed currently_installing 31 | -- Append existing PLSQL_CCFLAGS 32 | -- Since may be set with existing flags (specifically no_op) 33 | -- var cur_plsql_ccflags varchar2(500); 34 | -- 35 | -- declare 36 | -- parnam varchar2(256); 37 | -- intval binary_integer; 38 | -- strval varchar2(500); 39 | -- partyp binary_integer; 40 | -- begin 41 | -- partyp := dbms_utility.get_parameter_value('plsql_ccflags',intval, strval); 42 | -- 43 | -- if strval is not null then 44 | -- strval := ',' || strval; 45 | -- end if; 46 | -- :cur_plsql_ccflags := strval; 47 | -- end; 48 | -- / 49 | -- 50 | -- -- Convert bind variable to substitution string 51 | -- -- https://blogs.oracle.com/opal/entry/sqlplus_101_substitution_varia 52 | -- column cur_plsql_ccflags new_value cur_plsql_ccflags 53 | -- select :cur_plsql_ccflags cur_plsql_ccflags from dual; 54 | -- 55 | -- alter session set plsql_ccflags='currently_installing:true&cur_plsql_ccflags' 56 | -- / 57 | 58 | create or replace trigger biu_logger_prefs 59 | before insert or update on logger_prefs 60 | for each row 61 | begin 62 | $if $$logger_no_op_install $then 63 | null; 64 | $else 65 | :new.pref_name := upper(:new.pref_name); 66 | :new.pref_type := upper(:new.pref_type); 67 | 68 | if 1=1 69 | and :new.pref_type = logger.g_pref_type_logger 70 | and :new.pref_name = 'LEVEL' then 71 | :new.pref_value := upper(:new.pref_value); 72 | end if; 73 | 74 | -- #TODO:50 mdsouza: 3.1.1 75 | -- #TODO:100 mdsouza: if removing then decrease indent 76 | -- $if $$currently_installing is null or not $$currently_installing $then 77 | -- Since logger.pks may not be installed when this trigger is compiled, need to move some code here 78 | if 1=1 79 | and :new.pref_type = logger.g_pref_type_logger 80 | and :new.pref_name = 'LEVEL' 81 | and upper(:new.pref_value) not in (logger.g_off_name, logger.g_permanent_name, logger.g_error_name, logger.g_warning_name, logger.g_information_name, logger.g_debug_name, logger.g_timing_name, logger.g_sys_context_name, logger.g_apex_name) then 82 | raise_application_error(-20000, '"LEVEL" must be one of the following values: ' || 83 | logger.g_off_name || ', ' || logger.g_permanent_name || ', ' || logger.g_error_name || ', ' || 84 | logger.g_warning_name || ', ' || logger.g_information_name || ', ' || logger.g_debug_name || ', ' || 85 | logger.g_timing_name || ', ' || logger.g_sys_context_name || ', ' || logger.g_apex_name); 86 | end if; 87 | 88 | -- Allow for null to be used for Plugins, then default to NONE 89 | if 1=1 90 | and :new.pref_type = logger.g_pref_type_logger 91 | and :new.pref_name like 'PLUGIN_FN%' 92 | and :new.pref_value is null then 93 | :new.pref_value := 'NONE'; 94 | end if; 95 | 96 | -- #103 97 | -- Only predefined preferences and Custom Preferences are allowed 98 | -- Custom Preferences must be prefixed with CUST_ 99 | if 1=1 100 | and :new.pref_type = logger.g_pref_type_logger 101 | and :new.pref_name not in ( 102 | 'GLOBAL_CONTEXT_NAME' 103 | ,'INCLUDE_CALL_STACK' 104 | ,'INSTALL_SCHEMA' 105 | ,'LEVEL' 106 | ,'LOGGER_DEBUG' 107 | ,'LOGGER_VERSION' 108 | ,'PLUGIN_FN_ERROR' 109 | ,'PREF_BY_CLIENT_ID_EXPIRE_HOURS' 110 | ,'PROTECT_ADMIN_PROCS' 111 | ,'PURGE_AFTER_DAYS' 112 | ,'PURGE_MIN_LEVEL' 113 | ) 114 | then 115 | raise_application_error (-20000, 'Setting system level preferences are restricted to a set list.'); 116 | end if; 117 | 118 | -- this is because the logger package is not installed yet. We enable it in logger_configure 119 | logger.null_global_contexts; 120 | -- #TODO:60 mdsouza: 3.1.1 121 | -- $end 122 | $end -- $$logger_no_op_install 123 | end; 124 | / 125 | 126 | alter trigger biu_logger_prefs disable; 127 | 128 | declare 129 | begin 130 | $if $$logger_no_op_install $then 131 | null; 132 | $else 133 | -- Configure Data 134 | merge into logger_prefs p 135 | using ( 136 | select 'PURGE_AFTER_DAYS' pref_name, '7' pref_value from dual union 137 | select 'PURGE_MIN_LEVEL' pref_name, 'DEBUG' pref_value from dual union 138 | select 'LOGGER_VERSION' pref_name, 'x.x.x' pref_value from dual union -- x.x.x will be replaced when running the build script 139 | select 'LEVEL' pref_name, 'DEBUG' pref_value from dual union 140 | select 'PROTECT_ADMIN_PROCS' pref_name, 'TRUE' pref_value from dual union 141 | select 'INCLUDE_CALL_STACK' pref_name, 'TRUE' pref_value from dual union 142 | select 'PREF_BY_CLIENT_ID_EXPIRE_HOURS' pref_name, '12' pref_value from dual union 143 | select 'INSTALL_SCHEMA' pref_name, sys_context('USERENV','CURRENT_SCHEMA') pref_value from dual union 144 | -- #46 145 | select 'PLUGIN_FN_ERROR' pref_name, 'NONE' pref_value from dual union 146 | -- #64 147 | select 'LOGGER_DEBUG' pref_name, 'FALSE' pref_value from dual 148 | ) d 149 | on (p.pref_name = d.pref_name) 150 | when matched then 151 | update set p.pref_value = 152 | case 153 | -- Only LOGGER_VERSION should be updated during an update 154 | when p.pref_name = 'LOGGER_VERSION' then d.pref_value 155 | else p.pref_value 156 | end 157 | when not matched then 158 | insert (p.pref_name,p.pref_value) 159 | values (d.pref_name,d.pref_value); 160 | $end 161 | end; 162 | / 163 | 164 | 165 | 166 | 167 | -- #127: Add pref_type 168 | declare 169 | type typ_tab_col is record ( 170 | column_name varchar2(30), 171 | data_type varchar2(100)); 172 | type typ_arr_tab_col is table of typ_tab_col index by pls_integer; 173 | 174 | l_count pls_integer; 175 | l_new_col typ_tab_col; 176 | l_new_cols typ_arr_tab_col; 177 | begin 178 | 179 | l_new_col.column_name := 'PREF_TYPE'; 180 | l_new_col.data_type := 'VARCHAR2(30)'; 181 | l_new_cols(l_new_cols.count+1) := l_new_col; 182 | 183 | for i in 1 .. l_new_cols.count loop 184 | select count(1) 185 | into l_count 186 | from user_tab_columns 187 | where 1=1 188 | and upper(table_name) = upper('logger_prefs') 189 | and column_name = l_new_cols(i).column_name; 190 | 191 | if l_count = 0 then 192 | execute immediate 'alter table logger_prefs add (' || l_new_cols(i).column_name || ' ' || l_new_cols(i).data_type || ')'; 193 | 194 | -- Custom post-add columns 195 | 196 | -- #127 197 | if lower(l_new_cols(i).column_name) = 'pref_type' then 198 | -- If "LOGGER" is changed then modify logger.pks g_logger_prefs_pref_type value 199 | execute immediate q'!update logger_prefs set pref_type = 'LOGGER'!'; 200 | execute immediate q'!alter table logger_prefs modify pref_type not null!'; 201 | end if; 202 | 203 | end if; -- l_count = 0 204 | end loop; 205 | 206 | end; 207 | / 208 | 209 | 210 | -- #127 If old PK, then drop it 211 | declare 212 | l_count pls_integer; 213 | begin 214 | select count(*) 215 | into l_count 216 | from user_cons_columns 217 | where 1=1 218 | and constraint_name = 'LOGGER_PREFS_PK' 219 | and column_name != 'PREF_NAME'; 220 | 221 | if l_count = 0 then 222 | -- PK only has one column, drop it and it will be rebuilt below 223 | execute immediate 'alter table logger_prefs drop constraint logger_prefs_pk'; 224 | end if; 225 | 226 | end; 227 | / 228 | 229 | 230 | -- Ensure that pref_name is upper 231 | declare 232 | type typ_constraint is record( 233 | name user_constraints.constraint_name%type, 234 | condition varchar(500) 235 | ); 236 | 237 | type typ_tab_constraint is table of typ_constraint index by pls_integer; 238 | 239 | l_constraint typ_constraint; 240 | l_constraints typ_tab_constraint; 241 | l_count pls_integer; 242 | l_sql varchar2(500); 243 | begin 244 | l_constraint.name := 'LOGGER_PREFS_PK'; 245 | l_constraint.condition := 'primary key (pref_type, pref_name)'; 246 | l_constraints(l_constraints.count+1) := l_constraint; 247 | 248 | l_constraint.name := 'LOGGER_PREFS_CK1'; 249 | l_constraint.condition := 'check (pref_name = upper(pref_name))'; 250 | l_constraints(l_constraints.count+1) := l_constraint; 251 | 252 | l_constraint.name := 'LOGGER_PREFS_CK2'; 253 | l_constraint.condition := 'check (pref_type = upper(pref_type))'; 254 | l_constraints(l_constraints.count+1) := l_constraint; 255 | 256 | 257 | -- All pref names/types should be upper 258 | update logger_prefs 259 | set 260 | pref_name = upper(pref_name), 261 | pref_type = upper(pref_type) 262 | where 1=1 263 | or pref_name != upper(pref_name) 264 | or pref_type != upper(pref_type); 265 | 266 | for i in l_constraints.first .. l_constraints.last loop 267 | select count(1) 268 | into l_count 269 | from user_constraints 270 | where 1=1 271 | and table_name = 'LOGGER_PREFS' 272 | and constraint_name = l_constraints(i).name; 273 | 274 | if l_count = 0 then 275 | l_sql := 'alter table logger_prefs add constraint %CONSTRAINT_NAME% %CONSTRAINT_CONDITION%'; 276 | l_sql := replace(l_sql, '%CONSTRAINT_NAME%', l_constraints(i).name); 277 | l_sql := replace(l_sql, '%CONSTRAINT_CONDITION%', l_constraints(i).condition); 278 | 279 | execute immediate l_sql; 280 | end if; 281 | end loop; -- l_constraints 282 | 283 | end; 284 | / 285 | 286 | alter trigger biu_logger_prefs enable; 287 | -------------------------------------------------------------------------------- /source/tables/logger_prefs_by_client_id.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_count pls_integer; 3 | l_nullable user_tab_columns.nullable%type; 4 | 5 | type typ_required_columns is table of varchar2(30) index by pls_integer; 6 | l_required_columns typ_required_columns; 7 | 8 | l_sql varchar2(2000); 9 | 10 | begin 11 | -- Create Table 12 | select count(1) 13 | into l_count 14 | from user_tables 15 | where table_name = 'LOGGER_PREFS_BY_CLIENT_ID'; 16 | 17 | if l_count = 0 then 18 | execute immediate q'! 19 | create table logger_prefs_by_client_id( 20 | client_id varchar2(64) not null, 21 | logger_level varchar2(20) not null, 22 | include_call_stack varchar2(5) not null, 23 | created_date date default sysdate not null, 24 | expiry_date date not null, 25 | constraint logger_prefs_by_client_id_pk primary key (client_id) enable, 26 | constraint logger_prefs_by_client_id_ck1 check (logger_level in ('OFF','PERMANENT','ERROR','WARNING','INFORMATION','DEBUG','TIMING')), 27 | constraint logger_prefs_by_client_id_ck2 check (expiry_date >= created_date), 28 | constraint logger_prefs_by_client_id_ck3 check (include_call_stack in ('TRUE', 'FALSE')) 29 | ) 30 | !'; 31 | end if; 32 | 33 | -- COMMENTS 34 | execute immediate q'!comment on table logger_prefs_by_client_id is 'Client specific logger levels. Only active client_ids/logger_levels will be maintained in this table'!'; 35 | execute immediate q'!comment on column logger_prefs_by_client_id.client_id is 'Client identifier'!'; 36 | execute immediate q'!comment on column logger_prefs_by_client_id.logger_level is 'Logger level. Must be OFF, PERMANENT, ERROR, WARNING, INFORMATION, DEBUG, TIMING'!'; 37 | execute immediate q'!comment on column logger_prefs_by_client_id.include_call_stack is 'Include call stack in logging'!'; 38 | execute immediate q'!comment on column logger_prefs_by_client_id.created_date is 'Date that entry was created on'!'; 39 | execute immediate q'!comment on column logger_prefs_by_client_id.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'!'; 40 | 41 | 42 | -- 92: Missing APEX and SYS_CONTEXT support 43 | l_sql := 'alter table logger_prefs_by_client_id drop constraint logger_prefs_by_client_id_ck1'; 44 | execute immediate l_sql; 45 | 46 | -- Rebuild constraint 47 | l_sql := q'!alter table logger_prefs_by_client_id 48 | add constraint logger_prefs_by_client_id_ck1 49 | check (logger_level in ('OFF','PERMANENT','ERROR','WARNING','INFORMATION','DEBUG','TIMING', 'APEX', 'SYS_CONTEXT'))!'; 50 | execute immediate l_sql; 51 | 52 | end; 53 | / 54 | -------------------------------------------------------------------------------- /source/views/logger_logs_5_min.sql: -------------------------------------------------------------------------------- 1 | create or replace force view logger_logs_5_min as 2 | select * 3 | from logger_logs 4 | where time_stamp > systimestamp - (5/1440) 5 | / -------------------------------------------------------------------------------- /source/views/logger_logs_60_min.sql: -------------------------------------------------------------------------------- 1 | create or replace force view logger_logs_60_min as 2 | select * 3 | from logger_logs 4 | where time_stamp > systimestamp - (1/24) 5 | / 6 | -------------------------------------------------------------------------------- /source/views/logger_logs_terse.sql: -------------------------------------------------------------------------------- 1 | set termout off 2 | -- setting termout off as this view will install with an error as it depends on logger.date_text_format 3 | create or replace force view logger_logs_terse as 4 | select id, logger_level, 5 | substr(logger.date_text_format(time_stamp),1,20) time_ago, 6 | substr(text,1,200) text 7 | from logger_logs 8 | where time_stamp > systimestamp - (5/1440) 9 | order by id asc 10 | / 11 | 12 | set termout on 13 | -------------------------------------------------------------------------------- /tests/logger_log_load_test.sql: -------------------------------------------------------------------------------- 1 | set serveroutput on 2 | set timing on 3 | set verify off 4 | 5 | define param_text_size = '&1' 6 | define param_iterations = '&2' 7 | 8 | declare 9 | l_user varchar2(255); 10 | l_text varchar2(32767); 11 | 12 | l_text_size pls_integer := ¶m_text_size; 13 | l_iterations pls_integer := ¶m_iterations; 14 | l_extra clob; 15 | 16 | begin 17 | for i in 1..l_text_size loop 18 | l_text := l_text || 'a'; 19 | end loop; 20 | 21 | -- Add text to Extra to test appending 22 | if length(l_text) > 4000 then 23 | l_extra := l_text; 24 | end if; 25 | 26 | for i in 1..l_iterations loop 27 | logger.log(l_text, null, l_extra); 28 | end loop; 29 | end; 30 | / 31 | -------------------------------------------------------------------------------- /tests/logger_params.sql: -------------------------------------------------------------------------------- 1 | declare 2 | l_params logger.tab_param; 3 | l_param logger.rec_param; 4 | l_param2 logger.rec_param; 5 | begin 6 | l_param.name := 'testname'; 7 | l_param.val := 'val'; 8 | l_params(1) := l_param; 9 | 10 | -- Testing non-sequential parameters 11 | l_param2.name := 'test2'; 12 | l_param2.val := 'val'; 13 | l_params(5) := l_param2; 14 | 15 | logger.log('test', 'test',null, l_params); 16 | end; 17 | / --------------------------------------------------------------------------------