Delphi/400のプログラム実行中に、
エラーやレコードロックの発生した原因をジョブログから調べる局面があるかと存じます。
そういった際に、ACSやPCOMMといったIBM i のエミュレータで
WRKACTJOBから対象ジョブを見つけ出してジョブログを参照するのが一般的ですが、
状況によっては実行中のプログラムから自分自身のジョブログをそのまま参照したい
こともあるかと思います。
本記事では、その手順を解説します。
1次ジョブログのみ参照できれば良い場合
前提として、本記事ではジョブログの種類を以下のように記載します。
- 1次ジョブログ:エミュレータで表示できる基本的なジョブログの情報で
「すべてのメッセージの表示」で参照できる。 - 2次ジョブログ:エミュレータで1次ジョブログを参照中にF1キーを押すと表示される
「追加のメッセージ情報」で、原因や回復手順も参照できる。
ジョブログをDelphi/400のプログラムから参照する場合は
DSPJOBLOGコマンドを発行してファイルに内容を出力して、
その内容をSQLなどで参照することになります。
1次ジョブログだけの参照でよい場合は、
DSPJOBLOG OUTPUT(*OUTFILE) OUTFILE(QTEMP/TEST1)
といったコマンドをTAS400コンポーネントのRemoteCmdメソッドで発行します。
AS4001.RemoteCmd('DSPJOBLOG OUTPUT(*OUTFILE) OUTFILE(QTEMP/TEST1)');
なお、パラメータOUTPUT(*OUTFILE)は自ジョブの場合しか指定できないため、
エミュレータ等の他のジョブからジョブログをファイルに出力することは出来ません。
出力されたファイルは、以下のようなSQLで参照可能です。
(フィールドQMHMDTに、実際のメッセージデータが入っています)
// すべての情報を見る場合 SELECT * FROM QTEMP/TEST1 // メッセージデータ(QMHMDT)のみ見る場合 SELECT QMHMDT FROM QTEMP/TEST1
(※ソート順が不安な場合は、SQLの末尾に「ORDER BY RRN(TEST1) 」を付けて到着順指定とします。)
補足事項として、TAS400コンポーネントでIBM i に接続後に
CHGJOB LOG(4 00 *SECLVL) LOGCLPGM(*YES)
というコマンドをRemoteCmdなどで発行しておくと、重大度00のメッセージや
ジョブ内部のコマンド発行履歴まで詳細にジョブログに出力されます。
2次ジョブログも参照したい場合
原因や回復手順も含まれた2次ジョブログもプログラム内から参照したい場合、
手順は複雑になりますが実装は可能です。
DSPJOBLOGコマンドで2次ジョブログも出力する場合は、
『OUTPUT(*APIDFN)』
というパラメータを使用します。しかし、
AS4001.RemoteCmd('DSPJOBLOG OUTPUT(*APIDFN)');
といったコマンドをいきなり発行しようとすると、
『*APIDFNオプションの場合には,最初にQMHCTLJL APIを実行しなければならない。』
というエラーメッセージが表示されるため、
「QMHCTLJL APIとは何なのか?」と断念された方もおられるかもしれません。
QSYSライブラリにあるプログラム「QMHCTLJL」を
事前に特定のパラメータで呼び出しておくことを示すのですが、
システムのプログラムでCLやRPG等ではないため、Call400を使って呼び出すことはできません。
そこで、Delphi/400からでもCall400を使って呼び出しが可能なCLを作成して、
そのCLの中からQMHCTLJLを呼び出します。
事前準備としては、以下の3点が必要になりますのでそれぞれ解説します。
- ①:ライブラリの決定
- ②:呼び出し先のCLソースの作成
- ③:Delphi/400から呼び出すロジックの記述
①:ライブラリの決定
後述のCLソースでも解説しますが、
ジョブログの出力先となるファイルを事前に任意の場所に作成しておく必要があります。
このファイルの作成場所にはQTEMPを使用することができない(エラーになる)ため、
QTEMPでない任意のライブラリを決定しておきます。
なお、今回のロジックではファイル名は
1次ジョブログ:「PJBLG」
2次ジョブログ:「SJBLG」
としております。
(※ファイル名が5文字なのは、②のロジックの都合で5桁以内にしたかったためです。特にこの名前で無ければならないわけではありません。)
②:呼び出し先のCLソースの作成
下記のCLプログラムのソースを、任意のライブラリに展開してコンパイルします。
今回は「CRTJOBLOG」というプログラム名としております。
<CLP【CRTJOBLOG】ソース>
PGM PARM( + /*ライブラリ名*/ &LIBN + /*ジョブ番号*/ &NBR) /*変数定義と初期値*/ DCL VAR(&LIBN) TYPE(*CHAR) LEN(10) /*ライブラリ名*/ DCL VAR(&FILENAME) TYPE(*CHAR) LEN(65) DCL VAR(&FORMAT) TYPE(*CHAR) LEN(8) VALUE('CTLJ0100') DCL VAR(&MSG) TYPE(*CHAR) LEN(30) DCL VAR(&FILTER) TYPE(*CHAR) LEN(4) VALUE(X'00000000') DCL VAR(&MSGQ) TYPE(*CHAR) LEN(20) VALUE('*SYSOPR ') DCL VAR(&ERROR) TYPE(*CHAR) LEN(256) VALUE(X'00000000') DCL VAR(&USR) TYPE(*CHAR) LEN(10) DCL VAR(&NBR) TYPE(*CHAR) LEN(6) RTVJOBA USER(&USR) NBR(&NBR) /*出力先となるファイルをQSYSから複製*/ CRTDUPOBJ OBJ(QAMHJLPR) FROMLIB(QSYS) OBJTYPE(*FILE) TOLIB(&LIBN) NEWOBJ(PJBLG) MONMSG MSGID(CPF0000) /*既に存在エラー回避*/ CRTDUPOBJ OBJ(QAMHJLSC) FROMLIB(QSYS) OBJTYPE(*FILE) TOLIB(&LIBN) NEWOBJ(SJBLG) MONMSG MSGID(CPF0000) /*既に存在エラー回避*/ /*パラメータとなる変数値の設定*/ CHGVAR VAR(%BIN(&FILENAME 1 4)) VALUE(65) CHGVAR VAR(%SST(&FILENAME 5 10)) VALUE('PJBLG ') CHGVAR VAR(%SST(&FILENAME 15 10)) VALUE(&LIBN) CHGVAR VAR(%SST(&FILENAME 25 10)) VALUE('JOB ') CHGVAR VAR(%SST(&FILENAME 28 6)) VALUE(&NBR) CHGVAR VAR(%SST(&FILENAME 35 10)) VALUE('SJBLG ') CHGVAR VAR(%SST(&FILENAME 45 10)) VALUE(&LIBN) CHGVAR VAR(%SST(&FILENAME 55 10)) VALUE(%SST(&FILENAME 25 10)) CHGVAR VAR(%SST(&FILENAME 65 1)) VALUE('0') /*プログラムQMHCTLJL APIの実行*/ CALL PGM(QSYS/QMHCTLJL) PARM(&FILENAME &FORMAT &MSG &FILTER + &MSGQ &ERROR) /*使用するファイルメンバーの指定*/ OVRDBF FILE(PJBLG) TOFILE(PJBLG) MBR(%SST(&FILENAME 25 10)) OVRSCOPE(*JOB) OVRDBF FILE(SJBLG) TOFILE(SJBLG) MBR(%SST(&FILENAME 25 10)) OVRSCOPE(*JOB) /*使用するファイル権限の設定*/ CHGOBJOWN OBJ(&LIBN/%SST(&FILENAME 5 10)) OBJTYPE(*FILE) + NEWOWN(&USR) CUROWNAUT(*REVOKE) RVKOBJAUT OBJ(&LIBN/%SST(&FILENAME 5 10)) OBJTYPE(*FILE) + USER(*PUBLIC) AUT(*ALL) CHGOBJOWN OBJ(&LIBN/%SST(&FILENAME 35 10)) OBJTYPE(*FILE) + NEWOWN(&USR) CUROWNAUT(*REVOKE) RVKOBJAUT OBJ(&LIBN/%SST(&FILENAME 35 10)) OBJTYPE(*FILE) + USER(*PUBLIC) AUT(*ALL) /*終了処理*/ ENDPGM
③:Delphi/400から呼び出すロジックの記述
上記①②で作成したプログラムの呼び出しと、
ジョブログの受け皿となるファイルをオープンする処理を記述します。
ネイティブ接続と、今回はFireDAC接続からなるジョブが接続済みの状態で
Button1をクリックすると、Call400(cal4CRTJOBLOG)で②のCLを呼び出して
QMHCTLJL APIを実行します。
cal4CRTJOBLOGに渡すパラメータは、
1.手順①で設定したファイル出力先ライブラリ
2.ジョブ番号(戻り値)
となります。
(ジョブ番号のパラメータは、OVRDBFをCL内ではなくDelphi/400側で掛けたい場合の使用を想定しています。)
呼び出したCLでQMHCTLJL APIを実行したあとDSPJOBLOGコマンドを実行することで、
CLにて指定されたファイル・メンバーに1次・2次ジョブログが出力されます。
{******************************************************************************* ジョブログ出力ボタン 押下時処理 *******************************************************************************} procedure TForm1.Button1Click(Sender: TObject); begin // QMHCTLJL API 実行CLの呼び出し with cal4CRTJOBLOG do begin LibraryName := '*LIBL'; ProgramName := 'CRTJOBLOG'; Params.Clear; AddParam('A', 10, 0); // ライブラリ名 AddParam('A', 6, 0); // (戻値)ジョブ番号 Value[0] := edtLIBL.Text; // ライブラリ名 Value[1] := ''; // (戻値)ジョブ番号 Execute; end; // QMHCTLJLの後に、ジョブログ発行コマンド AS400.RemoteCmd('DSPJOBLOG OUTPUT(*APIDFN)'); end;
この後の処理で、
- 1次ジョブログ:「SELECT * FROM PJBLG」
- 2次ジョブログ:「SELECT * FROM SJBLG」
というSQLを発行すれば、
出力されたジョブログをDelphi/400の画面上で参照できます。
(1次ジョブログの内容は、本記事前半の
「1次ジョブログのみ参照できれば良い場合」で出力されるものと同じになります。)
(今回は両ファイルとも「JOB+ジョブ番号」の名前のメンバーにジョブログが出力されます。
②のCL内でOVRDBFを行っておけば、そのままSELECTするだけで参照できます。)
(1次ジョブログでメッセージデータのみを参照したい場合は、
「SELECT QMHMDT FROM PJBLG」といったSQLで記述します。
全フィールド表示するとエラーになる場合にお試しください。
ソート順が不安な場合は、SQLの末尾に「ORDER BY RRN(PJBLG) 」を付けて到着順指定とします。)
(2次ジョブログでメッセージのみを参照したい場合は、
「SELECT QMHLIN FROM SJBLG」といったSQLで記述します。
全フィールド表示するとエラーになる場合にお試しください。
ソート順が不安な場合は、SQLの末尾に「ORDER BY RRN(SJBLG) 」を付けて到着順指定とします。)