本文涉及到的内容:
- 日志组件的关系及MDC
- 配置历史记录级别(HistoryLevel)
- 配置基于db的事件日志(Event logging)
1.日志组件的关系及MDC
-
日志门面中,slf4j和commons-logging都是不能直接打印日志的,需要依赖日志实现把日志记录下来
-
优选关系:Logback > Log4j > Java util logging
-
slf4j和Logback组合的时候默认不需要桥接方式
其中,Log4j既是门面又是实现;一般的组合是slf4j+logback;slf4j-log4j12实现slf4j+log4j; sf4j-jdk14的桥接方式能够实现slf4j+Java util logging
Jcl-over-slf4j排除掉commons-logging的历史实现,当需要commons-logging时,只需要引入jcl-over-slf4j即可;log4j-over-slf4j排除掉Log4j的历史实现
MDC(映射诊断上下文)的作用?
MDC打印输出一些上下文信息(如当前登录的用户、当前操作的订单,存储到线程的变量里面);MDC默认是没有开启的,需要手动设置;activiti做的比较保守,只有在流程执行过程中出现异常的时候才会记录MDC的信息。
2.配置历史记录级别
3.配置基于DB的事件日志
实现过程:
Activiti开启mdc的日志记录
在MDC的记录过程的上下文数据都是基于运行过程的,所以需要有一个可以执行的流程来完成这个事,若采用helloword的程序是在方法中执行,不合适,并且junit单元测试中不支持输入。
1.新建测试类ConfigMDCTest.java (复制MyUnitTest中的代码)
public class ConfigMDCTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigMDCTest.class);
@Rule
public ActivitiRule activitiRule = new ActivitiRule();//activitiRule将整个流程引擎的启动和创建过程都构建好了
@Test
@Deployment(resources = {"com/syc/activiti/my-process.bpmn20.xml"})
public void test() {
ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process");//activiti提供的将单元测试文件部署到流程引擎中
assertNotNull(processInstance);
Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
assertEquals("Activiti is awesome!", task.getName());
activitiRule.getTaskService().complete(task.getId());//添加该代码,让流程执行下去
}
}
尝试运行时编译报错,显示表已经存在。原因是activiti.cfg.xml文件中数据库更新的值必须写成true
<property name="databaseSchemaUpdate" value="true"/>
修改数据库配置为h2数据库
<beanid="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000;MVCC=TRUE"/>
<property name="jdbcDriver" value="org.h2.Driver"/>
<property name="jdbcUsername" value="sa"/>
<property name="jdbcPassword" value=""/>
</bean>
2.修改logback.xml的以下内容
(添加mdc的属性,并且控制台输出中将std改为mdc)
<configuration debug="false"scan="true"scanPeriod="30seconds">
<!--定义日志文件的存储地址勿在LogBack的配置中使用相对路径-->
property name="log.dir" value="target/logs"/>
<property name="encoding" value="UTF-8"/>
<property name="plain" value="%msg%n"/>
<property name="std" value="%d{HH:mm:ss.SSS}[%thread][%-5level]%msg %X{user} %logger{10}.%M:%L%n "/>
<property name="mdc" value="%d{HH:mm:ss.SSS}[%thread][%-5level]%msg ProcessDefinitonId=%X{mdcProcessDefinitionID}
executionId=%X{mdcExecutionId} mdcProcessInstanceID=%X{mdcProcessInstanceID} mdcBusinessKey=%X{mdcBusinessKey} %longger{10}.%M:%L%n" />
<property name="normal" value="%d{yyyy-MM-dd:HH:mm:ss.SSS}[%thread][%-5level] %logger{10}.%M:%L %msg%n"/>
<!--控制台输出-->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${mdc}</pattern>
<charset>${encoding}</charset>
</encoder>
</appender>
3.在ConfigMDCTest中打开mdc
在test()方法的首行中加入打开mdc的代码,此时运行测试程序不会有mdc日志(因此mdc只在程序错误时打印输出)
LogMDC.setMDCEnabled(true);
4.新建一个MDCErrorDelegate的类(只是用于测试,用于制造异常)
public class MDCErrorDelegate implements JavaDelegate {
private static final Logger LOGGER = LoggerFactory.getLogger(MDCErrorDelegate.class);
@Override
public void execute(DelegateExecution execution) {
LOGGER.info("run MDCErrorDelegate");
throw new RuntimeException("only test!");
}
}
5.将my-process.bpmn20.xml中的userTask改为serviceTask
<!--<userTask id="someTask" name="Activiti is awesome!" />-->
<serviceTask id="someTask" activiti:class="com.syc.activiti.delegate.MDCErrorDelegate"/>
测试能够输出ProcessDefinitionId的信息
(只有当出错时才能打印出日志,因此并不实用,下面通过拦截器进行改进,把每一个流程信息都打印出来)
6 .使用拦截器,把每一个流程信息都打印出来
编写拦截器的代码:
public class MDCCommandInvoker extends DebugCommandInvoker {
private static final Logger LOGGER = LoggerFactory.getLogger(MDCCommandInvoker.class);
@Override
public void executeOperation(Runnable runnable) {
boolean mdcEnabled = LogMDC.isMDCEnabled();//首先获取mdc的状态是否生效
LogMDC.setMDCEnabled(true);
if (runnable instanceof AbstractOperation) {
AbstractOperation operation = (AbstractOperation) runnable;
if (operation.getExecution() != null) {
LogMDC.putMDCExecution(operation.getExecution());//将可执行对象放到上下文中
}
}
super.executeOperation(runnable);//执行正常的业务操作
LogMDC.clear();//上下文信息清空
if(!mdcEnabled){//若进来时候mdc就是不生效的,就要还原不生效的状态
LogMDC.setMDCEnabled(false);
}
}
}
7.复制一个新的activiti_mdc.cfg.xml
配置beans里面的属性commandInvoker为我们自定义的MDCCommandInvoker
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<property name="commandInvoker" ref="commandInvoker"/>
</bean>
<bean id="commandInvoker" class="com.syc.activiti.iterator.MDCCommandInvoker"/>
8.复制一个新的my-process_mdcerror.bpmn20.xml
恢复原来的my-process.bpmn20.xml
9.在ConfigMDCTest中修改配置文件为“activiti_mdc.cfg.xml”
@Rule
public ActivitiRule activitiRule = new ActivitiRule("activiti_mdc.cfg.xml");
最后测试运行应该是能把每一个流程id、执行id等流程相关信息打印输出