├── .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 |
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 | Level |
64 | Actionable |
65 | Target Audience |
66 | ~%Reference |
67 |
68 |
69 | Debug |
70 | No |
71 | Developers |
72 | 90% |
73 |
74 |
75 | Information |
76 | No |
77 | Developers/Business |
78 | 1% |
79 |
80 |
81 | Warning |
82 | Yes |
83 | Developers/Business |
84 | 1% |
85 |
86 |
87 | Error |
88 | Yes |
89 | Developers/IT/DBA |
90 | 5% |
91 |
92 |
93 | Permanent |
94 | No |
95 | Developers/Business |
96 | 0.5% |
97 |
98 |
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 | Issue# |
12 | Feature |
13 | Articles |
14 |
15 |
16 | 136 |
17 | ORA-1031 on install |
18 | |
19 |
20 |
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 |
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 | Issue# |
104 | Feature |
105 | Articles |
106 |
107 |
108 | 25 |
109 | Add CLIENT_INFO to logger_logs |
110 | New fields CLIENT_INFO and SID |
111 |
112 |
113 | 32 |
114 | printf type support for substitution strings (printf) |
115 | printf support |
116 |
117 |
118 | 42 |
119 | ok_to_log takes in both name or number of logger level |
120 | |
121 |
122 |
123 | 43 |
124 | Reference new global constants for names |
125 | |
126 |
127 |
128 | 44 |
129 | Global Constants for Logger Level Names |
130 | Global Level Names |
131 |
132 |
133 | 47 |
134 | Support for both logger level name and number |
135 | |
136 |
137 |
138 | 46 |
139 | Plugins for Logger |
140 | Plugins |
141 |
142 |
143 | 51 |
144 | Add SID to logger_logs |
145 | New fields CLIENT_INFO and SID |
146 |
147 |
148 | 56 |
149 | Updated documentation format to use Flatdoc |
150 | Flatdoc Documentation |
151 |
152 |
153 | 57 |
154 | Modify build script so that no_op code is auto generated as part of build |
155 | |
156 |
157 |
158 | 59 |
159 | Allow security check to be bypassed for client specific logging level |
160 | |
161 |
162 |
163 | 68 |
164 | Added TOCHAR function |
165 | Explicit Conversions Using logger.tochar |
166 |
167 |
168 | 80 |
169 | Added log_warn and log_info shortcuts |
170 | log_warn and log_info shortcuts |
171 |
172 |
173 | 88 |
174 | Developer Guide |
175 | Developer's Guide |
176 |
177 |
178 | 89 |
179 | Include docs with each release |
180 | |
181 |
182 |
183 | 90 |
184 | Change License |
185 | New License |
186 |
187 |
188 |
189 |
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 | Issue# |
199 | Feature |
200 | Articles |
201 |
202 |
203 | 39 |
204 | Bug setting client level multiple times. |
205 | |
206 |
207 |
208 | 41 |
209 | Bug viewing logger.status when client_id is defined. |
210 | |
211 |
212 |
213 |
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 | Issue# |
223 | Feature |
224 | Articles |
225 |
226 |
227 | 37 |
228 | Bug compiling logger_configure fixed. |
229 | |
230 |
231 |
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 | Issue# |
246 | Feature |
247 | Articles |
248 |
249 |
250 | 17 |
251 | Allow 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) |
252 | Text Length > 4000 Characters |
253 |
254 |
255 | 20 |
256 | Restructured 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 | |
262 | Documentation Overhaul |
263 |
264 |
265 | 24 |
266 | Open ok_to_log as public. |
267 | ok_to_log |
268 |
269 |
270 | 26 |
271 | Fixed error compiling in 10g (or any 11gR1 and below). |
272 | 10g Sequence Fixed |
273 |
274 |
275 | 27 |
276 | Generate demo scripts and add them to build. More demo scripts to come during future releases. Avoids the need to regenerate for presentations and learning. |
277 | Demo Scripts |
278 |
279 |
280 | 28 |
281 | Include 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. |
282 | |
283 |
284 |
285 | 31 |
286 | Remove 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. |
287 | ins_logger_logs |
288 |
289 |
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 | Issue# |
303 | Feature |
304 | Articles |
305 |
306 |
307 | New |
308 | Moved to GitHub and restructured / updated documentation |
309 | |
310 |
311 |
312 | New |
313 | Added p_params support and append_params functions. |
314 | Logging Parameters |
315 |
316 |
317 | 4 |
318 | Client specific level setting (to enable logging based on client_id) |
319 | Enable Session Specific Logging Session Specific Logging - Advanced Features |
320 |
321 |
322 | 8 |
323 | New build script which will allow for future versions of logger to be updated. This was built off a 1.4.0 release. |
324 | |
325 |
326 |
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 | Issue# |
335 | Feature |
336 | Articles |
337 |
338 |
339 | Fix |
340 | Fixed an issue detecting 11.2 RAC installations |
341 | |
342 |
343 |
344 | New |
345 | APEX 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. |
346 | |
347 |
348 |
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 | Issue# |
358 | Feature |
359 | Articles |
360 |
361 |
362 | Fix |
363 | Fixed major flaw in time calculation used in time_start/time_stop |
364 | |
365 |
366 |
367 | New |
368 | Changed 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. |
369 | |
370 |
371 |
372 |
373 |
374 | ## Change Log 1.2.2
375 |
376 |
377 |
378 | Issue# |
379 | Feature |
380 | Articles |
381 |
382 |
383 | Fix |
384 | Fixed an error with the admin security check reported by John Flack |
385 | |
386 |
387 |
388 | New |
389 | It 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. |
390 | |
391 |
392 |
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 | Issue# |
401 | Feature |
402 | Articles |
403 |
404 |
405 | New |
406 | PROTECT_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. |
407 | |
408 |
409 |
410 | New |
411 | Preference 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. |
412 | |
413 |
414 |
415 | New |
416 | CLOB 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. |
417 | |
418 |
419 |
420 | New |
421 | logger.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. |
422 | |
423 |
424 |
425 | New |
426 | logger.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. |
427 | |
429 |
430 | New |
431 | logger.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. |
432 | |
433 |
434 |
435 | Fix |
436 | set_level, purge and purge_all so they are now autonomous transactions (thanks Tony). |
437 | |
438 |
439 |
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 |
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 | Preference |
179 | Description |
180 |
181 |
182 | GLOBAL_CONTEXT_NAME |
183 | Context that Logger uses to save values. It is not recommended to modify this setting. |
184 |
185 |
186 | INCLUDE_CALL_STACK |
187 | Store the call stack. Note client specific settings can override this. |
188 |
189 |
190 | INSTALL_SCHEMA |
191 | Schema that Logger is installed in. Do not modify. |
192 |
193 |
194 | LEVEL |
195 | The current schema Logger level. |
196 |
197 |
198 | LOGGER_VERSION |
199 | Current version of Logger. Do no modify this as it may affect future migrations. |
200 |
201 |
202 | PREF_BY_CLIENT_ID_EXPIRE_HOURS |
203 | Default time (in hours) that client specific logging levels are set for. |
204 |
205 |
206 | PROTECT_ADMIN_PROCS |
207 | If TRUE then only user, defined in INSTALL_SCHEMA, can run privilidged procedures. |
208 |
209 |
210 | PURGE_AFTER_DAYS |
211 | Purge logs, equal to or higher than PURGE_MIN_LEVEL, after this many days. A purge job is run each night to clean up logger. |
212 |
213 |
214 | PURGE_MIN_LEVEL |
215 | Min level to purge logs used in auto Logger cleanup job. |
216 |
217 |
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 | Attribute |
20 | Description |
21 |
22 |
23 | TODO |
24 | TODO |
25 |
26 |
27 | TODO |
28 | TODO |
29 |
30 |
31 | TODO |
32 | TODO |
33 |
34 |
35 | TODO |
36 | TODO |
37 |
38 |
39 | TODO |
40 | TODO |
41 |
42 |
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 | Name |
10 | Description |
11 |
12 |
13 | g_logger_version |
14 | Version of Logger. |
15 |
16 |
17 | g_context_name |
18 | Context Logger uses for storing attributes. |
19 |
20 |
21 | gc_empty_tab_param |
22 | Empty param used for default value in logger main procedures. |
23 |
24 |
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 | Name |
36 | Description |
37 |
38 |
39 | g_off |
40 | Logger level off (0). |
41 |
42 |
43 | g_permanent |
44 | Logger level permanent (1). |
45 |
46 |
47 | g_error |
48 | Logger level error (2). |
49 |
50 |
51 | g_warning |
52 | Logger level warning (4). |
53 |
54 |
55 | g_information |
56 | Logger level information (8). |
57 |
58 |
59 | g_debug |
60 | Logger level debug (16). |
61 |
62 |
63 | g_timing |
64 | *Deprecated* Logger level timing (32). |
65 |
66 |
67 | g_sys_context |
68 | *Deprecated* Logger level sys context (64). This is applicable for logging system variables. |
69 |
70 |
71 | g_apex |
72 | *Deprecated* Logger level apex (128). |
73 |
74 |
75 |
76 | ### Name
77 | This will still work, however it is recommended that you use the numeric values.
78 |
79 |
80 | g_off_name |
81 | Logger level name: OFF |
82 |
83 |
84 | g_permanent_name |
85 | Logger level name: PERMANENT |
86 |
87 |
88 | g_error_name |
89 | Logger level name: ERROR |
90 |
91 |
92 | g_warning_name |
93 | Logger level name: WARNING |
94 |
95 |
96 | g_information_name |
97 | Logger level name: INFORMATION |
98 |
99 |
100 | g_debug_name |
101 | Logger level name: DEBUG |
102 |
103 |
104 | g_timing_name |
105 | *Deprecated* Logger level name: TIMING |
106 |
107 |
108 | g_sys_context_name |
109 | *Deprecated* Logger level name: SYS_CONTEXT |
110 |
111 |
112 | g_apex_name |
113 | *Deprecated* Logger level name: APEX |
114 |
115 |
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 | Name |
123 | Description |
124 |
125 |
126 | g_apex_item_type_all |
127 | Log both application and page level items |
128 |
129 |
130 | g_apex_item_type_app |
131 | Only application level items |
132 |
133 |
134 | g_apex_item_type_page |
135 | Only page level items |
136 |
137 |
138 | <page_number> |
139 | All page items corresponding to the given page will be logged |
140 |
141 |
142 |
143 |
144 | # Types
145 |
146 |
147 | Name |
148 | Description |
149 |
150 |
151 | rec_param |
152 |
153 | Consists of:
154 | name (varchar2)
155 | value (varchar2)
156 | |
157 |
158 |
159 | tab_param |
160 | Array of rec_param |
161 |
162 |
163 | rec_logger_log |
164 | Consists of:
165 | id (number)
166 | logger_level (number)
167 |
168 | See Plugins documentation for more information and examples. |
169 |
170 |
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 | Parameter |
202 | Description |
203 |
204 |
205 | p_text |
206 | p_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 | |
208 |
209 |
210 | p_scope |
211 | p_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. |
212 |
213 |
214 | p_extra |
215 | When 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. |
216 |
217 |
218 | p_params |
219 | p_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. |
222 |
223 |
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 | Parameter |
322 | Description |
323 |
324 |
325 | p_detail_level |
326 | Valid values are: ALL, NLS, USER (default), or INSTANCE |
327 |
328 |
329 | p_show_null |
330 | If true, then variables that have no value will still be displayed. |
331 |
332 |
333 | p_scope |
334 | Scope to log variables under. |
335 |
336 |
337 | p_level |
338 | Highest 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. |
339 |
340 |
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 | Parameter |
394 | Description |
395 |
396 |
397 | p_show_null |
398 | If true, then variables that have no value will still be displayed. |
399 |
400 |
401 | p_scope |
402 | Scope to log CGI variables under. |
403 |
404 |
405 | p_level |
406 | Highest 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. |
407 |
408 |
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 | Parameter |
447 | Description |
448 |
449 |
450 | p_text |
451 | Text to retrieve character codes for. |
452 |
453 |
454 | p_scope |
455 | Scope to log text under. |
456 |
457 |
458 | p_show_common_codes |
459 | Provides legend of common character codes in output. |
460 |
461 |
462 | p_level |
463 | Highest 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. |
464 |
465 |
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 | Parameter |
498 | Description |
499 |
500 |
501 | p_text |
502 | Text to be added to TEXT column. |
503 |
504 |
505 | p_scope |
506 | Scope to log text under. |
507 |
508 |
509 | p_item_type |
510 | Determines 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. |
511 |
512 |
513 | p_log_null_items |
514 | If set to false, null values won't be logged. |
515 |
516 |
517 | p_level |
518 | Highest 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. |
519 |
520 |
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 | Prameter |
596 | Description |
597 |
598 |
599 | p_val |
600 | Value (in original data type) |
601 |
602 |
603 | return |
604 | Varchar2 value of p_val |
605 |
606 |
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 | Prameter |
660 | Description |
661 |
662 |
663 | p_str |
664 | String to apply substitution strings to |
665 |
666 |
667 | p_s<1..10> |
668 | Substitution strings |
669 |
670 |
671 | return |
672 | Formated string |
673 |
674 |
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 | Prameter |
719 | Description |
720 |
721 |
722 | p_show_null |
723 | Show null variables. |
724 |
725 |
726 | return |
727 | Formatted list of CGI variables |
728 |
729 |
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 | Prameter |
753 | Description |
754 |
755 |
756 | p_pref_name |
757 | Preference to get value for. |
758 |
759 |
760 | p_pref_type |
761 | Preference type |
762 |
763 |
764 | return |
765 | Prefence value. |
766 |
767 |
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 | Prameter |
795 | Description |
796 |
797 |
798 | p_pref_type |
799 | Type of preference. Use your own name space to avoid conflicts with Logger. Types will automatically be converted to uppercase |
800 |
801 |
802 | p_pref_name |
803 | Preference to get value for. Must be prefixed with "CUST_". Value will be created or updated. This value will be stored as uppercase. |
804 |
805 |
806 | p_pref_value |
807 | Prefence value. |
808 |
809 |
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 | Prameter |
835 | Description |
836 |
837 |
838 | p_pref_type |
839 | Namepsace / type of preference. |
840 |
841 |
842 | p_pref_name |
843 | Custom preference to delete. |
844 |
845 |
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 | Prameter |
869 | Description |
870 |
871 |
872 | p_purge_after_days |
873 | Purge entries older than n days. |
874 |
875 |
876 | p_purge_min_level |
877 | Minimum level to purge entries. For example if set to *logger.g\_information* then information, debug, timing, sys_context, and apex logs will be deleted. |
878 |
879 |
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 | Prameter |
921 | Description |
922 |
923 |
924 | p_output_format |
925 | What type of output. Accepted values are SQL-DEVELOPER, HTML, and DBMS_OUPUT. |
926 |
927 |
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 | Prameter |
1000 | Description |
1001 |
1002 |
1003 | p_level |
1004 | Level name. |
1005 |
1006 |
1007 | return |
1008 | Level number |
1009 |
1010 |
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 | Prameter |
1036 | Description |
1037 |
1038 |
1039 | p_date |
1040 | Date to compare |
1041 |
1042 |
1043 | return |
1044 | Time difference between p_date and now. |
1045 |
1046 |
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 | Prameter |
1074 | Description |
1075 |
1076 |
1077 | p_string |
1078 | String to get codes for. |
1079 |
1080 |
1081 | p_show_common_codes |
1082 | Display legend of common character codes. |
1083 |
1084 |
1085 | return |
1086 | String with character codes. |
1087 |
1088 |
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 | Prameter |
1119 | Description |
1120 |
1121 |
1122 | p_params |
1123 | Param array to append parameter value to. |
1124 |
1125 |
1126 | p_name |
1127 | Name of the parameter. |
1128 |
1129 |
1130 | p_val |
1131 | Value (in original data type). |
1132 |
1133 |
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 | Prameter |
1175 | Description |
1176 |
1177 |
1178 | p_level |
1179 | Level (name) to test for. |
1180 |
1181 |
1182 | return |
1183 | Wether or not level will be logged. |
1184 |
1185 |
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 | Prameter |
1250 | Description |
1251 |
1252 |
1253 | p_logger_level |
1254 | Logger level. See Constants section for list of variables to chose from. |
1255 |
1256 |
1257 | p_text |
1258 | Text column. |
1259 |
1260 |
1261 | p_scope |
1262 | Scope. |
1263 |
1264 |
1265 | p_call_stack |
1266 | PL/SQL call stack. |
1267 |
1268 |
1269 | p_unit_name |
1270 | Unit name (this is usually the calling procedure). |
1271 |
1272 |
1273 | p_line_no |
1274 | Line number |
1275 |
1276 |
1277 | p_extra |
1278 | Extra CLOB. |
1279 |
1280 |
1281 | po_id |
1282 | Logger ID (out). |
1283 |
1284 |
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 | Prameter |
1351 | Description |
1352 |
1353 |
1354 | p_level |
1355 | Use logger.g_<level>_name variables. See [Constants](#constants-logger-levels). If the level is deprecated it will automatically be set to DEBUG. |
1356 |
1357 |
1358 | p_client_id |
1359 | Optional: If defined, will set the level for the given client identifier. If null will affect global settings. |
1360 |
1361 |
1362 | p_include_call_stack |
1363 | Optional: Only valid if p_client_id is defined Valid values: TRUE, FALSE. If not set will use the default system pref in logger_prefs. |
1364 |
1365 |
1366 | p_client_id_expire_hours |
1367 | If p_client_id, expire after number of hours. If not defined, will default to system preference PREF_BY_CLIENT_ID_EXPIRE_HOURS. |
1368 |
1369 |
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 | Prameter |
1438 | Description |
1439 |
1440 |
1441 | p_client_id |
1442 | Client identifier to unset logging level. |
1443 |
1444 |
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 | Prameter |
1556 | Description |
1557 |
1558 |
1559 | p_unit |
1560 | Name for timing unit |
1561 |
1562 |
1563 | p_log_in_table |
1564 | If true, will log the start event in LOGGER_LOGS. |
1565 |
1566 |
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 | Prameter |
1586 | Description |
1587 |
1588 |
1589 | p_unit |
1590 | Timer to stop. |
1591 |
1592 |
1593 | p_scope |
1594 | Scope to log timer under. |
1595 |
1596 |
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 | Prameter |
1618 | Description |
1619 |
1620 |
1621 | p_unit |
1622 | Timer to stop. |
1623 |
1624 |
1625 | p_scope |
1626 | Scope to log timer under. |
1627 |
1628 |
1629 | p_log_in_table |
1630 | Store result in LOGGER_LOGS. |
1631 |
1632 |
1633 | return |
1634 | Timer results. |
1635 |
1636 |
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 | Prameter |
1660 | Description |
1661 |
1662 |
1663 | p_unit |
1664 | Timer to stop. |
1665 |
1666 |
1667 | p_scope |
1668 | Scope to log timer under. |
1669 |
1670 |
1671 | p_log_in_table |
1672 | Store result in LOGGER_LOGS. |
1673 |
1674 |
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 | Name |
17 | Associated Procedure |
18 | Description |
19 |
20 |
21 | PLUGIN_FN_ERROR |
22 | logger.log_error |
23 | Allows 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 |
24 |
25 |
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 | /
--------------------------------------------------------------------------------