if( pkg_const.debug ) then
if( utl_file.is_open( fi )) then
utl_file.put_line( fi, ' error:' );
utl_file.put_line( fi, ' sqlcode = ' || sqlcode );
utl_file.put_line( fi, ' sqlerrm = ' || sqlerrm );
utl_file.put_line( fi, ' ****** usp_getempbydept end at ' || to_char( sysdate,
'hh24:mi:ss mm-dd-yyyy' ) || ' ******' );
utl_file.new_line( fi, 1 );
utl_file.fflush( fi );
utl_file.fclose( fi );
end if;
end if;
/* raise the exception for caller. */
raise_application_error( -20001, sqlcode || '|' || sqlerrm );
end usp_getempbydept;
在上面的代码中,我们又引用了两个新的常量:
debug
log_path
分别定义了调试开关参数和文件路径参数,对此,我们需要修改我们前面定义的程序包:
create or replace package pkg_const as
type ref_cursor is ref cursor;
debug constant boolean := true;
log_path constant varchar2(256) := '/usr/tmp/db';
end pkg_const;
在代码块的起始处,将输入参数的名称与值成对的记入log文件,在代码块的正常退出部分,将输出参数的名称和数值也成对的记录下来,如果程序非正常退出,则在exception
的处理部分,把错误代码及错误信息写入log文件。一般使用这些信息就可以较迅速的找出程序运行中出现的大部分错误。
注意:如果返回参数的类型是cursor,是无法在存储过程内部将返回的结果集一条一条写入log文件的,此时应当结合在调用程序中记录的log信息,下面具体分析一下上述代码:
fopen()
函数使用给定的路径和文件名,新建文件或者打开已有的文件,这取决于最后一个参数,
当使用'a'作为参数时,如果给定的文件不存在,则以此文件名新建文件,并以写'w'方式打开,返回一个文件句柄。
上面代码以天为单位建立日志文件,并且,不同存储过程之间共享log文件,这种方式的优点是可能通过查看log文件追溯出程序的调用顺序和逻辑。实际应用中,应根据不同的需求,具体分析,可以使用更复杂的log文件生成策略。
put_line()
函数用于写入字符到文件,并在字符串的结尾加入换行符,若不想换行,使用put()函数。
new_line() 函数用于生成指定数目的空行,上面对文件的修改写在一个缓冲区内,执行fflush() 将立即将buffer中的内容写入文件,当你希望在文件还未关闭之前就需要读取已经作出的改变时,调用此函数。
is_open() 函数用于判断一个文件句柄的状态,最后用完一定记得把打开的文件关闭,调用fclose() 函数,并且应把这个语句加入exception的处理中,防止过程非正常退出时留下未关闭的文件句柄。
捕获违例
在plsql中,你可以通过两个内建的函数sqlcode 和sqlerrm 来找出发生了哪类错误并且获得详细的message信息,在内部违例发生时,sqlcode返回从-1至-20000之间的一个错误号,但有一个例外,仅当内部违例no_data_found 发生时,才会返回一个正数 100。当用户自定义的违例发生时,sqlcode返回+1,除非用户使用 pragma exception_init 将自定义违例绑定一个自定义的错误号。当没有任何违例抛出时,sqlcode返回0。
下面是一个简单的捕获违例的例子:
declare
i number(3);
begin
select 100/0 into i from dual;
exception
when zero_divide then
...
end;
在上面的exception 中我们使用others
关键字捕获所有未明确指定的违例,并进行记录log处理,同时我们必须在做完这些处理之后,把违例再次抛出给调用程序,调用函数:
raise_application_error(),此函数向调用程序返回一个用户自定义的错误号码和错误信息,第一个参数指定一个错误号码,由用户自行定义,但必须限定在-20000至-20999之间,避免与oracle内部定义exception的错误号码冲突,第二个参数需要返回一个字符串,这里我们使用它返回我们上面捕获的错误号码和错误描述。
注意:通过raise_application_error()函数抛出的违例已经不是开始在程序块内部捕获的内部违例,而是由用户自己定义的。