今回のテクニックの紹介は日常に潜む問題を解決してくれるテクニックであり
非常に有効なものとして公開するに至ったのでよく理解して欲しい。
さてCLPでエラーを最初にMONMSG CPF0000 でモニターしておくと
どのようなエラーが発生してもモニターすることができると
「60. CLP でのエラー・モニター (1)」で解説した。
例えば
0009.00 MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR)) : (CLPでの実行) : RETURN 0016.00 ERROR: RCVMSG MSGTYPE(*LAST) RMV(*NO) MSG(&MSG) :
という具合であり事実 MONMSG CPF0000 は有効に働いている。
これまでどのCLPでも最初に MONMSG CPF0000 を宣言するのは
非常に有効であるとして海外サイトでも半ば常識になっている。
一方では
「124. デバッグするにはMONMSGははずして」で紹介したように
MONMSG CPF0000 ではエラーが発生したときにはエラー行がわからないので
デバッグ中やまだ開発中であるときは MONMSG を一時的に除去するように
解説した。
ところが実際は MONMSG CPF0000 を記述したものをエンド・ユーザーに
リリースするわけなのでそこで実際の運用中にエラーが発生すると
エラーはわかるが発生箇所がわからないという矛盾を生じてしまう。
そこでエラー・メッセージもエラー発生場所の特定もわかる処理が
必要になってくる。
(1) SENDER の情報をさぐる
0026.00 DCL VAR(&SENDER) TYPE(*CHAR) LEN(80) : 0029.00 MONMSG MSGID(CPF9999) EXEC(GOTO CMDLBL(ERROR)) : 0050.00 ERROR: RCVMSG MSGTYPE(*LAST) RMV(*NO) KEYVAR(&MSGKEY) + 0051.00 MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) + 0052.00 SENDER(&SENDER) MSGF(&MSGF) + 0053.00 SNDMSGFLIB(&MSGFLIB)
のように SENDER の CL 変数 として 80バイトまたは 720バイトの &SENDER という
文字変数を定義することができる。(SENDER は *SHORTの80バイトまたは *LONGを指定すると720バイトの
情報を取得することができる)
この SENDER という変数にはメッセージの送り手の情報が戻され
どのプログラムのどのような命令でエラーになったのかの情報が収められている。
もちろん発生場所の情報もある。
それではこの SENDER を解析すればようことになるのだがいろいろ調べてみると
発生場所としては MI(=Machine Interface)の場所はわかるものの
CLPのステートメント番号は入っていないことがわかった。
しかし ステートメントXXXXでエラーが発生しました、という情報は
よく目にするのでどこかにCLPのステートメント場所の情報はあるはずである。
(2) CPF9999 による解決
そこでIBM SWMA(=プログラム相談室)に相談して調べてもらったところ
意外な方法が判明した。
MONMSG CPF0000 ではなく MONMSG CPF9999 でモニターすると
エラーのステートメント番号が判明するということである。
なるほど試してみると確かに受信したCPF9999 の中にエラー行が
あってエラー行も報告されていることがわかった。
そこで MONMSG CPF9999 に変更したCLPを作ってみると次のようにすれば
エラー場所とエラー・メッセージの両方を取得できることがわかった。
[サンプルCLP: TESTSNDR ]
ソースはこちらから
0001.00 PGM 0002.00 /*-------------------------------------------------------------------*/ 0003.00 /* TESTSNDR : エラー場所を表示するテスト */ 0004.00 /* */ 0005.00 /* 2021/03/28 作成 */ 0006.00 /*-------------------------------------------------------------------*/ 0007.00 DCL VAR(&MSG) TYPE(*CHAR) LEN(132) 0008.00 DCL VAR(&STMMSG) TYPE(*CHAR) LEN(132) 0009.00 DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) 0010.00 DCL VAR(&MSGF) TYPE(*CHAR) LEN(10) 0011.00 DCL VAR(&MSGFLIB) TYPE(*CHAR) LEN(10) 0012.00 DCL VAR(&MSGKEY) TYPE(*CHAR) LEN(4) 0013.00 DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(132) 0014.00 DCL VAR(&ERRDTA) TYPE(*CHAR) LEN(132) 0015.00 DCL VAR(&TYPE) TYPE(*CHAR) LEN(1) 0016.00 DCL VAR(&TOPGMQ) TYPE(*CHAR) LEN(10) 0017.00 DCL VAR(&MSGTYPE) TYPE(*CHAR) LEN(10) + 0018.00 VALUE('*ESCAPE ') 0019.00 DCL VAR(&RCVTYPE) TYPE(*CHAR) LEN(10) + 0020.00 VALUE('*LAST') 0021.00 DCL VAR(&APIERR) TYPE(*CHAR) LEN(116) + 0022.00 VALUE(X'000074') /* 2 進数 */ 0023.00 DCL VAR(&ERR) TYPE(*CHAR) LEN(1) 0024.00 DCL VAR(&NULL4) TYPE(*CHAR) LEN(4) + 0025.00 VALUE(X'00000000') 0026.00 DCL VAR(&SNDPGM) TYPE(*CHAR) LEN(10) 0027.00 DCL VAR(&STMT) TYPE(*CHAR) LEN(4) 0028.00 MONMSG MSGID(CPF9999) EXEC(GOTO CMDLBL(ERROR)) 0029.00 0030.00 /*( 環境の取得 )*/ 0031.00 RTVJOBA TYPE(&TYPE) 0032.00 IF COND(&TYPE *EQ '0') THEN(DO) /* バッチ */ 0033.00 CHGVAR VAR(&TOPGMQ) VALUE('*SYSOPR ') 0034.00 ENDDO /* バッチ */ 0035.00 ELSE CMD(DO) /* 対話式 */ 0036.00 CHGVAR VAR(&TOPGMQ) VALUE('*TOPGMQ ') 0037.00 ENDDO /* 対話式 */ 0038.00 0039.00 DLTF FILE(QTEMP/TESTF) 0040.00 RETURN 0041.00 0042.00 APIERR: 0043.00 CHGVAR VAR(&MSGID) VALUE(%SST(&APIERR 9 7)) 0044.00 CHGVAR VAR(&MSGDTA) VALUE(%SST(&APIERR 17 100)) 0045.00 CHGVAR VAR(&MSGF) VALUE('QCPFMSG ') 0046.00 CHGVAR VAR(&MSGFLIB) VALUE('QSYS ') 0047.00 GOTO SNDMSG 0048.00 0049.00 ERROR: RCVMSG MSGTYPE(*LAST) RMV(*NO) KEYVAR(&MSGKEY) + 0050.00 MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) + 0051.00 MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB) 0052.00 IF COND(&MSGID *EQ 'CPF9999') THEN(DO) 0053.00 CHGVAR VAR(&ERRDTA) VALUE(&MSGDTA) 0054.00 RCVMSG MSGTYPE(*PRV) MSGKEY(&MSGKEY) RMV(*NO) + 0055.00 MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) + 0056.00 MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB) 0057.00 ENDDO 0058.00 CHGVAR VAR(&STMMSG) VALUE(' プログラム ' *CAT + 0059.00 %SST(&ERRDTA 8 10) *TCAT + 0060.00 ' のステートメント ' *CAT %SST(&ERRDTA + 0061.00 24 4) *CAT ' で次のエラーが発生しました。 ') 0062.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&STMMSG) + 0063.00 TOMSGQ(&TOPGMQ) MSGTYPE(*DIAG) 0064.00 SNDMSG: IF COND(&MSGID *EQ ' ') THEN(DO) 0065.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) + 0066.00 TOMSGQ(&TOPGMQ) MSGTYPE(&MSGTYPE) 0067.00 MONMSG MSGID(CPF2400) EXEC(RETURN) 0068.00 ENDDO 0069.00 ELSE CMD(DO) 0070.00 SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) + 0071.00 MSGDTA(&MSGDTA) TOMSGQ(&TOPGMQ) + 0072.00 MSGTYPE(&MSGTYPE) 0073.00 MONMSG MSGID(CPF2400) EXEC(RETURN) 0074.00 ENDDO 0075.00 ENDPGM
[コンパイル]
CRTCLPGM OBJLIB/TESTSNDR SRCFILE(MYSRCLIB/QCLSRC9 AUT(*ALL)
[解説]
最初のエラー・モニターを
0028.00 MONMSG MSGID(CPF9999) EXEC(GOTO CMDLBL(ERROR))
として定義しておいて
0049.00 ERROR: RCVMSG MSGTYPE(*LAST) RMV(*NO) KEYVAR(&MSGKEY) + 0050.00 MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) + 0051.00 MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB)
でエラー・メッセージを受信するがこれはほとんどが MSGID=CPF9999 が受信されることになる。
しかしここで欲しいのは &MSGDTA なので
0052.00 IF COND(&MSGID *EQ 'CPF9999') THEN(DO) 0053.00 CHGVAR VAR(&ERRDTA) VALUE(&MSGDTA) 0054.00 RCVMSG MSGTYPE(*PRV) MSGKEY(&MSGKEY) RMV(*NO) + 0055.00 MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) + 0056.00 MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB) 0057.00 ENDDO
として直前のメッセージを受信するがこれが本当のエラー・メッセージである。
先に受信した CPF9999のメッセージ・データ: &ERRDTA を使って
0058.00 CHGVAR VAR(&STMMSG) VALUE(' プログラム ' *CAT + 0059.00 %SST(&ERRDTA 8 10) *TCAT + 0060.00 ' のステートメント ' *CAT %SST(&ERRDTA + 0061.00 24 4) *CAT ' で次のエラーが発生しました。 ') 0062.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&STMMSG) + 0063.00 TOMSGQ(&TOPGMQ) MSGTYPE(*DIAG)
としてから本当のエラーも次のようにして報告する。
0064.00 SNDMSG: IF COND(&MSGID *EQ ' ') THEN(DO) 0065.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) + 0066.00 TOMSGQ(&TOPGMQ) MSGTYPE(&MSGTYPE) 0067.00 MONMSG MSGID(CPF2400) EXEC(RETURN) 0068.00 ENDDO 0069.00 ELSE CMD(DO) 0070.00 SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) + 0071.00 MSGDTA(&MSGDTA) TOMSGQ(&TOPGMQ) + 0072.00 MSGTYPE(&MSGTYPE) 0073.00 MONMSG MSGID(CPF2400) EXEC(RETURN) 0074.00 ENDDO
このプログラムを実行してみると
コマンド入力
> call test.com/testsndr
プログラム TESTSNDR のステートメント 3900 で次のエラーが発生しました
QTEMP に,タイプ *FILE のオブジェクト TESTF が見つからない。
というようにエラー箇所とエラー・メッセージが同時に報告されるようになった。
この方法でリリースしておけばエラーが発生したとしても
エラー・メッセージだけでなくエラー箇所も同時に報告されるようになった。
これは開発上大きな進歩である。
ぜひ活用して頂きたい。
次にこの方法を組み込んだCLPテンプレートを紹介していく予定である。