67.2. 持久性审计日志


流程引擎可以存储关于进程实例执行的信息,包括实例的连续历史状态。

很多情况下,这些信息很有用。例如,您可能希望验证已为特定进程实例执行哪些操作,或监控和分析特定进程的效率。

但是,在运行时数据库中存储历史记录信息会导致数据库迅速增加的大小,并会影响到持久层的性能。因此,历史记录日志信息会单独存储。

进程引擎基于进程在执行过程中生成的事件创建日志。它使用事件监听程序机制接收事件并提取必要的信息,然后将此信息保留在数据库中。jbpm-audit 模块包含一个事件监听程序,它利用 JPA 在数据库中存储与进程相关的信息。

您可以使用过滤器来限制日志信息的范围。

67.2.1. 进程引擎审计日志数据模型

您可以查询进程引擎审计日志的日志信息,以便在不同的情况下使用它,例如为一个特定的进程实例创建历史记录日志,或分析特定进程的所有实例的性能。

审计日志数据模型是默认的实现。根据您的用例,您还可以定义自己的数据模型来存储所需信息。您可以使用进程事件监听程序来提取信息。

数据模型包含三个实体:一个用于处理实例信息,一个用于节点实例信息,一个用于处理变量实例信息。

ProcessInstanceLog 表包含有关进程实例的基本日志信息。

Expand
表 67.1. ProcessInstanceLog 表字段
字段描述nullable

id

日志实体的主密钥和 ID

不是 NULL

correlationKey

此进程实例的关联

 

duration

自从开始日期开始以来,此进程实例的实际持续时间

 

end_date

适用时,进程实例的结束日期

 

externalId

用于与一些元素关联的可选外部标识符,如部署 ID

 

user_identity

启动进程实例的用户的可选标识符

 

结果

进程实例的结果。如果进程实例完成并带有错误事件,则此字段包含错误代码。

 

parentProcessInstanceId

父进程实例的进程实例 ID(如果适用)

 

processid

进程的 ID

 

processinstanceid

进程实例 ID

不是 NULL

processname

进程的名称

 

processtype

实例的类型(处理或问题单)

 

processversion

进程的版本

 

sla_due_date

根据服务级别协议(SLA)的到期日期

 

slaCompliance

SLA 合规级别

 

start_date

进程实例的开始日期

 

status

映射到进程实例状态的进程实例的状态

 

NodeInstanceLog 表包含有关各个进程实例内执行哪些节点的更多信息。每当从其进入的连接中输入某个节点实例时,或通过其中一个传出连接退出,则有关该事件的信息都会存储在此表中。

Expand
表 67.2. NodeInstanceLog 表字段
字段描述nullable

id

日志实体的主密钥和 ID

不是 NULL

连接

导致此节点实例的序列流的实际识别符

 

log_date

事件的日期

 

externalId

用于与一些元素关联的可选外部标识符,如部署 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

引用标识符

 

影响

如果节点是调度的事件类型,则原始节点实例 ID 和作业 ID。您可以使用这些信息再次触发作业。

 

VariableInstanceLog 表包含有关变量实例中更改的信息。默认情况下,进程引擎在变量更改其值后生成日志条目。流程引擎也可以在更改之前记录条目。

Expand
表 67.3. VariableInstanceLog 表字段
字段描述nullable

id

日志实体的主密钥和 ID

不是 NULL

externalId

用于与一些元素关联的可选外部标识符,如部署 ID

 

log_date

事件的日期

 

processid

进程实例正在执行的进程 ID

 

processinstanceid

进程实例 ID

不是 NULL

oldvalue

在日志进行时,之前变量的值

 

value

在日志进行时,变量的值

 

variableid

进程定义中的变量 ID

 

variableinstanceid

变量实例的 ID

 

AuditTaskImpl 表包含有关用户任务的信息。

Expand
表 67.4. AuditTaskImpl 表字段
字段描述nullable

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 引擎用于构建 chart 和仪表板的任务的信息。

Expand
表 67.5. BAMTaskSummary table 字段
字段描述nullable

pk

日志实体的主密钥和 ID

不是 NULL

createdDate

任务创建日期

 

duration

任务创建的时间

 

endDate

任务到达最终状态时的日期(完成、退出、失败、跳过)

 

processinstanceid

进程实例 ID

 

startDate

任务启动时的日期

 

status

任务的当前状态

 

taskId

任务标识符

 

taskName

任务的名称

 

userId

分配给任务的用户 ID

 

optlock

作为 optimistic 锁定值的 version 字段

 

TaskVariableImpl 表包含有关任务变量实例的信息。

Expand
表 67.6. TaskVariableImpl 表字段
字段描述nullable

id

日志实体的主密钥和 ID

不是 NULL

modificationDate

最近修改变量的日期

 

name

任务的名称

 

processid

进程实例正在执行的进程 ID

 

processinstanceid

进程实例 ID

 

taskId

任务标识符

 

type

变量的类型:任务的输入或输出

 

value

变量值

 

TaskEvent 表包含有关任务实例中更改的信息。声明启动和停止 等操作存储在此表中,以提供给定任务发生的事件的时间表视图。

Expand
表 67.7. TaskEvent 表字段
字段描述nullable

id

日志实体的主密钥和 ID

不是 NULL

logTime

保存此事件的日期

 

message

事件日志信息

 

processinstanceid

进程实例 ID

 

taskId

任务标识符

 

type

事件类型。类型与任务的生命周期阶段对应

 

userId

分配给任务的用户 ID

 

workItemId

为任务分配的工作项目的标识符

 

optlock

作为 optimistic 锁定值的 version 字段

 

correlationKey

进程实例的关联密钥

 

processType

进程实例的类型(进程或问题单)

 

currentOwner

任务的当前所有者

 

要使用默认数据模型在数据库中记录进程历史记录信息,您必须在您的会话中注册日志记录器。

在 KIE 会话中注册日志记录器

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

// invoke methods for your session here
Copy to Clipboard Toggle word wrap

要指定存储信息的数据库,您必须修改 persistence.xml 文件,使其包含审计日志类: ProcessInstanceLogNodeInstanceLogVariableInstanceLog

修改后的 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>
Copy to Clipboard Toggle word wrap

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
Copy to Clipboard Toggle word wrap

这只是配置 JMS 审计日志记录器的可能方法之一。您可以使用 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 + "]";
    }
}
Copy to Clipboard Toggle word wrap

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);
}
Copy to Clipboard Toggle word wrap

默认索引器使用 toString() 方法为单个变量生成单个审计条目。其他索引器可以从索引单个变量返回对象列表。

要启用对 Person 类型的有效查询,您可以构建将 Person 实例索引到单独的审计条目中的自定义索引程序,一个用于代表该年龄。

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;
    }

}
Copy to Clipboard Toggle word wrap

进程引擎可在 Person 类型时使用此索引器索引值,而所有其他变量则以默认 到String() 方法进行索引。现在,要查询指向年龄 34 人的进程实例或任务,您可以使用以下查询:

  • 变量名称: person.age
  • 变量值: 34

因为没有使用 LIKE 类型查询,数据库服务器可以优化查询,并使其在大量数据上高效。

自定义索引器

进程引擎支持处理和任务变量的索引器。但是,它将不同的接口用于索引器,因为它们必须生成代表 变量审计视图的不同类型的对象。

您必须实施以下接口来构建自定义索引器:

  • 对于进程变量: org.kie.internal.process.ProcessVariableIndexer
  • 对于任务变量: org.kie.internal.task.api.TaskVariableIndexer

您必须对以下任意接口实施两种方法:

  • accept :指示某个类型是否由此索引程序处理。进程引擎期望只有一个索引程序可以索引给定变量值,因此它会使用接受该类型的第一个索引器。
  • 索引 :索引值,生成对象或对象列表(通常是字符串)以包含在审计日志中。

在实施接口后,您必须将此实施打包为 JAR 文件,并在以下其中一个文件中列出实施:

  • 对于进程变量,META-INF/services/org.kie.internal.process.Process.ProcessVariableIndexer 文件,列出进程变量索引器(每行单类名)的完全限定类名称。
  • 对于任务变量,META-INF/services/org.kie.internal.task.api.TaskVariableIndexer 文件,它列出了任务变量索引器(每行单类名称)

ServiceLoader 机制使用这些文件发现索引器。当索引进程或任务变量时,进程引擎会检查注册的索引器以查找接受变量值的索引程序。如果没有其他索引程序接受值,则进程引擎将使用 toString() 方法应用默认的索引程序。

返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat