67.2. 永続的な監査ログ

プロセスエンジンは、インスタンスの過去の状態など、プロセスインスタンスの実行に関する情報を保存できます。

この情報は多くの場合で役に立ちます。たとえば、特定のプロセスインスタンスに対して実行されたアクションを確認したり、特定のプロセスの効率を監視して分析したりできます。

ただし、ランタイムデータベースに履歴情報を保存すると、データベースのサイズが急増し、永続レイヤーのパフォーマンスにも影響を及ぼします。そのため、履歴ログ情報は別々に保存されます。

プロセスエンジンは、プロセスの実行時に生成するイベントに基づいてログを作成します。イベントリスナーのメカニズムを使用してイベントを受け取り、必要な情報を抽出してから、この情報をデータベースに永続化します。jbpm-audit モジュールには、JPA を使用してデータベースにプロセス関連の情報を保存するイベントリスナーが含まれています。

フィルターを使用してログ情報の範囲を制限できます。

67.2.1. プロセスエンジン監査ログデータモデル

プロセスエンジンの監査ログ情報をクエリーして、異なるシナリオで使用することができます。たとえば、1 つの特定のプロセスインスタンスに対して履歴ログを作成したり、特定のプロセスの全インスタンスのパフォーマンスを分析したりできます。

監査ログデータモデルはデフォルトの実装です。ユースケースによっては、必要な情報を格納するための独自のデータモデルを定義することもできます。プロセスイベントリスナーを使用して情報を抽出できます。

データモデルには、プロセスインスタンス情報用のエンティティー、ノードインスタンス情報用のエンティティー、およびプロセス変数インスタンス情報用のエンティティーが含まれます。

ProcessInstanceLog テーブルには、プロセスインスタンスに関する基本的なログ情報が含まれます。

表67.1 ProcessInstanceLog テーブルフィールド

フィールド説明Null 許容型

id

ログエンティティーのプライマリーキーおよび ID

Null 不可

correlationKey

このプロセスインスタンスの相関

 

duration

このプロセスインスタンスの開始日からの実際の時間

 

end_date

プロセスインスタンスの終了日 (該当する場合)

 

externalId

一部の要素 (デプロイメント ID など) に関連付けるのに使用される任意の外部 ID

 

user_identity

プロセスインスタンスを開始したユーザーの任意の識別子

 

outcome

プロセスインスタンスの結果。プロセスインスタンスがエラーイベントで終了した場合に、このフィールドにはエラーコードが含まれます。

 

parentProcessInstanceId

親プロセスインスタンスのプロセスインスタンス ID (該当する場合)

 

processid

プロセス ID

 

processinstanceid

プロセスインスタンス ID

Null 不可

processname

プロセスの名前

 

processtype

インスタンスのタイプ (プロセスまたはケース)

 

processversion

プロセスのバージョン

 

sla_due_date

サービスレベルアグリーメント (SLA) に基づくプロセスの期日

 

slaCompliance

SLA への準拠のレベル

 

start_date

プロセスインスタンスの開始日

 

status

プロセスインスタンスの状態にマップするプロセスインスタンスのステータス

 

NodeInstanceLog テーブルには、各プロセスインスタンス内で実行されたノードに関する詳細情報が含まれます。ノードインスタンスが内向き接続のいずれかから入力したり、外向き接続のいずれかを介して終了するたびに、イベントに関する情報がこのテーブルに保存されます。

表67.2 NodeInstanceLog テーブルフィールド

フィールド説明Null 許容型

id

ログエンティティーのプライマリーキーおよび ID

Null 不可

connection

このノードインスタンスの原因となったシーケンスフローの実際の識別子

 

log_date

イベントの日付

 

externalId

一部の要素 (デプロイメント ID など) に関連付けるのに使用される任意の外部 ID

 

nodeid

プロセス定義の対応するノードのノード ID

 

nodeinstanceid

ノードインスタンス ID

 

nodename

ノードの名前

 

nodetype

ノードのタイプ

 

processid

プロセスインスタンスが実行しているプロセスの ID

 

processinstanceid

プロセスインスタンス ID

Null 不可

sla_due_date

サービスレベルアグリーメント (SLA) に基づくノードの期日

 

slaCompliance

SLA への準拠のレベル

 

type

イベントのタイプ (0 = enter、1 = exit)

Null 不可

workItemId

(任意、特定のノードタイプのみ) ワークアイテムの識別子

 

nodeContainerId

ノードが埋め込みサブプロセスノード内にある場合のコンテナーの識別子

 

referenceId

参照識別子

 

observation

ノードがスケジューリングされたイベントタイプである場合、元のノードのインスタンス ID およびジョブ ID。この情報を使用して、再度ジョブを起動させることができます。

 

VariableInstanceLog テーブルには、変数インスタンスの変更に関する情報が含まれます。デフォルトでは、変数が値を変更すると、プロセスエンジンはログエントリーを生成します。プロセスエンジンは、変更前にエントリーをログに記録することもできます。

表67.3 VariableInstanceLog テーブルフィールド

フィールド説明Null 許容型

id

ログエンティティーのプライマリーキーおよび ID

Null 不可

externalId

一部の要素 (デプロイメント ID など) に関連付けるのに使用される任意の外部 ID

 

log_date

イベントの日付

 

processid

プロセスインスタンスが実行しているプロセスの ID

 

processinstanceid

プロセスインスタンス ID

Null 不可

oldvalue

ログ作成時の変数の以前の値

 

value

ログが作成された時点の変数の値

 

variableid

プロセス定義の変数 ID

 

variableinstanceid

変数インスタンスの ID

 

AuditTaskImpl テーブルには、ユーザータスクに関する情報が含まれます。

表67.4 AuditTaskImpl テーブルフィールド

フィールド説明Null 許容型

id

タスクログエンティティーのプライマリーキーおよび ID

 

activationTime

このタスクがアクティベートされた時間

 

actualOwner

このタスクに割り当てられている実際の所有者。この値は、所有者がタスクを要求した場合にのみ設定されます。

 

createdBy

このタスクを作成したユーザー

 

createdOn

タスクの作成日

 

deploymentId

このタスクが含まれるデプロイメントの ID

 

description

タスクの説明

 

dueDate

このタスクに設定した期日

 

name

タスクの名前

 

parentId

親タスク ID

 

priority

タスクの優先順位

 

processId

このタスクが属するプロセス定義 ID

 

processInstanceId

このタスクが関連付けられているプロセスインスタンス ID

 

processSessionId

このタスクの作成に使用する KIE セッション ID

 

status

タスクの現在の状態

 

taskId

タスクの識別子

 

workItemId

プロセス側でこのタスク ID に割り当てられたワークアイテムの識別子

 

lastModificationDate

プロセスインスタンスの状態が永続データベースに最後に記録された日時

 

BAMTaskSummary テーブルは、チャートとダッシュボードの構築に、BAM エンジンが使用するタスクに関する情報を収集します。

表67.5 BAMTaskSummary テーブルフィールド

フィールド説明Null 許容型

pk

ログエンティティーのプライマリーキーおよび ID

Null 不可

createdDate

タスクの作成日

 

duration

タスクの作成後の期間

 

endDate

タスクが終了状態に達した日付 (complete、exit、fail、skip)

 

processinstanceid

プロセスインスタンス ID

 

startDate

タスクの開始日

 

status

タスクの現在の状態

 

taskId

タスクの識別子

 

taskName

タスクの名前

 

userId

タスクに割り当てられたユーザー ID

 

optlock

最適なロック値として機能する version フィールド

 

TaskVariableImpl テーブルには、タスク変数インスタンスに関する情報が含まれます。

表67.6 TaskVariableImpl テーブルフィールド

フィールド説明Null 許容型

id

ログエンティティーのプライマリーキーおよび ID

Null 不可

modificationDate

変数が直近で変更された日

 

name

タスクの名前

 

processid

プロセスインスタンスが実行しているプロセスの ID

 

processinstanceid

プロセスインスタンス ID

 

taskId

タスクの識別子

 

type

変数のタイプ: タスクの入力または出力のいずれか

 

value

変数値

 

TaskEvent テーブルには、タスクインスタンスの変更に関する情報が含まれます。claimstartstop などの操作は、指定のタスクに発生したイベントのタイムラインビューを提供するためにこの表に保存されます。

表67.7 TaskEvent テーブルフィールド

フィールド説明Null 許容型

id

ログエンティティーのプライマリーキーおよび ID

Null 不可

logTime

このイベントが保存された日付

 

message

ログイベントメッセージ

 

processinstanceid

プロセスインスタンス ID

 

taskId

タスクの識別子

 

type

イベントのタイプ。タイプはタスクのライフサイクルフェーズに対応します。

 

userId

タスクに割り当てられたユーザー ID

 

workItemId

タスクが割り当てられているワークアイテムの識別子

 

optlock

最適なロック値として機能する version フィールド

 

correlationKey

プロセスインスタンスの相関キー

 

processType

プロセスインスタンスのタイプ (プロセスまたはケース)

 

currentOwner

タスクの現在のオーナー

 

67.2.2. プロセスイベントログをデータベースに格納するための設定

デフォルトのデータモデルを使用してデータベースにプロセス履歴情報のログを記録するには、セッションでロガーを登録する必要があります。

KIE セッションでのロガーの登録

KieSession ksession = ...;
ksession.addProcessEventListener(AuditLoggerFactory.newInstance(Type.JPA, ksession, null));

// invoke methods for your session here

情報を保存するデータベースを指定するには、persistence.xml ファイルを変更して監査ログクラス (ProcessInstanceLogNodeInstanceLog、および 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() メソッドを使用するデフォルトのインデクサーを適用します。