log4j-database appender

11059 ワード

1.log 4 j構成
<appender name="DATABASE" class="com.sf.core.log4j.DataSourceAppender">
		<filter class="org.apache.log4j.varia.LevelRangeFilter" >
			<param name="levelMin" value="error"/>
		</filter>
	</appender>
    <root>
        <level value="INFO"/>
        <appender-ref ref="CONSOLE"/>
		<appender-ref ref="DATABASE"/>      
    </root>

 2.自分でAppenderクラスを定義し、ここではspringが提供するJdbcTemplateを使用してログを一括挿入する.
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sf.core.log4j;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;

import org.apache.log4j.Level;
import org.apache.log4j.MDC;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;

import com.sf.core.util.AppUtil;
import com.sf.core.util.UniqueIdUtil;


public class DataSourceAppender extends org.apache.log4j.AppenderSkeleton
		implements org.apache.log4j.Appender {

	


	protected String sqlStatement = "";

	/**
	 * size of LoggingEvent buffer before writting to the database. Default is
	 * 1.
	 */
	protected int bufferSize = 1;
	
	protected int maxBuffSize = 10000;

	/**
	 * ArrayList holding the buffer of Logging Events.
	 */
	protected ArrayList buffer;

	/**
	 * Helper object for clearing out the buffer
	 */
	protected ArrayList removes;

	private boolean locationInfo = false;

	public DataSourceAppender() {
		super();
		buffer = new ArrayList(bufferSize);
		removes = new ArrayList(bufferSize);
	}

	/**
	 * Gets whether the location of the logging request call should be captured.
	 * 
	 * @since 1.2.16
	 * @return the current value of the <b>LocationInfo</b> option.
	 */
	public boolean getLocationInfo() {
		return locationInfo;
	}

	/**
	 * The <b>LocationInfo</b> option takes a boolean value. By default, it is
	 * set to false which means there will be no effort to extract the location
	 * information related to the event. As a result, the event that will be
	 * ultimately logged will likely to contain the wrong location information
	 * (if present in the log format).
	 * <p/>
	 * <p/>
	 * Location information extraction is comparatively very slow and should be
	 * avoided unless performance is not a concern.
	 * </p>
	 * 
	 * @since 1.2.16
	 * @param flag
	 *            true if location information should be extracted.
	 */
	public void setLocationInfo(final boolean flag) {
		locationInfo = flag;
	}

	/**
	 * Adds the event to the buffer. When full the buffer is flushed.
	 */
	public void append(LoggingEvent event) {
		event.getNDC();
		event.getThreadName();
		// Get a copy of this thread's MDC.
		event.getMDCCopy();
		

		if (locationInfo) {
			event.getLocationInformation();
		}
		event.getRenderedMessage();
		event.getThrowableStrRep();
		buffer.add(event);

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

	/**
	 * By default getLogStatement sends the event to the required Layout object.
	 * The layout will format the given pattern into a workable SQL string.
	 * 
	 * Overriding this provides direct access to the LoggingEvent when
	 * constructing the logging statement.
	 * 
	 */
	protected String getLogStatement(LoggingEvent event) {
		String statement =  getLayout().format(event);
		return statement;
	}

	/**
	 * Closes the appender, flushing the buffer first then closing the default
	 * connection if it is open.
	 */
	public void close() {
		flushBuffer();

		this.closed = true;
	}

	/**
	 * loops through the buffer of LoggingEvents, gets a sql string from
	 * getLogStatement() and sends it to execute(). Errors are sent to the
	 * errorHandler.
	 * 
	 * If a statement fails the LoggingEvent stays in the buffer!
	 */
	@SuppressWarnings("unchecked")
	public void flushBuffer() {
		removes.ensureCapacity(buffer.size());
		try {
			if (AppUtil.getContext() != null && AppUtil.getServletContext()!=null) {
				removes.addAll(buffer);
				JdbcTemplate jdbcTemplate = (JdbcTemplate) AppUtil.getBean("jdbcTemplateLog4j");
				try{
					String sql = "INSERT INTO SYS_LOG4J_MSG("+ 
					"ID_,"+
					"CLAZZ," +
					"PRIORITY," +
					"MESSAGE," +
					"LOG_DATE," +
					"USER_ID," +
					"USER_NAME," +
					"USER_ACCOUNT) "
				+"values(?," +
						"?," +
						"?," +
						"?," +
						"?," +
						"?," +
						"?," +
						"?)"; 
					jdbcTemplate.batchUpdate(sql,new BatchPreparedStatementSetter() {
						@Override
						public void setValues(PreparedStatement ps, int i) throws SQLException {
							LoggingEvent logEvent = (LoggingEvent) buffer.get(i);
							String clazz = logEvent.getLocationInformation().getClassName();
							String level = getLogLevelH(logEvent.getLevel());
							String message = logEvent.getMessage().toString();
							Timestamp time = new Timestamp(logEvent.getTimeStamp());
							Long userId = (Long) MDC.get("current_user_id");
							String userName = (String) MDC.get("current_user_name");
							String userAccount = (String) MDC.get("current_user_account");
							ps.setLong(1, UniqueIdUtil.genId());
							ps.setString(2, clazz);
							ps.setString(3, level);
							ps.setString(4, message);
							ps.setTimestamp(5,time);
							ps.setLong(6, userId==null?0L:userId);
							ps.setString(7, userName);
							ps.setString(8, userAccount);
						}
						@Override
						public int getBatchSize() {
							return buffer.size();
						}
					});
				}catch (Exception e) {
					e.printStackTrace();
				}
			}else{
				if(buffer.size()>maxBuffSize){
					removes.addAll(buffer.subList(0, maxBuffSize/10));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		buffer.removeAll(removes);
		removes.clear();
	}

	
	private String getLogLevelH(Level l){
		String ls="";
		
		if(l.toInt()==Level.ALL.toInt()){
			ls="ALL";
		}else if(l.toInt()==Level.DEBUG.toInt()){
			ls="DEBUG";
		}else if(l.toInt()==Level.ERROR.toInt()){
			ls="ERROR";
		}else if(l.toInt()==Level.FATAL.toInt()){
			ls="FATAL";
		}else if(l.toInt()==Level.INFO.toInt()){
			ls="INFO";
		}else if(l.toInt()==Level.OFF.toInt()){
			ls="OFF";
		}else if(l.toInt()==Level.TRACE.toInt()){
			ls="TRACE";
		}else if(l.toInt()==Level.WARN.toInt()){
			ls="WARN";
		}
		return ls;
	}
	
	
	/** closes the appender before disposal */
	public void finalize() {
		close();
	}

	/**
	 * JDBCAppender requires a layout.
	 * */
	public boolean requiresLayout() {
		return true;
	}

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

	/**
	 * Returns pre-formated statement eg: insert into LogTable (msg) values
	 * ("%m")
	 */
	public String getSql() {
		return sqlStatement;
	}


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


	public int getBufferSize() {
		return bufferSize;
	}

}

 3.データテーブルの作成
-- Create table
create table SYS_LOG4J_MSG
(
  CLAZZ        VARCHAR2(512),
  PRIORITY     VARCHAR2(64),
  LOG_DATE     TIMESTAMP(6),
  MESSAGE      VARCHAR2(2000),
  USER_ID      NUMBER(18),
  USER_NAME    VARCHAR2(256),
  USER_ACCOUNT VARCHAR2(256),
  ID_          NUMBER(18) not null
);
-- Add comments to the table 
comment on table SYS_LOG4J_MSG
  is 'log4j ';
-- Add comments to the columns 
comment on column SYS_LOG4J_MSG.CLAZZ
  is ' ';
comment on column SYS_LOG4J_MSG.PRIORITY
  is ' ';
comment on column SYS_LOG4J_MSG.LOG_DATE
  is ' ';
comment on column SYS_LOG4J_MSG.MESSAGE
  is ' ';
comment on column SYS_LOG4J_MSG.USER_ID
  is ' ID';
comment on column SYS_LOG4J_MSG.USER_NAME
  is ' ';
comment on column SYS_LOG4J_MSG.USER_ACCOUNT
  is ' ';
comment on column SYS_LOG4J_MSG.ID_
  is 'ID';
-- Create/Recreate primary, unique and foreign key constraints 
alter table SYS_LOG4J_MSG
  add constraint PK_LOG4J_MSG primary key (ID_);