3-3. 実装方針の検討~「ログ」の出力~

バッチ処理の結果を確認するために出力されるのが「ログ」です。
バッチ処理が正常に終了したのか、問題があった場合はどのデータにどのような問題があったのかを把握するために出力します。
同一システムであれば、このログの出力内容が統一されてしかるべきですが、なかなかそうはいかないケースが多いようです。

まず、このログをどのような形で出力するか。
基本的にはファイルで出力されるのが一般的です。
最近では処理ログに相当する情報をデータベースに出力する方法も使われているようです。

普通に考えると、エラーが発生した場合はトランザクションをロールバックしますので、エラー情報をデータベースに出力しても、ロールバック時にそれまで書き込んだエラー情報もロールバックされてしまいます。
これには「自律型トランザクション」を使用することで、エラー情報をテーブルに書き込んだまま残すことができます。

自律型トランザクションの使用方法は別途記述しますが、ファイル/データベースに関わらず、何らかの方法で処理結果をログに残すということが重要です。

ログに出力すべき情報

まず、ログに出力すべき内容は「システム名」「プログラム名」です。
次に処理の開始時刻と終了時刻。処理の結果、正常だったのか異常だったのかを出力します。
ここまでは、どんなシステムでも必ず出力すべき内容になるかと思います。

開発の仕事が多い方だと「システム名なんていちいち書かなくてもわかるのでは?」と思われるかもしれませんが、システムを運用する立場になると、複数のシステムに関わる場合が出てきます。
その時に出力されるファイルだけでは、どのシステムのログファイルかわかりません。
確実にどのシステムが出力したログなのかわかるようにするため、システム名といった当たり前の情報も出力しておくべきです。

そして、同じようにログがどの処理によって出力されたものなのを示す必要があります。

そして次に必要なのは処理件数の出力です。
漠然と「処理件数」ではなく、より具体的に、入力件数と出力件数、もしくは登録件数、更新件数、削除件数と、より具体的にどのような処理を行った件数なのかを示すのが望ましいです。

入力元となるテーブルが複数、AとBのテーブルからデータを取得するのであれば、Aから取得した件数とBから取得した件数の両方を出力すべきです。出力件数も同様です。
ただし、トランザクションとマスタを結合させて入力データとする場合は、結合後の件数を入力件数とすべきでしょう。

この内容を踏まえた、ログ出力内容のイメージは下記のようになります。
—————————————-
システム : ○△▽○△▽システム
処理名称 : ○□○□○□処理(XXXXABCDEF)

開始日時 : 2015/04/01 21:05:01
終了日時 : 2015/04/01 21:31:28

処理対象件数 : 18019件
登録件数 : 16010件
更新件数 : 2009件
—————————————-

エラー情報のログ出力

エラーが発生したら、どのようなエラーが発生したのか、どこで発生したのかを出力します。
エラーの内容は、あらかじめハンドリングされたエラーであれば、プログラムされたエラーメッセージを出力します。
その時、エラーの内容と同時に、エラーの発生箇所、エラーの原因となったデータの内容そのもの、もしくはエラーとなった情報を特定するためのキーとなる情報を合わせて出力する必要があります。

また、エラーの発生箇所を出力するにも注意が必要です。
PL/SQLでは、SQLERRM関数でエラーの発生プロシージャ、発生行を取得することができますが、特に行番号は、コンパイラが出力する行番号と、テキストエディタ中の行番号と一致しないケースがあります(過去のバージョンでは空白行はカウントされないこともありました)。

そのために使用されるのが、ステップ番号です。
ある変数に、プログラム中で一意となる文字列を設定するようにし、エラー時にはその変数をログ出力します。
そうすることで、エラーの発生箇所が明確になります。
以前のカーソルループのサンプルコードに埋め込んでみましょう。

------------------------------------------------------------
DECLARE
  CURSOR c1 IS
  SELECT empno, ename, hiredate, deptno
  FROM   emp
  WHERE  empno >= 5000;
  r1 c1%ROWTYPE;
  lv_step_no VARCHAR2(10);  --added.
BEGIN
  lv_step_no := '10';  --added.
  FOR r1 IN c1
  LOOP
    lv_step_no := '20';  --added.
    INSERT INTO emptmp(empno, ename, hiredate, deptno)
    VALUES(r1.empno, r1.ename, r1.hiredate, r1.deptno);
    lv_step_no := '30';  --added.
  END LOOP;
  lv_step_no := '40';  --added.
EXCEPTION
  WHEN OTHERS THEN
    dbms_output.put_line(lv_step_no || SQLERRM);  --added.
    RAISE;
END;
------------------------------------------------------------

記述は冗長になりますが、エラーの発生箇所は明確になります。

また、この記述を発展させることで、もう一つのメリットを享受することができます。
これは、後述の共通関数の章でお話ししましょう。

2018-02-12

Posted by tfurukaw