动机
我们可能会因为以下几个原因需要将信息、错误和调试信息写入日志文件中:
测试、分析并验证业务逻辑。
跟踪并查看详细的错误消息。
通过动态打开详细日志或关闭日志来解决在产品环境中碰到的业务逻辑 bug。
通过检查日志语句的执行时间来查明存储过程的性能问题。
我们可以对存储过程使用这个日志框架来满足以下需求:
在产品环境中动态打开或关闭全局或可选择的日志功能。
对日志文件进行归档,将当前日志文件维护到一个可管理的大小。
为每个存储过程和每个 DB2 代理确定日志语句。
为每个 DB2 代理分隔或合并日志文件。
确保日志文件本身不是资源敏感的。
整体架构和设计
DB2 有自己的日志方法,可以将消息存放到 db2diag.log 文件中。DB2 还提供了一些工具来捕获并存放详细的跟踪信息,这样 DB2 开发人员就可以对所报告的问题进行隔离和解决。本文中给出的日志框架提供了将信息和调试消息捕获到文件中进行分析的能力,这样存储过程的开发人员就可以对这些问题进行隔离和解决。
由于 DB2 存储过程可以由不同的 DB2 代理并行执行,因此记录日志的存储过程的每个实例都必须读取所有的日志控制信息,并与其他过程一起共享这些信息。为了实现这个目的,我们使用了共享内存,每个日志过程实例都可以访问这些共享内存中的信息。
如何配置日志功能
要为某个存储过程配置日志功能,就需要使用存储过程 DB2.UPDATE_SP_CONFIG。我们可以使用这个过程来启动、停止和控制记录日志的功能,并设置共享内存中的参数,这样,所有正在执行的存储过程都可以使用相同的日志来控制信息。
为了启用或禁用用于每个存储过程的日志,或同时启用或禁用用于所有存储过程的日志,可以将这些信息保存到一个配置文件中,并在首次调用日志存储过程时将其加载到共享内存中。在更改日志配置参数时,共享内存中的信息就会获得更新,同时还会保存到配置文件中。(注意:这非常类似于 DB2 配置参数的更改,其中对某些参数的更改可以立即生效,有些则会等到实例或数据库重新启动之后才生效。在这个日志框架中,对存储过程的日志参数进行的任何更改都会立即生效)。
这个日志框架的一个目标是可以保守地使用系统资源,这样日志过程调用所耗费的资源都不会太多,或者不会产生很大负载。这个框架的设计目标是可以被上千个存储过程使用,而不会对性能产生太大影响。
对于每个日志调用,都需要确定这条消息是否将发送到日志文件中。我们需要根据日志标记名使用的散列值对具有链表 (linked list) 的散列表进行查询。对于那些共享相同散列值的名称来说,我们在相同的关键字级别上维护了一个链表,以方便查找相同的日志标记名的配置参数。使用这种具有链表的散列表可以提供最快速的查询功能。
如果对所有的存储过程都启用完整日志,那么日志文件本身可能会增长的非常快。通过对日志文件进行归档,可以将日志文件移动到另外一个位置进行分析、存储或删除。
如何启动日志
要在 DB2 的存储过程中启用日志功能,首先要使用两个参数调用 DB2.OPEN_LOG。第一个参数是日志标记名,第二个是一个句柄。为了尽量简单,可以使用存储过程名作为日志标记名,也可以对一组实现特定业务功能的存储过程使用一个通用的日志标记。例如,如果有一个嵌套过程,而该过程又调用另外 6 个 DB2 存储过程来获取最佳实现,那么可以对这 7 个 DB2 存储过程使用标记名 GBQR。DB2.OPEN_LOG 中的第二个参数是一个 CHAR 类型的句柄(48 个字节)。该句柄由两个 C 结构组成。其中一个包含标记名的共享内存的位置及其日志操作,第二个结构包含调用 DB2.OPEN_LOG 时的时间戳。
如何记录消息
日志框架为记录消息提供了另外两个存储过程:DB2.LOGINFO 和 DB2.LOGGER。每个存储过程都需要两个参数。第一个参数是从 DB2.OPEN_LOG 中获得的日志句柄,第二个是要记录的消息。如果共享内存中存在这个标记,则 DB2.LOGINFO 会记录一条信息消息。如果调试参数被设置为 Y,并且在共享内存中存在一个日志标记,那么 DB2.LOGGER 就会记录一条调试信息。
通常,我们会使用 LOGINFO 来记录详细错误和其他信息消息,并会使用 LOGGER 将调试信息截获到日志文件中。当不想将调试消息记录到日志文件中时,将这个日志标记的调试参数设置成 N 即可。要设置这个参数,可以使用存储过程 DB2.UPDATE_SP_CONFIG,该过程用来实现日志管理的工作。如果希望彻底关闭 LOGINFO 消息,那么通过将调试参数设置成 R 来永久删除这个标记,就可以将这个日志标记从共享内存中删除。
如何关闭日志
在这个存储过程的末尾,可以通过调用 DB2.CLOSE_LOG 来关闭日志。CLOSE_LOG 过程需要使用日志句柄作为输入参数。CLOSE_LOG 会在最后向日志中写入最后一条消息,内容是从启动日志到结束日志经过了多少时间。CLOSE_LOG 调用对于确定使用的执行时间非常有用。
日志框架的引用指南
日志标记的解释
第一个 DB2.OPEN_LOG 调用的功能是从共享内存中保存的日志标记来构建一个如 图 1 所示的链表散列表。对于每个从共享内存或初次调用使用的配置文件中读取的日志标记,OPEN_LOG 都会使用标记名和散列表的大小来生成一个散列值。对于大小为 10 的散列表来说,get_quote、find_zip_code、verify_zip 和 put_culls 标记会共享同一个散列值 3。这个链表散列表包含有关共享内存日志标记指针的信息。共享内存的位置存储在句柄中,从 OPEN_LOG 到后续的 LOGGER 和 LOGINFO 调用都会传递这个句柄。
图 1. 链表散列表
图片看不清楚?请点击这里查看原图(大图)。
这些日志方法都可以在 SQL-PL 存储过程或其他外部存储过程(COBOL、Java™ 等)中使用。如果正在使用用 C 语言编写的外部存储过程,那么既可以使用日志存储过程,也可以直接使用日志调用,后者可以跳过存储过程。
日志存储过程框架包含两个部分。第一个部分是 日志管理,用来获取或设置配置参数。第二个部分是 日志存储过程,可以在 SQL-PL 或任何其他外部存储过程中使用。
日志管理
用来进行日志管理的存储过程有 3 个。DB2.UPDATE_SP_CONFIG 存储过程用来设置并存储日志配置参数。DB2.GET_SP_CONFIG 存储过程将列出日志参数及其值。第三个存储过程是 DB2.GET_SP_LOG,它可以打印日志文件的内容。
更新存储过程的配置
DB2.UPDATE_SP_CONFIG 是用来设置配置文件和共享内存中某个标记名所使用的日志管理存储过程。通常,我们会在命令行中使用 CLP 或 DB2 命令编辑器来执行这个存储过程,以实现对日志参数的更新。
CALL DB2.UPDATE_SP_CONFIG(log_token_name, value);
log_token_name —— 日志标记名。此框架使用了两类日志标记:全局标记和用于单个或一组存储过程的私有标记。
全局标记 —— 这个框架使用了两个全局标记。
GLOBAL_LOGGING —— 该值默认设置为 Y。在将这个参数设置为 N 时,LOGINFO 和 LOGGER 所使用的所有日志行为就会全部停止。利用这个参数,可以在运行时关闭所有存储过程的日志。
LOG_SPLIT —— 该默认设置为 N。当将 LOG_SPLIT 设置为 N 时,日志框架就会将所有的语句全部记录到日志文件 sp.log 中。当多个不同的 DB2 代理同时执行某个存储过程时,可以在一个日志文件中看到所有的日志语句,但是可以通过寻找每个 DB2 代理所使用的惟一应用程序 ID 号来区分每个日志语句。当所有语句都被记录到一个文件中时,就可以使用 grep 对这个文件进行过滤,从而查看某个特定 DB2 代理所记录的语句。在将这个参数设置为 Y 时,日志文件就会对每个 DB2 代理进行分隔,每个文件的名称都是以这个 DB2 代理的应用程序 ID 开头的。
私有标记 —— 单个存储过程或一组存储过程可以采用某个标记名,来说明这个标记需要输出日志。
使用 DB2.UPDATE_SP_CONFIG 存储过程更新日志标记:
要关闭全局日志:
call DB2.UPDATE_SP_CONFIG('GLOBAL_LOGGING','N');
要启用全局日志:
call DB2.UPDATE_SP_CONFIG('GLOBAL_LOGGING','Y');
要启用日志文件分隔功能:
call DB2.UPDATE_SP_CONFIG('LOG_SPLIT','Y');
注意:当启用日志分隔功能时,日志文件名就是从 db2 list applications 命令获得的应用程序 ID 的最后部分。例如,这个命令典型的输出如下所示:
清单 1. db2 list applications 命令的示例输出Auth Id Application Appl. Application Id DB # of
Name Handle Name Agents
-------- -------------- ---------- ------------------------------ -------- -----
DB2INST1 db2jcc_applica 864 GA0A0A34.A905.051223231443 FALCON 1
DB2INST1 db2jcc_applica 867 GA0A0A34.A904.051223231442 FALCON 1
DB2INST1 db2jcc_applica 866 GA0A0A34.A906.051223231441 FALCON 1
DB2INST1 db2jcc_applica 862 GA0A0A34.A907.051223231440 FALCON 1
如果上面的每个 DB2 代理都正在执行相同的存储过程,那么就会生成 4 个日志文件,其名称分别