/**
* Copyright (c) 2005-2009 springside.org.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
*
* $Id: JdbcAppenderTask.java 353 2009-08-22 09:33:28Z calvinxiu
*/
package org.springside.examples.showcase.log.appender;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.log4j.spi.LoggingEvent;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springside.examples.showcase.queue.BlockingConsumer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* 将Queue中的log4j event写入数据库的消费者任务.
*
* 即时阻塞的读取Queue中的事件,达到缓存上限后使用Jdbc批量写入模式.
* 如需换为定时读取模式,继承于PeriodConsumer稍加改造即可.
*
* @see BlockingConsumer
*
* @author calvin
*/
public class JdbcLogWriter extends BlockingConsumer {
protected String sql;
protected int batchSize = 10;
protected List<LoggingEvent> eventsBuffer = Lists.newArrayList();
protected SimpleJdbcTemplate jdbcTemplate;
protected TransactionTemplate transactionTemplate;
/**
* 带Named Parameter的insert sql.
*
* Named Parameter的名称见AppenderUtils中的常量定义.
*/
public void setSql(String sql) {
this.sql = sql;
}
/**
* 批量读取事件数量, 默认为10.
*/
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
/**
* 根据注入的DataSource创建jdbcTemplate.
*/
@Resource
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
/**
* 根据注入的PlatformTransactionManager创建transactionTemplate.
*/
@Resource
public void setDefaultTransactionManager(PlatformTransactionManager defaultTransactionManager) {
transactionTemplate = new TransactionTemplate(defaultTransactionManager);
}
/**
* 消息处理函数,将消息放入buffer,当buffer达到batchSize时执行批量更新函数.
*/
@Override
protected void processMessage(Object message) {
LoggingEvent event = (LoggingEvent) message;
eventsBuffer.add(event);
if (logger.isDebugEnabled()) {
logger.debug("get event: {}", new LoggingEventWrapper(event).convertToString());
}
//已到达BufferSize则执行批量插入操作
if (eventsBuffer.size() >= batchSize) {
updateBatch();
}
}
/**
* 将Buffer中的事件列表批量插入数据库.
*/
@SuppressWarnings("unchecked")
public void updateBatch() {
try {
//分析事件列表, 转换为jdbc批处理参数.
int i = 0;
Map[] paramMapArray = new HashMap[eventsBuffer.size()];
for (LoggingEvent event : eventsBuffer) {
paramMapArray[i++] = parseEvent(event);
}
final SqlParameterSource[] batchParams = SqlParameterSourceUtils.createBatch(paramMapArray);
//执行批量插入,如果失败调用失败处理函数.
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
jdbcTemplate.batchUpdate(getActualSql(), batchParams);
if (logger.isDebugEnabled()) {
for (LoggingEvent event : eventsBuffer) {
logger.debug("saved event: {}", new LoggingEventWrapper(event).convertToString());
}
}
} catch (DataAccessException e) {
status.setRollbackOnly();
handleDataAccessException(e, eventsBuffer);
}
}
});
//清除已完成的Buffer
eventsBuffer.clear();
} catch (Exception e) {
logger.error("批量提交任务时发生错误.", e);
}
}
/**
* 退出清理函数,完成buffer中未完成的消息.
*/
@Override
protected void clean() {
if (!eventsBuffer.isEmpty()) {
updateBatch();
}
logger.debug("cleaned task {}", this);
}
/**
* 分析Event, 建立Parameter Map, 用于绑定sql中的Named Parameter.
*/
protected Map<String, Object> parseEvent(LoggingEvent event) {
Map<String, Object> parameterMap = Maps.newHashMap();
LoggingEventWrapper eventWrapper = new LoggingEventWrapper(event);
parameterMap.put("thread_name", eventWrapper.getThreadName());
parameterMap.put("logger_name", eventWrapper.getLoggerName());
parameterMap.put("log_time", eventWrapper.getDate());
parameterMap.put("level", eventWrapper.getLevel());
parameterMap.put("message", eventWrapper.getMessage());
return parameterMap;
}
/**
* 可被子类重载的数据访问错误处理函数,如将出错的事件持久化到文件.
*/
protected void handleDataAccessException(DataAccessException e, List<LoggingEvent> errorEventBatch) {
if (e instanceof DataAccessResourceFailureException) {
logger.error("database connection error", e);
} else {
logger.error("other database error", e);
}
for (LoggingEvent event : errorEventBatch) {
logger.error("event insert to database error, ignore it: "
+ new LoggingEventWrapper(event).convertToString(), e);
}
}
/**
* 可被子类重载的sql提供函数,可对sql语句进行特殊处理,如日志表的表名可带日期后缀 LOG_2009_02_31.
*/
protected String getActualSql() {
return sql;
}
}