`
houfeng0923
  • 浏览: 144825 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

日志框架(二)------log4j 扩展 JdbcAppender输出器

阅读更多

 

 

log4j中提供了将日志记录到数据库中的输出器: org.apache.log4j.jdbc.JDBCAppender。通过以下配置即可生效

 

log4j.logger.dbLog=ERROR,dbLogAppender
log4j.appender.dbLogAppender=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.dbLogAppender.URL=jdbc:mysql://localhost:3306/test
log4j.appender.dbLogAppender.driver=com.mysql.jdbc.Driver
log4j.appender.dbLogAppender.user=root
log4j.appender.dbLogAppender.password=xiaodi
log4j.appender.dbLogAppender.sql=insert into t_log4j_info(priority,message,catalog,time) values('%p','%m','%c','%d{yyyy-MM-dd HH:mm:ss}')
log4j.appender.dbLogAppender.bufferSize=10
log4j.appender.dbLogAppender.layout=org.apache.log4j.PatternLayout

扩展类 PrepareStatementJdbcAppender 和 JndiDataSourceAppender

 

JdbcAppender使用JDBC 的Statement提交sql,本例基于PreparedStatement重新实现数据库输出器(PrepareStatementJdbcAppender),

并以此为父类实现调用JNDI数据源的输出器(JndiDataSourceAppender)。

 

PrepareStatementJdbcAppender类定义

 

/**
 * 支持预编译sql的jdbc输出器<br>
 * 设置参数:<br>
 * URL、driver、user、password、sql、sqlParams 、[bufferSize]<br>
 * 不需要设置layout
 * 
 * @author houfeng-win7
 * 
 */
public class PrepareStatementJdbcAppender extends
		org.apache.log4j.AppenderSkeleton implements org.apache.log4j.Appender {

	protected Connection connection = null;

	protected String driverClass;
	protected String databaseURL = "jdbc:mysql://localhost:3306/test";
	protected String databaseUser = "root";
	protected String databasePassword = "xiaodi";

	/**
	 *  预编译sql //insert into tab values (?,?)
	 */
	protected String sqlStatement = "";
 
	/**
	 * sql参数 有序字符串 <br>
	 * 不要有任何多余字符。
	 * 格式范例:%p%m%c%d{yyyy-MM-dd HH:mm:ss}
	 */
	protected String sqlParams = "";
	/**
	 * 存储当前sql参数值
	 */
	protected ArrayList sqlParamValues ;

	protected int bufferSize = 1;
	protected ArrayList buffer;
	protected ArrayList removes;

	/**
	 * sqlParams模式转换器
	 */
	protected PatternConverter patternConverter;


	public PrepareStatementJdbcAppender() {
		this.buffer = new ArrayList(this.bufferSize);
		this.removes = new ArrayList(this.bufferSize);
	}

	public void append(LoggingEvent event) {

		if (!checkEntryConditions())
			return;

		this.buffer.add(event);

		if (this.buffer.size() >= this.bufferSize)
			flushBuffer();
	}

	/**
	 * 检测必需参数是否设置 
	 * @return
	 */
	protected boolean checkEntryConditions() {
		if(this.driverClass == null){
			this.errorHandler.error("No 'driver' set for the appender named [" + name + "]");
			return false;
		}
		if(this.databaseURL == null){
			this.errorHandler.error("No 'URL' set for the appender named [" + name + "]");
			return false;
		}
		if(this.databaseUser == null){
			this.errorHandler.error("No 'user' set for the appender named [" + name + "]");
			return false;
		}
		if(this.databasePassword == null){
			this.errorHandler.error("No 'password' set for the appender named [" + name + "]");
			return false;
		}
		if(this.sqlStatement == null){
			this.errorHandler.error("No 'sql' set for the appender named [" + name + "]");
			return false;
		}
		if (this.sqlParams == null || this.patternConverter == null) {
			this.errorHandler.error("No 'sqlParams' set for the appender named [" + name + "]");
			return false;
		}

		return true;
	}

	protected void execute()  {
		String sql = this.sqlStatement; 
		ArrayList list = this.sqlParamValues;
		Connection con = null;
		PreparedStatement stmt = null;
		
		try {
			try {
				con = getConnection(); 
				stmt = con.prepareStatement(sql);
				for(int i=0;i<list.size();i++){
					stmt.setString(i+1, list.get(i).toString());
				} 
				stmt.executeUpdate(); 
			} finally{
				stmt.close();
				closeConnection(con); 
			}
		} catch (SQLException e) {
			errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE);
		} 
	}

	protected void closeConnection(Connection con) {
		// 不立即关闭,保持jdbc连接而不立即释放,保证执行效率,在log销毁时通过close方法释放。
		// 该方法在数据源连接的时候可增加关闭连接的代码。
	}

	protected Connection getConnection() throws SQLException {
		if (this.connection == null||this.connection.isClosed()) {
			if (!(DriverManager.getDrivers().hasMoreElements())) {
				setDriver("sun.jdbc.odbc.JdbcOdbcDriver");
			}
			
			this.connection = DriverManager.getConnection(this.databaseURL,
					this.databaseUser, this.databasePassword);
		}

		return this.connection;
	}

	public void close() {
		flushBuffer();
		try {
			if ((this.connection != null) && (!(this.connection.isClosed())))
				this.connection.close();
		} catch (SQLException e) {
			this.errorHandler.error("Error closing connection", e, 0);
		}
		this.closed = true;
	}

	public void flushBuffer() {
		this.removes.ensureCapacity(this.buffer.size());
		for (Iterator i = this.buffer.iterator(); i.hasNext();) {
			 
				LoggingEvent logEvent = (LoggingEvent) i.next();
				StringBuffer tempBuf = new StringBuffer();
				 
				for(PatternConverter c = this.patternConverter;c!=null;c=c.next){
					c.format(tempBuf, logEvent);
					this.sqlParamValues.add(tempBuf.toString());
					tempBuf.setLength(0);
				} 
				execute();
				this.sqlParamValues.clear();
				
				this.removes.add(logEvent);  
		}

		this.buffer.removeAll(this.removes); 
		this.removes.clear();
	}

	public boolean requiresLayout() {
		return false;// 不需要Layout
	}

	public void setSql(String s) {
		 
		this.sqlStatement = s;
		//if (super.getLayout() == null) super.setLayout(new PatternLayout(s));
		//else ((PatternLayout) super.getLayout()).setConversionPattern(s);
	}

	public String getSql() {
		return this.sqlStatement;
	}

	public void setSqlParams(String s) {
	 
		this.sqlParams = s.trim();
		this.patternConverter = new PatternParser(this.sqlParams).parse();
		this.sqlParamValues = new ArrayList();
	}

	public String getSqlParams() {
		return this.sqlParams;
	}

	public void setUser(String user) {
		this.databaseUser = user;
	}

	public void setURL(String url) {
		this.databaseURL = url;
	}

	public void setPassword(String password) {
		this.databasePassword = password;
	}

	public void setBufferSize(int newBufferSize) {
		this.bufferSize = newBufferSize;
		this.buffer.ensureCapacity(this.bufferSize);
		this.removes.ensureCapacity(this.bufferSize);
	}

	public String getUser() {
		return this.databaseUser;
	}

	public String getURL() {
		return this.databaseURL;
	}

	public String getPassword() {
		return this.databasePassword;
	}

	public int getBufferSize() {
		return this.bufferSize;
	}

	public void setDriver(String driverClass) {
		this.driverClass = driverClass;
		try {
			Class.forName(this.driverClass);
		} catch (Exception e) {
			this.errorHandler.error("Failed to load driver", e, 0);
		}
	}
}
 

JndiDataSourceAppender类定义:

 

/**
 * 使用JNDI数据源的 输出器<br>
 * 		由于JNDI数据源在log4j初始化前已经可用,所以可以通过配置的方式实现;
 * 或者在log4j中定义一个独立于程序和容器的数据源,也可以在配置中实现,但这样不利于数据源的统一维护。<br>
 * 相比,通过程序生成的数据源(包括spring配置的数据源)在log4j初始化的时候很可能还未创建,所以不能通过配置的方式定义这类
 * 数据源输出器,而只能在程序中创建Appender。<br>
 * 需要配置的参数:<br>
 * jndiName、 sql、sqlParams 、[bufferSize]<br>
 * @author houfeng-win7
 *
 */
public class JndiDataSourceAppender extends PrepareStatementJdbcAppender {
 
	protected DataSource ds = null;
	protected String jndiName = "java:comp/env/jdbc/mysql_log";
	
	public JndiDataSourceAppender(){  }
	
	public void setJndiName(String name){
		this.jndiName = name;
	}
	
	@Override
	protected boolean checkEntryConditions() { 
		
		if(this.jndiName == null){
			this.errorHandler.error("No 'jndiName' set for the appender named [" + name + "]");
			return  false;
		}
		if(this.sqlStatement == null){
			this.errorHandler.error("No 'sql' set for the appender named [" + name + "]");
			return false;
		}
		if (this.sqlParams == null || this.patternConverter == null) {
			this.errorHandler.error("No 'sqlParams' set for the appender named [" + name + "]");
			return false;
		}
		return true;
	}
	
	protected synchronized void init(){
		if(ds!=null) return;
		try {
			InitialContext context = new InitialContext();
			ds = (DataSource)context.lookup(jndiName); 
		} catch (NamingException e) {
			this.errorHandler.error(e.getMessage());
		}
	}
	
	@Override
	protected Connection getConnection() throws SQLException {
		while(ds==null) init();
		
		Connection conn = ds.getConnection();
		conn.setAutoCommit(true);
		return conn;
	} 
	
	@Override
	protected void closeConnection(Connection conn) {
		try {
			if(conn!=null&&!conn.isClosed())
				conn.close();
		} catch (SQLException e) {
			this.errorHandler.error(e.getMessage());
		}
	}
	
	
}
 

 

配置文件:

 

## -----自定义 logger2 prepareSqlDbLog -------------##
log4j.logger.prepareSqlDbLog=ERROR,prepareStatementDbLogAppender
log4j.appender.prepareStatementDbLogAppender=cn.log4j.datasource.PrepareStatementJdbcAppender
log4j.appender.prepareStatementDbLogAppender.URL=jdbc:mysql://localhost:3306/test
log4j.appender.prepareStatementDbLogAppender.driver=com.mysql.jdbc.Driver
log4j.appender.prepareStatementDbLogAppender.user=root
log4j.appender.prepareStatementDbLogAppender.password=xiaodi
log4j.appender.prepareStatementDbLogAppender.sql=insert into t_log4j_info(priority,message,catalog,time) values(?,?,?,?)
log4j.appender.prepareStatementDbLogAppender.sqlParams=%p%m%c%d{yyyy-MM-dd HH:mm:ss}
log4j.appender.prepareStatementDbLogAppender.bufferSize=10


##--------------db  ------------------##
log4j.appender.db=cn.log4j.datasource.JndiDataSourceAppender
log4j.appender.db.jndiName=java:comp/env/jdbc/mysql_log
log4j.appender.db.sql=insert into t_log4j_info(priority,message,catalog,time) values(?,?,?,?)
log4j.appender.db.sqlParams=%p%m%c%d{yyyy-MM-dd HH:mm:ss}
log4j.appender.db.bufferSize=5
 

 

 

另外,通过分析源码了解下log4j的JdbcAppender是如何保证线程安全的:

 

JdbcAppender继承AppenderSkeleton抽象基类,AppenderSkeleton通过synchronized的doAppend方法调用抽象的append方法,所以子类JdbcAppender实现的append方法也会是线程顺序进入,

从而保证了append中调用的日志缓冲区(ArrayList)及数据库连接对象没有线程并发访问问题。

同时,在log4j运行期间,同一名称的logger存在唯一实例;声明的同一名称的appender也存在唯一实例。 

 

 

 

补充:发现一个更强大的 JdbcAppender :http://www.mannhaupt.com/danko/projects/index.html  

 

 

1
0
分享到:
评论

相关推荐

    log4j日志写入数据库实例

    在IT行业中,日志管理是系统监控和故障排查的关键环节,而Log4j作为Java领域广泛应用的日志框架,因其灵活性和高效性深受开发者喜爱。本文将深入探讨如何使用Log4j将日志信息写入数据库,以实现更高级别的日志管理和...

    日志框架 log4j

    日志框架 Log4j 是 Apache 提供的一个开源日志组件,广泛应用于 Java 开发中。它提供了灵活的日志记录功能,包括日志级别的控制、多种输出目的地以及日志格式的定制。Log4j 主要由三个核心组成部分构成:日志记录器...

    log4j输出日志到数据库表中

    **标题解析:** "log4j输出日志到数据库表中" 指的是使用Log4j这个流行的Java日志框架,将日志记录存储在数据库的特定表中,而不是默认的文本文件或控制台。这通常是出于日志管理、分析和长期存储的需求。 **描述...

    log4j使用与java中log4j记录日志如何写入数据库

    1. **配置文件**:Log4j的配置文件通常是`log4j.properties`或`log4j.xml`,它定义了日志的级别(如DEBUG、INFO、WARN、ERROR、FATAL)、输出目的地(控制台、文件、数据库等)以及布局格式。 2. **日志类**:在...

    Log4j 日志信息存储到数据库中

    Apache Log4j 是一个流行的 Java 日志框架,它提供了灵活的日志记录机制,并支持将日志信息存储到不同的目的地,如控制台、文件或数据库等。 本文主要介绍如何使用 Log4j 将日志信息存储到 MySQL 数据库中。通过...

    日志框架log4j和slf4j入门教程(私塾在线)视频配套学习资料

    ### 日志框架log4j和slf4j入门教程知识点详解 #### 一、Log4j简介 **Log4j** 是Apache组织下的一个开源项目,主要用于Java应用程序的日志记录功能。通过Log4j,开发者可以灵活地控制日志信息的输出格式、输出目的...

    log4j日志驱动包

    log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver log4j.appender.DATABASE....

    log4j使用笔记

    通过上述详细介绍,我们可以看出 log4j 不仅提供了强大的日志管理功能,而且具有很高的灵活性和扩展性,能够满足各种复杂应用场景下的需求。对于开发者来说,掌握 log4j 的基本配置和使用方法是非常重要的。

    log4j与log4j.properties的配置.doc

    这个配置文件定义了 Log4j 的根记录器(rootLogger),包括日志输出级别、输出目的地和日志格式。 三、日志输出级别 Log4j 提供了五个日志输出级别: 1. DEBUG:调试信息 2. INFO:一般信息 3. WARN:警告信息 4....

    Java进阶课程日志框架视频教程

    -日志课程大纲.xlsx ...--16-Log4j的JDBCAppender配置.mp4 --17-Log4j的自定义logger.mp4 --18-JCL入门介绍.mp4 --19-JCL原理介绍.mp4 -- ---讲义 -日志技术(上).pdf - --day02 -day02资料.zip -

    使用log4j 记录日志到数据库

    Log4j是一款广泛使用的Java日志框架,它允许开发者灵活地控制日志信息的输出。这篇博客“使用log4j记录日志到数据库”将介绍如何配置和使用Log4j,以便将日志信息存储到数据库中,而非传统的文本文件。数据库存储的...

    如何借助log4j把日志写入数据库中

    Log4j是Apache提供的一款强大的、灵活的日志框架,广泛应用于各种Java应用程序。本文将深入讲解如何利用Log4j将日志信息存储到数据库中。 首先,我们需要理解Log4j的基本架构。Log4j由三个主要组件构成:Logger...

    Log4j写入数据库详解

    Log4j是一个流行的开源日志框架,由Apache Software Foundation开发维护。它提供了一种灵活的方式来控制日志的生成,使得日志的级别、格式以及输出目的地都可以在运行时进行配置。这不仅方便了开发人员对程序运行...

    log4j添加日志到数据库和文件中

    Log4j的核心优势在于其可配置性,可以根据不同的需求将日志输出到控制台、文件、数据库甚至网络上。 在"log4j添加日志到数据库和文件中"的场景中,我们首先需要理解Log4j的基本架构。它主要由三个组件构成:Logger...

    深入学习log4J

    《深入学习log4J》是一本专注于Java日志框架Log4J的专业书籍,旨在帮助开发者深入理解并熟练运用Log4J进行系统日志管理和分析。Log4J是Apache组织开发的一个开源日志记录工具,广泛应用于Java应用程序中,提供灵活的...

    log4j日志.rar

    Log4j的核心理念是提供一种灵活且可扩展的日志机制,允许开发者根据需要调整日志级别,控制日志输出的详细程度,从而实现对调试信息、警告信息和错误信息的精细化管理。Log4j由三个主要组件构成:Logger(日志器)、...

    日志配置到文件,数据库

    - `log4j.rootLogger=INFO,CONSOLE`:此行设置了根日志记录器的默认日志级别为`INFO`,并且指定了输出到控制台(CONSOLE)。 - `log4j.addivity.org.apache=true`:这里应该是`log4ity.additivity.org.apache=true...

    log4j写入数据库配置

    在这个文件中,我们定义了一个名为`JDBC`的日志输出器,它使用`org.apache.log4j.jdbc.JDBCAppender`来实现数据库记录。以下是一个示例配置: ```properties log4j.rootLogger=ERROR, stdout, JDBC # 设置日志追加...

    nlog4j-1.2.18

    NLog4j是一个广泛使用的日志记录框架,尤其在Java应用程序中。它的全名是Apache Log4j,是由Apache软件基金会开发的开源项目。NLog4j 1.2.18是该框架的一个特定版本,它包含了稳定性和兼容性的改进,以及可能的安全...

Global site tag (gtag.js) - Google Analytics