2021年2月17日水曜日

自律トランザクションを使ってデータベース操作のログを取得する

 Oracle APEXのアプリケーションで、操作のログはどのように残すのですか?と質問を受けました。

アプリケーションに依存したもので無ければ、Oracle APEXのアプリケーションへのアクセスは、ワークスペース・ユーティリティApplication Expressビューとして提供されているAPEX_WORKSPACE_ACTIVITY_LOGから参照できます。

ログの保持期間はデフォルトで28日(ログを保存する2つの表が14日毎に切り替わるため)で、パフォーマンスに影響が出るため延長は推奨されていません。とはいえ、変更方法ですが、オンプレのインストールの場合はOracle APEXの管理アプリケーションにサインインし、インスタンスの管理を開きます。

ログとファイルの管理よりログ間隔の管理を開きます。

ログ情報を保持する最小日数の指定ができます。

対応する画面がない環境では、APIの呼び出しによって変更が可能です。APEX_INSTANCE_ADMINパッケージに含まれるSET_LOG_SWITCH_INTERVALプロシージャを使用します。

アプリケーションで発生したエラーも標準でログに記録されていて、アクティビティのモニターからアプリケーション・エラーを開くことで参照できます。

いつ、誰が、どのページで、どのコンポーネントで、どのようなエラーが発生したのか、などを確認することができます。

標準のログについては以上です。

本記事の本題である、カスタムのログ取得について紹介します。

確認のためにクイックSQLの以下のモデルにて、表TTX_ACTIVITY_LOGとTTX_OPERATIONSを作成します。

# prefix: ttx
# semantics: default
activity_log
operation vc400
operation_date date /default sysdate

operations
name vc20
value vc20

アプリケーション・ビルダーより、空のアプリケーションを作成します。名前サンプルログ記録としました。

アプリケーションが作成できたら、ホーム・ページをページ・デザイナで開き、3つのリージョンをContent Bodyに追加します。

最初にタイトルDB操作タイプ静的コンテンツとしてリージョンを作成します。


リージョン内に3つのページ・アイテムを作成します。名前をそれぞれP1_TX1、P1_TX2、P1_TX3とし、ラベルもそれぞれTX1、TX2、TX3とします。P1_TX1での設定は以下になります。


ページ・アイテムを送信するためのボタンを作成します。ボタン名B_SUBMITとし、ラベル送信にします。


静的コンテンツのリージョンについては以上で作成完了です。

続いて対話モード・レポートのリージョンを作成します。タイトルとしてOPERATIONSを設定し、タイプ対話モード・レポート表名としてTTX_OPERATIONSを選択します。


同様にタイトルACTIVITY_LOGタイプ対話モード・レポート表名TTX_ACTIVITY_LOGのリージョンを作成します。


画面の開発は以上で完了です。ページを実行すると以下の画面が表示されます。

ページ・デザイナに戻り、プロセスを3つ追加します。以下のコードを実行します。名前Process TX1とし、タイプコードを実行を選びます。TX2、TX3については、それぞれTX1の部分をTX2、TX3に変更します。

declare
l_log varchar2(400);
begin
l_log := :REQUEST || 'が押されたのでTX1の値' || :P1_TX1 || 'を保存します。';
insert into ttx_activity_log(operation) values(l_log);
insert into ttx_operations(name, value) values('TX1', :P1_TX1);
end;


ページ・アイテムP1_TX1、P1_TX2、P1_TX3の値を受け取って、表TTX_OPERATIONSにデータを挿入し、同時に操作を表TTX_ACTIVITY_LOGに書き込むようになりました。

この状態で実行します。TX1、TX2、TX3に値を指定し、送信ボタンをクリックします。


以下のようにレポートが表示されます。データが保存され、操作も記録されています。きちんと動いているように見えます。


今度はTX3に20バイトを超えるデータを指定します。想定どおりエラーORA-12899: 列"APEXDEV"."TTX_OPERATIONS"."VALUE"の値が大きすぎます(実際: 180、最大: 20)が発生し、データベースへの書き込みは行われません。


表の内容をみるとページ・アイテムP1_TX1、P1_TX2、P1_TX3の値が保存されていないのは想定どおりなのですが、操作も記録されていません。操作の記録はエラーの発生によらず、記録されている必要があります。


そのため、ログを保存する操作は自律トランザクションとして、本体とは異なるトランザクションを開始する必要があります。

次のプロシージャを作成します。pragma autonomous_transaction句を含めることで、独立したトランザクションが開始されます。プロシージャの終了前にcommitすることで、このプロシージャを呼び出したトランザクションがrollbackしても、この書き込みは永続化されます。

create or replace procedure ttx_log_operation
(
p_operation in varchar2
)
as
pragma autonomous_transaction;
begin
insert into ttx_activity_log(operation) values(p_operation);
commit;
end ttx_log_operation;

プロセスに記述したコードを以下に変更します。表TTX_ACTIVITY_LOGへのINSERT文をプロシージャTTX_LOG_OPERATIONの呼び出しに変更します。TX2、TX3についても同様の変更を行います。

declare
l_log varchar2(400);
begin
l_log := :REQUEST || 'が押されたのでTX1の値' || :P1_TX1 || 'を保存します。';
ttx_log_operation(l_log);
insert into ttx_operations(name, value) values('TX1', :P1_TX1);
end;

先ほど失敗したエラーになった操作を再実行すると、レポートの表示は以下に変わります。

データが保存されていないのは同様ですが、Operationには、プロセス自体が呼び出されていることが分かる操作ログを確認できます。

以上で、自律トランザクションを使ったデータベースへのログ書き込み方法の紹介は終了です。

 確認に使用したアプリケーションのエクスポートを以下に置きました。

https://github.com/ujnak/apexapps/blob/master/exports/samplelogging.sql

Oracle APEXのアプリケーション開発の一助になれば幸いです。