八.通用日志管理工具
1. 引言
AS/400系统上有日志(JRN)和日志接收器(JRNRCV)目标对象,用来存放对数据库文件进行增删改等操作的日志记录。日志接收器是存放日志记录的具体地方,它可以是由一个无限增大的文件组成,也可以是由多个固定大小的文件组成。多个日志接收器文件形成日志接收器链,当当前ATTACHED的有限大小的日志接收器满了以后,系统会自动产生或由用户产生一个新的同样属性的接收器文件。
用CHGJRN命令可以改变产生新的日志方式,当Manage receivers( MNGRCV)选项选择*SYSTEM时,系统会自动产生新的日志接收器,当Manage receivers( MNGRCV)选项选择*USER时,则当日志接收器满了以后,需要由用户产生新的日志接收器。当Manage receivers( MNGRCV)选项选择*SYSTEM时,可以在Delete receivers(DLTRCV)选项里选择*YES来让系统自动删除已经被DETACH的日志接收器文件。
但如果我们希望定制保留固定个数的日志记录,或者保存若干天的日志记录,或者其他一些原因不能让系统自动删除日志接收器,那么我们就必须手工删除日志接收器,如果系统业务比较频繁,产生日志接收器量比较大,可能会很快使系统硬盘空间涨满,那么我们手工维护日志接收器的工作量很大。在一般的双机热备系统中比如MIMIX、OMS等都提供了日志管理功能,本文提供一个与这些软件日志管理功能类似的独立的一个通用日志管理工具,可以使你不需要依靠其他软件就可以独立地由程序来完成日志管理工作。
保留日志接收器,通常有两种方式,一种是按时间即天来保留,一种是按日志记录个数来保留。本文的例子是按日志记录个数来保留日志接收器的。但按照该例的方法也可以很容易实现按天来保留日志接收器。
2.日志管理原理
(1) 要由程序来实现日志管理,必须先用CHGJRN命令把日志属性中Manage receivers( MNGRCV)选项改为*USER。
(2) 如果Manage receivers( MNGRCV)属性已经设置为*USER,那么当前日志接收器超过其阀值大小后,系统会发送CPF7099的消息。程序根据这个消息知道当前日志接收器已经满了,需要产生一个新的日志接收器。这个消息是由系统发送到日志属性中Journal message queue(MSGQ)中所定义的消息队列里。通常日志该消息队列缺省为QSYSOPR。用CRTMSGQ命令创建一个叫TTJRNMSGQ的消息队列。并用该队列名用CHGJRN命令修改Journal message queue(MSGQ)的值。
(3) 把保留的日志个数设置成参数,保存在一个数据区域里,下例创建了一个保留1000万日志记录参数的数据区域:
CRTDTAARA DTAARA(QGPL/TTDTA)
TYPE(*CHAR)
LEN(10)
VALUE('0010000000')
这个参数可以由用户根据硬盘大小来调节,用CHGDTAARA命令可以改变这个参数。
(4) 在程序运行过程中,当产生新的日志接收器文件后,程序会计算最新日志接收器件的开始日志序号与最前一个日志接收器的开始日志序号差,并用这个差与数据区域中定义的保留最大日志记录个数相比较,当日志序号差超过最大日志记录个数,程序就会自动删除过量的日志接收器文件,并把最后一个删除日志接收器文件保存在叫LASTRCV的SAVF文件里,以维护硬盘空间不会增长。 下面是日志管理原理示意图:
图 日志管理示意
3.源程序
(1) 主调程序JRNMGR
用RCVMSG命令读TTJRNMSGQ消息队列,当有CPF7099消息时,先调用CHKJRNCL程序来产生新的日志接收器,然后再调用删除日志接收器程序,如果删除接收器不成功,主调程序等待一段时间后会继续调用删除日志接收器程序:
CODE:
/*===================================================================*/
/* */
/* PROGRAM NAME : JRNMGR */
/* CREATED BY : BLOG LIOU */
/* DESCRIPTION : */
/*===================================================================*/
PGM
DCL VAR(&MSGID) TYPE(*CHAR) LEN(7)
DCL VAR(&RTCDCHK) TYPE(*CHAR) LEN(1)
DCL VAR(&JRNNAM) TYPE(*CHAR) LEN(10) +
VALUE('TTJRN’') /* 测试日志TTJRN */
DCL VAR(&JRNLIB) TYPE(*CHAR) LEN(10) +
VALUE(‘QGPL’)
DCL VAR(&DJRNFLG) TYPE(*CHAR) LEN(1) VALUE('0')
DCL VAR(&DJRNRTN) TYPE(*CHAR) LEN(1)
CHGVAR VAR(&RTCDCHK) VALUE('0')
LOOP:
/* 条件满足时调用删除日志接收器程序 */
IF COND(&DJRNFLG *EQ + '1')) THEN(DO)
CALL PGM(DELJRNCL) PARM(&JRNNAM &JRNLIB +
&DJRNRTN)
IF COND(&DJRNRTN *EQ '0') THEN(CHGVAR +
VAR(&DJRNFLG) VALUE('0'))
ENDDO
/* 读TTJRNMSGQ消息队列,调用产生新日志接收器程序*/
RCVMSG MSGQ(TTJRNMSGQ) RMV(*YES) MSGID(&MSGID)
IF COND((&MSGID *EQ 'CPF7099') *OR (&RTCDCHK +
*EQ '1')) THEN(DO)
CALL PGM(CHKJRNCL) PARM( &JRNNAM &JRNLIB +
&RTCDCHK)
CHGVAR VAR(&MSGID) VALUE(' ')
IF COND(&RTCDCHK *EQ '0') THEN(DO)
CHGVAR VAR(&DJRNFLG) VALUE('1') /* 设置执行删除接收器程序标志 */
CLRMSGQ MSGQ(TTJRNMSGQ)
MONMSG MSGID(CPF2357)
ENDDO
ENDDO
DLYJOB DLY(60)
GOTO CMDLBL(LOOP)
ENDPGM(2) 产生新的日志接收器程序CHKJRNCL
当主调程序从消息队列读到CPF7099的消息后,开始调用CHKJRNCL程序,来产生新的日志接收器。
CODE:
/*===================================================================*/
/* PROGRAM NAME : CHKJRNCL */
/* CREATED BY : BLOG LIOU */
/* CREATED DATE : */
/* DESCRIPTION : */
/*===================================================================*/
PGM PARM( &JRNNAM &JRNLIB &RTCD)
DCL VAR(&JRNNAM) TYPE(*CHAR) LEN(10)
DCL VAR(&JRNLIB) TYPE(*CHAR) LEN(10)
DCL VAR(&RTCD) TYPE(*CHAR) LEN(1)
CHGVAR VAR(&RTCD) VALUE('0')
/*-------------------------------------------------------------------*/
/*产生新的日志接收器 */
/*-------------------------------------------------------------------*/
SNDMSG MSG(&JRNNAM *TCAT '*GEN') TOUSR(MYUSR)
CHGJRN JRN(&JRNLIB/&JRNNAM) JRNRCV(*GEN)
MONMSG MSGID(CPF7013) EXEC(CHGVAR VAR(&RTCD) +
VALUE('1'))
END: ENDPGM(3) 删除日志接收器程序DELJRNCL
当产生新的日志接收器调用成功后,主调程序会调用删除过量日志接收器程序。
CODE:
/*===================================================================*/
/* PROGRAM NAME : DELJRNCL */
/* CREATED BY : BLOG LIOU */
/* CREATED DATE : */
/* DESCRIPTION : */
/*===================================================================*/
PGM PARM( &JRNNAM &JRNLIB &RTNCDE)
DCL VAR(&RTNCDE) TYPE(*CHAR) LEN(1)
DCL VAR(&CURRCV) TYPE(*CHAR) LEN(10)
DCL VAR(&JRNRCV) TYPE(*CHAR) LEN(10)
DCL VAR(&RCVLIB) TYPE(*CHAR) LEN(10)
DCL VAR(&JRNNAM) TYPE(*CHAR) LEN(10)
DCL VAR(&JRNLIB) TYPE(*CHAR) LEN(10)
DCL VAR(&STRSEQ) TYPE(*DEC) LEN(10 0)
DCL VAR(&STRSEQ1) TYPE(*DEC) LEN(10 0)
DCL VAR(&STRSEQ2) TYPE(*DEC) LEN(10 0)
DCL VAR(&SNDMSG) TYPE(*CHAR) LEN(50)
DCL VAR(&JRNNAMPR) TYPE(*CHAR) LEN(5)
DCL VAR(&DELJRNSC) TYPE(*CHAR) LEN(10)
DCL VAR(&DELJRNS) TYPE(*DEC) LEN(10 0)
DCL VAR(&SNDMSG1) TYPE(*CHAR) LEN(10)
/*-------------------------------------------------------------------*/
/* Init,从TTDTA数据区域读保留日志个数 */
/*-------------------------------------------------------------------*/
CHGVAR VAR(&RTNCDE) VALUE('0')
CHGVAR VAR(&JRNNAMPR) VALUE(%SST(&JRNNAM 1 5))
RTVDTAARA DTAARA(TTDTA (1 10)) RTNVAR(&DELJRNSC)
CHGVAR VAR(&DELJRNS) VALUE(&DELJRNSC)
/*------------------------------------------------------------------------*/
/* 提取当前日志接收器第一个日志记录序号和接收器名 */
/*------------------------------------------------------------------------*/
RTVJRNE JRN(&JRNLIB/&JRNNAM) RCVRNG(*CURRENT) +
TOENT(*FIRST) RTNSEQNBR(&STRSEQ) +
RTNRCV(&CURRCV) RTNRCVLIB(&RCVLIB)
/*-------------------------------------------------------------------*/
/* 删除过量日志接收器 */
/*-------------------------------------------------------------------*/
LOOP: RTVJRNE JRN(&JRNLIB/&JRNNAM) RCVRNG(*CURCHAIN) +
TOENT(*FIRST) RTNSEQNBR(&STRSEQ1) +
RTNRCV(&JRNRCV) RTNRCVLIB(&RCVLIB)
IF COND(&JRNRCV *EQ &CURRCV) THEN(GOTO +
CMDLBL(END))
CHGVAR VAR(&STRSEQ2) VALUE(&STRSEQ1 - &STRSEQ)
/* 日志序号差在定义参数范围内,不执行删除动作 */
IF COND(&STRSEQ2 *LT &DELJRNS) THEN(GOTO +
CMDLBL(END))
/*删除前先备份,LASTRCV重复使用*/
DLTF FILE(&RCVLIB/LASTRCV)
MONMSG MSGID(CPF2105)
CRTSAVF FILE(&RCVLIB/LASTRCV)
SAVOBJ OBJ(&JRNRCV) LIB(&RCVLIB) DEV(*SAVF) +
OBJTYPE(*JRNRCV) SAVF(&RCVLIB/LASTRCV) +
SAVFDTA(*YES)
MONMSG MSGID(CPF3770) /* No object saved */
/*删除日志接收器*/
DLTJRNRCV JRNRCV(&RCVLIB/&JRNRCV)
MONMSG MSGID(CPF2105 CPF7023)
CHGVAR VAR(&SNDMSG) VALUE(&JRNRCV *BCAT 'Journal +
Receiver Deleted !')
SNDMSG MSG(&SNDMSG) TOUSR(MYUSR)
GOTO CMDLBL(LOOP)
END: ENDPGM最后把JRNMGR程序提交在QBATCH子系统下运行,就可以实现对日志的自动维护。