67.2. 永続的な監査ログ
プロセスエンジンは、インスタンスの過去の状態など、プロセスインスタンスの実行に関する情報を保存できます。
この情報は多くの場合で役に立ちます。たとえば、特定のプロセスインスタンスに対して実行されたアクションを確認したり、特定のプロセスの効率を監視して分析したりできます。
ただし、ランタイムデータベースに履歴情報を保存すると、データベースのサイズが急増し、永続レイヤーのパフォーマンスにも影響を及ぼします。そのため、履歴ログ情報は別々に保存されます。
プロセスエンジンは、プロセスの実行時に生成するイベントに基づいてログを作成します。イベントリスナーのメカニズムを使用してイベントを受け取り、必要な情報を抽出してから、この情報をデータベースに永続化します。jbpm-audit モジュールには、JPA を使用してデータベースにプロセス関連の情報を保存するイベントリスナーが含まれています。
フィルターを使用してログ情報の範囲を制限できます。
67.2.1. プロセスエンジン監査ログデータモデル
プロセスエンジンの監査ログ情報をクエリーして、異なるシナリオで使用することができます。たとえば、1 つの特定のプロセスインスタンスに対して履歴ログを作成したり、特定のプロセスの全インスタンスのパフォーマンスを分析したりできます。
監査ログデータモデルはデフォルトの実装です。ユースケースによっては、必要な情報を格納するための独自のデータモデルを定義することもできます。プロセスイベントリスナーを使用して情報を抽出できます。
データモデルには、プロセスインスタンス情報用のエンティティー、ノードインスタンス情報用のエンティティー、およびプロセス変数インスタンス情報用のエンティティーが含まれます。
ProcessInstanceLog テーブルには、プロセスインスタンスに関する基本的なログ情報が含まれます。
表67.1 ProcessInstanceLog テーブルフィールド
| フィールド | 説明 | Null 許容型 |
|---|---|---|
|
| ログエンティティーのプライマリーキーおよび ID | Null 不可 |
|
| このプロセスインスタンスの相関 | |
|
| このプロセスインスタンスの開始日からの実際の時間 | |
|
| プロセスインスタンスの終了日 (該当する場合) | |
|
| 一部の要素 (デプロイメント ID など) に関連付けるのに使用される任意の外部 ID | |
|
| プロセスインスタンスを開始したユーザーの任意の識別子 | |
|
| プロセスインスタンスの結果。プロセスインスタンスがエラーイベントで終了した場合に、このフィールドにはエラーコードが含まれます。 | |
|
| 親プロセスインスタンスのプロセスインスタンス ID (該当する場合) | |
|
| プロセス ID | |
|
| プロセスインスタンス ID | Null 不可 |
|
| プロセスの名前 | |
|
| インスタンスのタイプ (プロセスまたはケース) | |
|
| プロセスのバージョン | |
|
| サービスレベルアグリーメント (SLA) に基づくプロセスの期日 | |
|
| SLA への準拠のレベル | |
|
| プロセスインスタンスの開始日 | |
|
| プロセスインスタンスの状態にマップするプロセスインスタンスのステータス |
NodeInstanceLog テーブルには、各プロセスインスタンス内で実行されたノードに関する詳細情報が含まれます。ノードインスタンスが内向き接続のいずれかから入力したり、外向き接続のいずれかを介して終了するたびに、イベントに関する情報がこのテーブルに保存されます。
表67.2 NodeInstanceLog テーブルフィールド
| フィールド | 説明 | Null 許容型 |
|---|---|---|
|
| ログエンティティーのプライマリーキーおよび ID | Null 不可 |
|
| このノードインスタンスの原因となったシーケンスフローの実際の識別子 | |
|
| イベントの日付 | |
|
| 一部の要素 (デプロイメント ID など) に関連付けるのに使用される任意の外部 ID | |
|
| プロセス定義の対応するノードのノード ID | |
|
| ノードインスタンス ID | |
|
| ノードの名前 | |
|
| ノードのタイプ | |
|
| プロセスインスタンスが実行しているプロセスの ID | |
|
| プロセスインスタンス ID | Null 不可 |
|
| サービスレベルアグリーメント (SLA) に基づくノードの期日 | |
|
| SLA への準拠のレベル | |
|
| イベントのタイプ (0 = enter、1 = exit) | Null 不可 |
|
| (任意、特定のノードタイプのみ) ワークアイテムの識別子 | |
|
| ノードが埋め込みサブプロセスノード内にある場合のコンテナーの識別子 | |
|
| 参照識別子 |
VariableInstanceLog テーブルには、変数インスタンスの変更に関する情報が含まれます。デフォルトでは、変数が値を変更すると、プロセスエンジンはログエントリーを生成します。プロセスエンジンは、変更前にエントリーをログに記録することもできます。
表67.3 VariableInstanceLog テーブルフィールド
| フィールド | 説明 | Null 許容型 |
|---|---|---|
|
| ログエンティティーのプライマリーキーおよび ID | Null 不可 |
|
| 一部の要素 (デプロイメント ID など) に関連付けるのに使用される任意の外部 ID | |
|
| イベントの日付 | |
|
| プロセスインスタンスが実行しているプロセスの ID | |
|
| プロセスインスタンス ID | Null 不可 |
|
| ログ作成時の変数の以前の値 | |
|
| ログが作成された時点の変数の値 | |
|
| プロセス定義の変数 ID | |
|
| 変数インスタンスの ID |
AuditTaskImpl テーブルには、ユーザータスクに関する情報が含まれます。
表67.4 AuditTaskImpl テーブルフィールド
| フィールド | 説明 | Null 許容型 |
|---|---|---|
|
| タスクログエンティティーのプライマリーキーおよび ID | |
|
| このタスクがアクティベートされた時間 | |
|
| このタスクに割り当てられている実際の所有者。この値は、所有者がタスクを要求した場合にのみ設定されます。 | |
|
| このタスクを作成したユーザー | |
|
| タスクの作成日 | |
|
| このタスクが含まれるデプロイメントの ID | |
|
| タスクの説明 | |
|
| このタスクに設定した期日 | |
|
| タスクの名前 | |
|
| 親タスク ID | |
|
| タスクの優先順位 | |
|
| このタスクが属するプロセス定義 ID | |
|
| このタスクが関連付けられているプロセスインスタンス ID | |
|
| このタスクの作成に使用する KIE セッション ID | |
|
| タスクの現在の状態 | |
|
| タスクの識別子 | |
|
| プロセス側でこのタスク ID に割り当てられたワークアイテムの識別子 | |
|
| プロセスインスタンスの状態が永続データベースに最後に記録された日時 |
BAMTaskSummary テーブルは、チャートとダッシュボードの構築に、BAM エンジンが使用するタスクに関する情報を収集します。
表67.5 BAMTaskSummary テーブルフィールド
| フィールド | 説明 | Null 許容型 |
|---|---|---|
|
| ログエンティティーのプライマリーキーおよび ID | Null 不可 |
|
| タスクの作成日 | |
|
| タスクの作成後の期間 | |
|
| タスクが終了状態に達した日付 (complete、exit、fail、skip) | |
|
| プロセスインスタンス ID | |
|
| タスクの開始日 | |
|
| タスクの現在の状態 | |
|
| タスクの識別子 | |
|
| タスクの名前 | |
|
| タスクに割り当てられたユーザー ID | |
|
| 最適なロック値として機能する version フィールド |
TaskVariableImpl テーブルには、タスク変数インスタンスに関する情報が含まれます。
表67.6 TaskVariableImpl テーブルフィールド
| フィールド | 説明 | Null 許容型 |
|---|---|---|
|
| ログエンティティーのプライマリーキーおよび ID | Null 不可 |
|
| 変数が直近で変更された日 | |
|
| タスクの名前 | |
|
| プロセスインスタンスが実行しているプロセスの ID | |
|
| プロセスインスタンス ID | |
|
| タスクの識別子 | |
|
| 変数のタイプ: タスクの入力または出力のいずれか | |
|
| 変数値 |
TaskEvent テーブルには、タスクインスタンスの変更に関する情報が含まれます。claim、start、stop などの操作は、指定のタスクに発生したイベントのタイムラインビューを提供するためにこの表に保存されます。
表67.7 TaskEvent テーブルフィールド
| フィールド | 説明 | Null 許容型 |
|---|---|---|
|
| ログエンティティーのプライマリーキーおよび ID | Null 不可 |
|
| このイベントが保存された日付 | |
|
| ログイベントメッセージ | |
|
| プロセスインスタンス ID | |
|
| タスクの識別子 | |
|
| イベントのタイプ。タイプはタスクのライフサイクルフェーズに対応します。 | |
|
| タスクに割り当てられたユーザー ID | |
|
| タスクが割り当てられているワークアイテムの識別子 | |
|
| 最適なロック値として機能する version フィールド | |
|
| プロセスインスタンスの相関キー | |
|
| プロセスインスタンスのタイプ (プロセスまたはケース) |
67.2.2. プロセスイベントログをデータベースに格納するための設定
デフォルトのデータモデルを使用してデータベースにプロセス履歴情報のログを記録するには、セッションでロガーを登録する必要があります。
KIE セッションでのロガーの登録
KieSession ksession = ...; ksession.addProcessEventListener(AuditLoggerFactory.newInstance(Type.JPA, ksession, null)); // invoke methods for your session here
情報を保存するデータベースを指定するには、persistence.xml ファイルを変更して監査ログクラス (ProcessInstanceLog、NodeInstanceLog、および VariableInstanceLog) を追加する必要があります。
監査ログクラスが含まれる変更された persistence.xml ファイル
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence
version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/jbpm-ds</jta-data-source>
<mapping-file>META-INF/JBPMorm.xml</mapping-file>
<class>org.drools.persistence.info.SessionInfo</class>
<class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
<class>org.drools.persistence.info.WorkItemInfo</class>
<class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class>
<class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class>
<class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</class>
<class>org.jbpm.process.audit.ProcessInstanceLog</class>
<class>org.jbpm.process.audit.NodeInstanceLog</class>
<class>org.jbpm.process.audit.VariableInstanceLog</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.max_fetch_depth" value="3"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.connection.release_mode" value="after_transaction"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/>
</properties>
</persistence-unit>
</persistence>
67.2.3. プロセスイベントログを JMS キューに送信する設定
プロセスエンジンがデフォルトの監査ログ実装でデータベースにイベントを保存すると、データベース操作はプロセスインスタンスの実際の実行と同じトランザクション内で同期的に完了します。この操作には時間がかかり、負荷の高いシステムでは特に履歴ログとランタイムデータの両方が同じデータベースに格納される場合に、データベースのパフォーマンスに影響する可能性があります。
または、プロセスエンジンが提供する JMS ベースのロガーを使用できます。このロガーは、プロセスログエントリーをデータベースで直接永続化するのではなく、JMS キューにメッセージとして送信するように設定できます。
プロセスエンジントランザクションのロールバック時にデータの不整合を回避するために、JMS ロガーをトランザクションとして設定できます。
JMS 監査ロガーの使用
ConnectionFactory factory = ...;
Queue queue = ...;
StatefulKnowledgeSession ksession = ...;
Map<String, Object> jmsProps = new HashMap<String, Object>();
jmsProps.put("jbpm.audit.jms.transacted", true);
jmsProps.put("jbpm.audit.jms.connection.factory", factory);
jmsProps.put("jbpm.audit.jms.queue", queue);
ksession.addProcessEventListener(AuditLoggerFactory.newInstance(Type.JMS, ksession, jmsProps));
// invoke methods one your session here
これは、JMS 監査ロガーの設定を可能にする方法の 1 つです。AuditLoggerFactory クラスを使用して、追加の設定パラメーターを設定できます。
67.2.4. 変数の監査
デフォルトでは、プロセス変数およびタスク変数の値は文字列表現として監査テーブルに保存されます。文字列以外の変数型の文字列表現を作成するには、プロセスエンジンが variable.toString() メソッドを呼び出します。変数にカスタムクラスを使用する場合は、このメソッドをクラスに実装できます。多くの場合は、この表現で十分です。
ただし、特にプロセス変数またはタスク変数による効率的なクエリーが必要な場合は、ログで文字列表現が不十分な場合があります。たとえば、変数の値として使用する Person オブジェクトは、以下の構造を持つ場合があります。
プロセスまたはタスク変数の値として使用される Person オブジェクトの例
public class Person implements Serializable {
private static final long serialVersionUID = -5172443495317321032L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
toString() メソッドは、人間が判読できる形式を提供します。ただし、検索には十分ではない場合があります。サンプル文字列の値は Person [name="john", age="34"] です。このような文字列を多数検索して 34 の年齢を見つけると、データベースクエリーが非効率になります。
より効率的な検索を有効にするには、VariableIndexer オブジェクトを使用して変数を監査し、監査ログのストレージの関連する部分を抽出します。
VariableIndexer インターフェイスの定義
/**
* Variable indexer that transforms a variable instance into another representation (usually string)
* for use in log queries.
*
* @param <V> type of the object that will represent the indexed variable
*/
public interface VariableIndexer<V> {
/**
* Tests if this indexer can index a given variable
*
* NOTE: only one indexer can be used for a given variable
*
* @param variable variable to be indexed
* @return true if the variable should be indexed with this indexer
*/
boolean accept(Object variable);
/**
* Performs an index/transform operation on the variable. The result of this operation can be
* either a single value or a list of values, to support complex type separation.
* For example, when the variable is of the type Person that has name, address, and phone fields,
* the indexer could build three entries out of it to represent individual fields:
* person = person.name
* address = person.address.street
* phone = person.phone
* this configuration allows advanced queries for finding relevant entries.
* @param name name of the variable
* @param variable actual variable value
* @return
*/
List<V> index(String name, Object variable);
}
デフォルトのインデクサーは toString() メソッドを使用して、単一の変数の単一の監査エントリーを生成します。他のインデクサーは、単一の変数をインデックス化してオブジェクトの一覧を返すことができます。
Person タイプの効率的なクエリーを有効にするために、Person インスタンスを個別の監査エントリーにインデックス付けするカスタムインデクサーを構築できます。1 つは名前を表し、もう 1 つは年齢を表します。
Person タイプのインデクサーのサンプル
public class PersonTaskVariablesIndexer implements TaskVariableIndexer {
@Override
public boolean accept(Object variable) {
if (variable instanceof Person) {
return true;
}
return false;
}
@Override
public List<TaskVariable> index(String name, Object variable) {
Person person = (Person) variable;
List<TaskVariable> indexed = new ArrayList<TaskVariable>();
TaskVariableImpl personNameVar = new TaskVariableImpl();
personNameVar.setName("person.name");
personNameVar.setValue(person.getName());
indexed.add(personNameVar);
TaskVariableImpl personAgeVar = new TaskVariableImpl();
personAgeVar.setName("person.age");
personAgeVar.setValue(person.getAge()+"");
indexed.add(personAgeVar);
return indexed;
}
}
プロセスエンジンは、Person タイプの場合は、このインデクサーを使用して値をインデックス化し、その他の変数はすべてデフォルトの toString() メソッドでインデックス化されます。これで、年齢が 34 のプロセスインスタンスまたはタスクについてクエリーするには、以下のクエリーを使用できます。
-
変数名:
person.age -
変数値:
34
LIKE タイプのクエリーが使用されていないため、データベースサーバーはクエリーを最適化し、大量のデータに対して効率化できます。
カスタムインデクサー
プロセスエンジンは、プロセス変数とタスク変数の両方のインデクサーをサポートします。ただし、変数の監査ビューを表すさまざまなタイプのオブジェクトを生成する必要があるため、インデクサーに異なるインターフェイスを使用します。
以下のインターフェイスを実装してカスタムインデクサーを構築する必要があります。
-
プロセス変数の場合:
org.kie.internal.process.ProcessVariableIndexer -
タスク変数:
org.kie.internal.task.api.TaskVariableIndexer
インターフェイスのいずれかに 2 つのメソッドを実装する必要があります。
-
accept: タイプがこのインデクサーによって処理されるかどうかを示します。プロセスエンジンは、1 つのインデクサーのみが指定の変数値をインデックス化できることを想定し、タイプを受け入れる最初のインデクサーを使用します。 -
index: 値にインデックス化し、監査ログに含まれるオブジェクト (通常は文字列) を生成します。
インターフェイスを実装したら、この実装を JAR ファイルとしてパッケージ化し、以下のファイルのいずれかで実装の一覧を作成する必要があります。
-
プロセス変数の場合は
META-INF/services/org.kie.internal.process.ProcessVariableIndexerファイル。これは、プロセス変数インデクサーの完全修飾クラス名を一覧表示 (1 行に 1 クラス名) します。 -
タスク変数の場合は
META-INF/services/org.kie.internal.task.api.TaskVariableIndexerファイル。タスク変数インデクサーの完全修飾クラス名を一覧表示 (1 行に 1 クラス名) します。
ServiceLoader のメカニズムは、これらのファイルを使用してインデクサーを検出します。プロセスまたはタスク変数をインデックス化する場合、プロセスエンジンは登録されたインデクサーを調べ、変数の値を受け入れるインデクサーを見つけます。他のインデクサーがこの値を受け入れない場合、プロセスエンジンは toString() メソッドを使用するデフォルトのインデクサーを適用します。