`
mengqingyu
  • 浏览: 333762 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

AOP+LOG4J日志框架(自定义注解)

阅读更多
工作中用到日志功能,参考网上一些资料,写了个比较通用的日志框架,现在拿出来分享,内容没有做太多解释,如有不清楚的地方可以给我留言或是通过网上查资料来解决。
设计思路:通过AOP进入方法之前拦截做行为日志记录,方法抛异常拦截做错误日志记录。实现自定义注解,可以存入行为中文注释,也可以配置成完全根据注解来决定是否记录日志的策略,支持控制台、文件、数据库、邮件和异步处理等功能以及针对数据库可以添加行为统计、异常统计和钻取明细等等。
web.xml文件配置如下:
<!-- 日志 -->
<context-param>
	<param-name>log4jConfigLocation</param-name>
	<param-value>classpath:log4j.xml</param-value>
</context-param>
<!-- 监听器 放在spring监听器之前-->
<listener>
	<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>


spring配置文件如下:
<!-- 日志  AOP-->	
<bean id="logBeforeAdvice" class="com.berheley.bi.system.log.logManage.LogBeforeAdvice">
	<property name="annotation" value="true"/>
</bean>
<bean id="logThrowingAdvice" class="com.berheley.bi.system.log.logManage.LogThrowingAdvice">
	<property name="annotation" value="true"/>
</bean>
<!-- 日志  begin-->
<aop:config> 
	<aop:advisor pointcut="execution(* com.berheley.bi.meta..*.*(..))"   advice-ref="logBeforeAdvice"/>  
</aop:config>
<!-- 日志 end -->

import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodBeforeAdvice;

/**
 * 
 * 类功能描述:日志框架
 *
 * @author <a href="mailto:qingyu.meng21@gmail.com">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2013-4-9 下午05:23:41
 */
public class LogBeforeAdvice implements MethodBeforeAdvice {
	
	protected Log log = LogFactory.getLog(this.getClass());
	
	private boolean annotation = true;
	
	public void setAnnotation(boolean annotation) {
		this.annotation = annotation;
	}

	/**
	 * service方法前调用,记录行为或调试
	 */
	public void before(Method method, Object[] args, Object target) throws Throwable {
        LogUtils.beforeLog(target.getClass(), method, args, log, annotation);
	}
}


import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.ThrowsAdvice;

import com.berheley.bi.basic.exp.BusinessException;

/**
 * 
 * 类功能描述:日志框架
 *
 * @author <a href="mailto:qingyu.meng21@gmail.com">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2013-4-9 下午05:23:41
 */
public class LogThrowingAdvice implements ThrowsAdvice {
	
	protected Log log = LogFactory.getLog(this.getClass());
	
	private boolean annotation = true;
	
	public void setAnnotation(boolean annotation) {
		this.annotation = annotation;
	}
	
	/**
	 * 
	 * @function:抛出异常时调用,记录错误内容
	 * @param method
	 * @param args
	 * @param target
	 * @param ex
	 * @throws BusinessException
	 * @author: mengqingyu    2013-4-9 下午05:24:20
	 */
	public void afterThrowing(Method method, Object[] args, Object target,  
			Exception ex) throws BusinessException {
//		DOMConfigurator.configure(LogThrowingAdvice.class.getResource("/log4j.xml"));//临时测试用
		LogUtils.throwingLog(target.getClass(), method, args, ex, log, annotation);
    }
}

log4j配置文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> 
<!-- log4j xml配置 控制台、文件、数据库和邮件 支持异步  mengqingyu -->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
	<!-- 控制台 -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="target" value="System.out"/>
		<layout class="org.apache.log4j.PatternLayout">
			<param name="conversionPattern" value="[%-5p] %d{yyyy-MM-dd HH:mm:ss} %m %n"/>
		</layout>
	</appender>
	
	<!-- 文件 -->
	<appender name="webframe" class="org.apache.log4j.DailyRollingFileAppender">
		<param name="file" value="${webframe.root}/WEB-INF/logs/webframe.log"/>
		<!-- 8K为一个写单元
		<param name="BufferedIO" value="true" />
        <param name="BufferSize" value="8192" />
         -->
		<param name="append" value="true"/>
		<param name="datePattern" value="'.'yyyy-MM-dd"/>
		<layout class="org.apache.log4j.PatternLayout">
			<param name="conversionPattern" value="[%-5p] %d{yyyy-MM-dd HH:mm:ss} %m %n"/>
		</layout>
	</appender>	
	
	<!-- 数据库 -->
	<appender name="db" class="com.berheley.bi.system.log.log4j.JDBCCustomAppender">
		<param name="bufferSize" value="1" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="conversionPattern"
				value="insert into t_log (LOG_NAME_,USER_NAME_,CLASS_,METHOD_NAME_,CREATE_TIME_,LOG_LEVEL_,METHOD_PARAM_,MSG_BRIEF_,MSG_DETAIL_,CLASS_DISCRIPTION_,METHOD_DISCRIPTION_) values ('%X{LOG_NAME_}','%X{USER_NAME_}','%X{CLASS_}','%X{METHOD_NAME_}','%d{yyyy-MM-dd HH:mm:ss}','%p','%X{METHOD_PARAM_}','%m','%X{MSG_DETAIL_}','%X{CLASS_DISCRIPTION_}','%X{METHOD_DISCRIPTION_}')" />
		</layout>
		<!-- 级别过滤
		<filter class="org.apache.log4j.varia.LevelRangeFilter"> 
			<param name="LevelMax" value="error" /> 
			<param name="LevelMin" value="error" /> 
		</filter> 
		-->
	</appender>
	
	<!-- 邮件 可配置多个appender模块划分-->
	<appender name="mail" class="com.berheley.bi.system.log.log4j.SMTPCustomAppender">
		<param name="threshold" value="fatal"/>
		<param name="bufferSize" value="1"/>
		<param name="from" value="1@gmail.com"/>
		<param name="smtpHost" value="smtp.gmail.com"/>
		<param name="smtpAuth" value="true"/>
		<param name="smtpUsername" value="1@gmail.com"/>
		<param name="smtpPassword" value="1"/>
		<param name="subject" value=" Log4J Message"/>
		<param name="packages" value="com.berheley.bi.fillform,com.berheley.bi.system"/>  <!-- 可以配置多个以,号间隔 -->		
		<param name="to" value="1@gmail.com"/> <!-- 可以配置多个以,号间隔 -->		
		<layout class="com.berheley.bi.system.log.log4j.HTMLCustomLayout"/>
	</appender>	
	
	<!-- 多线程 -->		
	<appender name="async" class="org.apache.log4j.AsyncAppender">
		<param name="bufferSize" value="100" />
		<appender-ref ref="db" />
		<appender-ref ref="mail" />
	</appender>
	
	<!-- 继承root log4j包下运行异步线程日志 -->
	<logger name="com.berheley.bi.system.log"> 
		<level value="info"/>
		<appender-ref ref="async"/> 
	</logger>
	
	<!-- 输出级别 -->	
	<root>
		<priority value="error" />
		<appender-ref ref="console" />
		<appender-ref ref="webframe" />
	</root>
	
	<!-- 单个日志级别控制
	<category name="monitorLogger" additivity="false">
        <priority value="info" />
        <appender-ref ref="console"/>
    </category >
    -->
</log4j:configuration>


import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.log4j.MDC;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.webframe.web.springmvc.exp.AjaxException;

import com.berheley.bi.basic.exp.BusinessException;
import com.berheley.bi.system.login.LoginUser;

/**
 * 
 * 类功能描述: 日志工具类,设置属性
 *
 * @author <a href="mailto:qingyu.meng21@gmail.com">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2013-4-12 下午04:55:44
 */
public abstract class LogUtils {
	
	public static final String WEB_CONTEXT_ 			= 		"WEB_CONTEXT_";
	
	public static final String LOG_NAME_ 				= 		"LOG_NAME_";
	
	public static final String USER_NAME_ 				= 		"USER_NAME_";
	
	public static final String CLASS_ 					= 		"CLASS_";
	
	public static final String PACKAGES_ 				= 		"PACKAGES_";
	
	public static final String METHOD_NAME_ 			= 		"METHOD_NAME_";
	
	public static final String METHOD_PARAM_ 			= 		"METHOD_PARAM_";
	
	public static final String CLASS_DISCRIPTION_ 		= 		"CLASS_DISCRIPTION_";
	
	public static final String METHOD_DISCRIPTION_ 		= 		"METHOD_DISCRIPTION_";
	
	public static final String MSG_DETAIL_ 				= 		"MSG_DETAIL_";
	
	/**
	 * 
	 * @function:操作行为记录
	 * @param clazz 类对象
	 * @param method 方法对象
	 * @param args 方法参数
	 * @param log 日志对象
	 * @throws BusinessException
	 * @author: mengqingyu    2013-5-30 下午05:29:57
	 */
	public static void beforeLog(Class<?> clazz, Method method, Object[] args, Log log, boolean annotation) throws BusinessException{
		LogDiscription methodDiscription =  method.getAnnotation(LogDiscription.class);
		if(annotation==false||methodDiscription!=null){
			initLog(clazz, method, args, null, methodDiscription);
			log.info(new StringBuilder("info:").append(LogUtils.getClassName()).append("->").append(LogUtils.getMethodName()));
		}
	}
	
	/**
	 * 
	 * @function:异常记录
	 * @param clazz 类对象
	 * @param method 方法对象
	 * @param args 方法参数
	 * @param ex 异常对象
	 * @param log 日志对象
	 * @throws BusinessException
	 * @author: mengqingyu    2013-5-30 下午05:30:00
	 */
	public static void throwingLog(Class<?> clazz, Method method, Object[] args, Exception ex, Log log, boolean annotation) throws BusinessException{
		LogDiscription methodDiscription =  method.getAnnotation(LogDiscription.class);
		if(annotation==false||methodDiscription!=null){
			initLog(clazz, method, args, ex, methodDiscription);
			if(ex instanceof BusinessException || ex instanceof AjaxException){
				log.error(ex.getMessage(),ex);
			}
			else{
				log.fatal(ex.getMessage(),ex);
			}
		}
	}
	
	/**
	 * 
	 * @function:初始化参数
	 * @param clazz 类对象
	 * @param method 方法对象
	 * @param args 方法参数
	 * @param ex 异常对象
	 * @throws BusinessException
	 * @author: mengqingyu    2013-4-12 下午04:56:09
	 */
	protected static void initLog(Class<?> clazz, Method method, Object[] args, Exception ex, LogDiscription methodDiscription) throws BusinessException{
		if(clazz==null||method==null)
			throw new BusinessException("日志初始化参数clazz或method为空");
		
		setLogName();
		setClass(clazz);
		setMethodName(method);
		setMethodParam(args,methodDiscription);
		setClassDiscription(clazz.getAnnotation(LogDiscription.class));
		setMethodDiscription(methodDiscription);
		setMsgDetail(ex);
	}
	
	/**
	 * 
	 * @function:初始化参数
	 * @param clazz 类对象
	 * @param method 方法名称
	 * @param classTypes 方法参数类型
	 * @param args 方法参数
	 * @param ex 异常对象
	 * @throws BusinessException
	 * @author: mengqingyu    2013-4-12 下午04:56:09
	 */
	@SuppressWarnings("rawtypes")
	public static Method initLog(Class<?> clazz, String methodName, Class[] classTypes) throws BusinessException{
		if(clazz==null||methodName==null)
			throw new BusinessException("日志初始化参数:clazz或method为空");
		
		Method method;
		try {
			method = clazz.getMethod(methodName, classTypes);
		} catch (SecurityException e) {
			throw new BusinessException("方法权限错误"+methodName,e);
		} catch (NoSuchMethodException e) {
			throw new BusinessException("方法名或参数类型错误:"+methodName,e);
		}
		return method;
	}
	
	private static void setLogName(){
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		String Username;
		String RealName;
		if(auth!=null){
			LoginUser loginUser = (LoginUser)auth.getPrincipal();
			Username = loginUser.getUsername();
			RealName = loginUser.getRealName();
		}
		else{
			Username = Thread.currentThread().getName();
			RealName = "无";
		}
		MDC.put(LogUtils.LOG_NAME_, Username);
		MDC.put(LogUtils.USER_NAME_, RealName);
	}
	
	private static void setClass(Class<?> clazz){
		MDC.put(LogUtils.CLASS_, clazz.getName().substring(clazz.getName().lastIndexOf(".")+1));
		MDC.put(LogUtils.PACKAGES_, clazz.getName());
	}
	
	private static void setMethodName(Method method){
		MDC.put(LogUtils.METHOD_NAME_, method.getName());
	}
	
	private static void setMethodParam(Object[] args, LogDiscription logDiscription){
		if(args!=null){
			StringBuilder methodParam = new StringBuilder();
			String param = logDiscription==null?"":logDiscription.param();
			if("".equals(param)){
		        for(Object o:args){  
		        	methodParam.append(o).append("<br/>");
		        }  
			}
			else{
				String[] paramArray = param.split(",");
				int paramLength = paramArray.length;
				if(paramLength != args.length){
					methodParam.append("参数注释错误");
				}
				else{
					for(int i=0;i<args.length;i++){
						methodParam.append(paramArray[i]).append(":").append(args[i]).append("<br/>");
					}
				}
			}
			MDC.put(LogUtils.METHOD_PARAM_, replaceParam(methodParam.toString()));
		}
	}
	
	private static void setClassDiscription(LogDiscription logDiscription){
		MDC.put(LogUtils.CLASS_DISCRIPTION_, replaceParam(logDiscription==null?"":logDiscription.clazz()));
	}
	
	private static void setMethodDiscription(LogDiscription logDiscription){
		MDC.put(LogUtils.METHOD_DISCRIPTION_, replaceParam(logDiscription==null?"":logDiscription.method()));
	}
	
	/**
	 * 
	 * @function:设置异常信息
	 * @param ex 异常对象
	 * @author: mengqingyu    2013-4-12 下午04:56:53
	 */
	public static void setMsgDetail(Exception ex){
		if(ex!=null){
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			ex.printStackTrace(pw);
			MDC.put(LogUtils.MSG_DETAIL_, sw);
		}
	}
	
	/**
	 * 
	 * @function:获取类名称
	 * @return
	 * @author: mengqingyu    2013-4-12 下午04:57:13
	 */
	public static String getClassName(){
		return MDC.get(LogUtils.CLASS_)==null?"null":MDC.get(LogUtils.CLASS_).toString();
	}
	
	/**
	 * 
	 * @function:获取方法名称
	 * @return
	 * @author: mengqingyu    2013-4-12 下午04:57:30
	 */
	public static String getMethodName(){
		return MDC.get(LogUtils.METHOD_NAME_)==null?"null":MDC.get(LogUtils.METHOD_NAME_).toString();
	}
	
	private static String replaceParam(String value){
		return value.replace(",", "\\,").replace("'", "\\'");
	}
}

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * 
 * 类功能描述:自定义日志注解
 *
 * @author <a href="mailto:qingyu.meng21@gmail.com">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2013-4-12 下午01:57:44
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface LogDiscription {
	/**
	 * 用法:在类名上方加入注解代码 @LogDiscription(clazz="表单分配")
	 * @function:类注释
	 * @return
	 * @author: mengqingyu    2013-4-12 下午01:57:10
	 */
	String clazz() default "";
	
	/**
	 * 用法:在方法名上方加入注解代码 @LogDiscription(method = "立即发布",param = "任务ID:{0} 主键:{1} 创建时间:{2}")
	 * @function:方法注释
	 * @return
	 * @author: mengqingyu    2013-4-12 下午01:57:22
	 */
	String method() default "";
	
	/**
	 * 
	 * @function:方法参数注释
	 * @return
	 * @author: mengqingyu    2013-4-12 下午01:57:30
	 */
	String param() default "";
}

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;

import javax.sql.DataSource;

import org.apache.log4j.jdbc.JDBCAppender;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;
import org.webframe.web.util.WebFrameUtils;

/**
 * 
 * 类功能描述:log4j 入库日志管理,重写此类解决数据源、批量插入和日志过滤等问题
 *
 * @author <a href="mailto:qingyu.meng21@gmail.com">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2013-4-9 下午02:20:08
 */
public class JDBCCustomAppender extends JDBCAppender {

	/**
	 * 类功能描述:数据库连接池,单例,延迟实例化
	 */
	private static class DataSourceHolder {
		private static final DataSource dataSource = (DataSource) WebFrameUtils.getApplicationContext().getBean("dataSource");
	}
	
	/**
	 * 在日志加入集合过程中进行处理
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void append(LoggingEvent event) {
		buffer.add(event);
		if (buffer.size() >= bufferSize)
			flushBuffer();
	}

	/**
	 * 从连接池中获取连接
	 */
	@Override
	protected Connection getConnection() throws SQLException {
		return DataSourceHolder.dataSource.getConnection();
	}

	/**
	 * 删去了源方法中的一些操作
	 */
	@Override
	public void close() {
		this.closed = true;
	}

	/**
	 * 释放连接
	 */
	@Override
	protected void closeConnection(Connection con) {
		try {
			con.close();
		} catch (SQLException e) {
			errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE);
		}
	}

	/**
	 * 重写批量插入操作
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void flushBuffer() {
		Connection con = null;
		Statement stmt = null;
		try {
			con = getConnection();
			stmt = con.createStatement();
			removes.ensureCapacity(buffer.size());
			for (Iterator<LoggingEvent> i = buffer.iterator(); i.hasNext();) {
				LoggingEvent logEvent = i.next();
				stmt.addBatch(getLogStatement(logEvent));
				removes.add(logEvent);
			}
		 	stmt.executeBatch();
		} catch (SQLException e) {
		    errorHandler.error("Failed to excute sql", e,ErrorCode.FLUSH_FAILURE);
		} finally {
			try {
				stmt.close();
			} catch (SQLException e) {
				errorHandler.error("Failed to excute sql", e,ErrorCode.FLUSH_FAILURE);
			} finally {
				closeConnection(con);
			}
		}
		buffer.removeAll(removes);
		removes.clear();
	}
}

import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.net.SMTPAppender;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;

/**
 * 
 * 类功能描述:log4j 邮件日志管理,重写此类解决不支持权限认证、缓存问题和gmail端口号问题,注:新版本log4j已经支持认证
 *
 * @author <a href="mailto:qingyu.meng21@gmail.com">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2013-4-11 下午01:44:19
 */
public class SMTPCustomAppender extends SMTPAppender {

	protected String smtpUsername;

	protected String smtpPassword;

	protected String smtpAuth;

	protected String[] packages;
	
	protected String to;

	protected String from;

	protected String subject;

	protected String smtpHost;

	protected int bufferSize = 512;

	protected boolean locationInfo = false;

	public void setSmtpUsername(String smtpUsername) {
		this.smtpUsername = smtpUsername;
	}

	public void setSmtpPassword(String smtpPassword) {
		this.smtpPassword = smtpPassword;
	}

	public void setSmtpAuth(String smtpAuth) {
		this.smtpAuth = smtpAuth;
	}

	public void setTo(String to) {
		this.to = to;
	}

	public void setPackages(String packages) {
		this.packages = packages.split(",");
	}

	public void setFrom(String from) {
		this.from = from;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}

	public void setSmtpHost(String smtpHost) {
		this.smtpHost = smtpHost;
	}

	public void setBufferSize(int bufferSize) {
		this.bufferSize = bufferSize;
	}

	public void setLocationInfo(boolean locationInfo) {
		this.locationInfo = locationInfo;
	}

	/**
	 * 在日志加入集合过程中进行处理
	 */
	public void append(LoggingEvent event) {
		if (!checkEntryConditions()) {
			return;
		}

		event.getThreadName();
		event.getNDC();
		if (locationInfo) {
			event.getLocationInformation();
		}
		//包过滤
		if(packages!=null&&packages.length>0){
			String clazz = event.getMDC(LogUtils.PACKAGES_).toString();
			for(int i=0;i<packages.length;i++){
				if(clazz.startsWith(packages[i])){
					cb.add(event);
					break;
				}
			}
		}
		else{
			cb.add(event);
		}
		if (evaluator.isTriggeringEvent(event)) {
            if (cb.length() >= bufferSize) { //添加缓存判断 
                sendBuffer();  
            }  
		}
	}

	/**
	 * 初始化邮件对象并进行权限认证
	 */
	public void activateOptions() {
		Properties props = new Properties(System.getProperties());
		if (smtpHost != null) {
			props.put("mail.smtp.host", smtpHost);
			// 邮件默认端口号是25,gmail邮件服务商用是465端口,需要手动处理
			if (smtpHost.indexOf("smtp.gmail.com") >= 0) {
				props.setProperty("mail.smtp.socketFactory.class",
						"javax.net.ssl.SSLSocketFactory");
				props.setProperty("mail.smtp.socketFactory.fallback", "false");
				props.setProperty("mail.smtp.port", "465");
				props.setProperty("mail.smtp.socketFactory.port", "465");
			}
		}

		Authenticator authenticator = null;

		if (smtpAuth != null && smtpAuth.trim().equals("true")) {
			props.put("mail.smtp.auth", "true");
			authenticator = new Authenticator() {
				protected PasswordAuthentication getPasswordAuthentication() {
					return new PasswordAuthentication(smtpUsername,
							smtpPassword);
				}
			};
		}

		Session session = Session.getInstance(props, authenticator);
		msg = new MimeMessage(session);

		try {
			if (from != null)
				msg.setFrom(getAddress(from));// 设置发件人
			else
				msg.setFrom();
			msg.setRecipients(Message.RecipientType.TO, parseAddress(to));// 设置收件人
			if (subject != null){
				String context = WebFrameUtils.getWebContextPath();
				context = StringUtils.isBlank(context)?subject:context.replace("/", "")+" "+subject;
				msg.setSubject(context);
			}
				
		} catch (MessagingException e) {
			LogLog.error("Could not activate SMTPAppender options.", e);
		}
	}

	/**
	 * 
	 * @function:获得地址
	 * @param addressStr
	 * @return
	 * @author: mengqingyu 2013-4-10 下午02:38:30
	 */
	protected InternetAddress getAddress(String addressStr) {
		try {
			return new InternetAddress(addressStr);
		} catch (AddressException e) {
			errorHandler.error("Could not parse address [" + addressStr + "].",
					e, ErrorCode.ADDRESS_PARSE_FAILURE);
			return null;
		}
	}

	/**
	 * 
	 * @function:转换地址
	 * @param addressStr
	 * @return
	 * @author: mengqingyu 2013-4-10 下午02:38:52
	 */
	protected InternetAddress[] parseAddress(String addressStr) {
		try {
			return InternetAddress.parse(addressStr, true);
		} catch (AddressException e) {
			errorHandler.error("Could not parse address [" + addressStr + "].",
					e, ErrorCode.ADDRESS_PARSE_FAILURE);
			return null;
		}
	}
}

import org.apache.log4j.HTMLLayout;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.helpers.Transform;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;

/**
 * 
 * 类功能描述:log4j 日志模板,重写此类解决中文乱码和自定义模板问题
 *
 * @author <a href="mailto:qingyu.meng21@gmail.com">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2013-4-11 下午01:46:43
 */
public class HTMLCustomLayout extends HTMLLayout {

	protected StringBuffer sbuf = new StringBuffer(BUF_SIZE);

	protected static String TRACE_PREFIX = "<br>&nbsp;&nbsp;&nbsp;&nbsp;";

	protected boolean locationInfo = false;

	protected String title = "Log4J Log Messages";

	public void setLocationInfo(boolean flag) {
		locationInfo = flag;
	}

	public boolean getLocationInfo() {
		return locationInfo;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getTitle() {
		return title;
	}

	/**
	 * 格式化HTML头部模板
	 */
	public String getHeader() {
		StringBuffer sbuf = new StringBuffer();
		sbuf.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
				+ Layout.LINE_SEP);
		sbuf.append("<html>" + Layout.LINE_SEP);
		sbuf.append("<head>" + Layout.LINE_SEP);
		sbuf.append("<title>" + title + "</title>" + Layout.LINE_SEP);
		sbuf.append("<style type=\"text/css\">" + Layout.LINE_SEP);
		sbuf.append("<!--" + Layout.LINE_SEP);
		sbuf.append("body, table {font-family: arial,sans-serif; font-size: x-small;}"
				+ Layout.LINE_SEP);
		sbuf.append("th {background: #336699; color: #FFFFFF; text-align: left;}"
				+ Layout.LINE_SEP);
		sbuf.append("-->" + Layout.LINE_SEP);
		sbuf.append("</style>" + Layout.LINE_SEP);
		sbuf.append("</head>" + Layout.LINE_SEP);
		sbuf.append("<body bgcolor=\"#FFFFFF\" topmargin=\"6\" leftmargin=\"6\">"
				+ Layout.LINE_SEP);
		sbuf.append("<hr size=\"1\" noshade>" + Layout.LINE_SEP);
		sbuf.append("Log session start time " + new java.util.Date() + "<br>"
				+ Layout.LINE_SEP);
		sbuf.append("<br>" + Layout.LINE_SEP);
		sbuf.append("<table cellspacing=\"0\" cellpadding=\"4\" border=\"1\" bordercolor=\"#224466\" width=\"100%\">"
				+ Layout.LINE_SEP);
		sbuf.append("<tr>" + Layout.LINE_SEP);
		sbuf.append("<th>登录名</th>" + Layout.LINE_SEP);
		sbuf.append("<th>用户名</th>" + Layout.LINE_SEP);
		sbuf.append("<th>级别</th>" + Layout.LINE_SEP);
		sbuf.append("<th>类名称</th>" + Layout.LINE_SEP);
		if (locationInfo) {
			sbuf.append("<th>File:Line</th>" + Layout.LINE_SEP);
		}
		sbuf.append("<th>方法名称</th>" + Layout.LINE_SEP);
		sbuf.append("</tr>" + Layout.LINE_SEP);
		return sbuf.toString();
	}

	/**
	 * 格式化HTML模板
	 */
	public String format(LoggingEvent event) {
		if (sbuf.capacity() > MAX_CAPACITY) {
			sbuf = new StringBuffer(BUF_SIZE);
		} else {
			sbuf.setLength(0);
		}

		sbuf.append(Layout.LINE_SEP + "<tr>" + Layout.LINE_SEP);

		sbuf.append("<td>");
		sbuf.append(event.getMDC("LOG_NAME_"));
		sbuf.append("</td>" + Layout.LINE_SEP);

		sbuf.append("<td>");
		sbuf.append(event.getMDC("USER_NAME_"));
		sbuf.append("</td>" + Layout.LINE_SEP);

		sbuf.append("<td title=\"Level\">");
		if (event.getLevel().equals(Level.DEBUG)) {
			sbuf.append("<font color=\"#339933\">");
			sbuf.append(event.getLevel());
			sbuf.append("</font>");
		} else if (event.getLevel().isGreaterOrEqual(Level.WARN)) {
			sbuf.append("<font color=\"#993300\"><strong>");
			sbuf.append(event.getLevel());
			sbuf.append("</strong></font>");
		} else {
			sbuf.append(event.getLevel());
		}
		sbuf.append("</td>" + Layout.LINE_SEP);

		sbuf.append("<td>");
		sbuf.append(event.getMDC("CLASS_"));
		sbuf.append("</td>" + Layout.LINE_SEP);

		if (locationInfo) {
			LocationInfo locInfo = event.getLocationInformation();
			sbuf.append("<td>");
			sbuf.append(Transform.escapeTags(locInfo.getFileName()));
			sbuf.append(':');
			sbuf.append(locInfo.getLineNumber());
			sbuf.append("</td>" + Layout.LINE_SEP);
		}

		sbuf.append("<td>");
		sbuf.append(event.getMDC("METHOD_NAME_"));
		sbuf.append("</td>" + Layout.LINE_SEP);
		sbuf.append("</tr>" + Layout.LINE_SEP);

		if (event.getNDC() != null) {
			sbuf.append("<tr><td bgcolor=\"#EEEEEE\" style=\"font-size : xx-small;\" colspan=\"6\" title=\"Nested Diagnostic Context\">");
			sbuf.append("NDC: " + Transform.escapeTags(event.getNDC()));
			sbuf.append("</td></tr>" + Layout.LINE_SEP);
		}

		Object methodParam = event.getMDC("METHOD_PARAM_");
		if(methodParam!=null){
			sbuf.append("<tr><td style=\"font-size : xx-small;\" colspan=\"6\">");
			sbuf.append("方法参数:"+methodParam.toString().replace("\\r\\n", "&nbsp;&nbsp;"));
			sbuf.append("</td></tr>" + Layout.LINE_SEP);
		}
		
		sbuf.append("<tr><td style=\"font-size : xx-small;\" colspan=\"6\">");
		sbuf.append("异常摘要:"+event.getMessage());
		sbuf.append("</td></tr>" + Layout.LINE_SEP);
		
		String[] s = event.getThrowableStrRep();
		if (s != null) {
			sbuf.append("<tr><td bgcolor=\"#993300\" style=\"color:White; font-size : xx-small;\" colspan=\"6\">");
			appendThrowableAsHTML(s, sbuf);
			sbuf.append("</td></tr>" + Layout.LINE_SEP);
		}

		return sbuf.toString();
	}

	/**
	 * 
	 * @function:拼装异常内容
	 * @param s
	 * @param sbuf
	 * @author: mengqingyu    2013-4-11 下午01:49:29
	 */
	protected void appendThrowableAsHTML(String[] s, StringBuffer sbuf) {
		if (s != null) {
			int len = s.length;
			if (len == 0)
				return;
			sbuf.append(Transform.escapeTags(s[0]));
			sbuf.append(Layout.LINE_SEP);
			for (int i = 1; i < len; i++) {
				sbuf.append(TRACE_PREFIX);
				sbuf.append(Transform.escapeTags(s[i]));
				sbuf.append(Layout.LINE_SEP);
			}
		}
	}

	/**
	 * 中文编码
	 */
	public String getContentType() {
		return "text/html;charset=GBK";
	}
}

注:在类或方法上通过注解的方式来进行描述
@LogDiscription(method = "立即发布",param = "任务ID, 主键, 创建时间")
	@Override
	public void insertOrUpdateMysqlDatas(JSONArray datas, String tableName, String pkName){}
分享到:
评论
2 楼 mengqingyu 2013-05-06  
肯定是线程安全的
w2220533 写道
我想问一个问题.
这个记录日志的框架是否是线程安全的.
如果是多个线程的调用.是不是日志输出就不太理想了.
不太懂.请多指教

异步输出,底层用的vector,线程安全的,多线程下会将所有要输出的日志对象都放到单独的一个线程中去同步处理vector中的日志,而不影响主线程返回结果,采用的是生产者消费者并发模式。
1 楼 w2220533 2013-05-03  
我想问一个问题.
这个记录日志的框架是否是线程安全的.
如果是多个线程的调用.是不是日志输出就不太理想了.
不太懂.请多指教

相关推荐

    springmvc log4j2 logback 注解 jackson 日志脱敏实现源码

    `Logback`和`Log4j`都是广泛使用的日志框架,它们允许自定义日志格式和处理策略。 2. **SpringMVC返回报文脱敏**:`SpringMVC`是Spring框架的一个模块,主要用于构建Web应用。在响应报文时,如果包含了敏感信息(如...

    idea 14 ssm 全注解框架+log4j+事物控制+mybatis基础Dao配置

    通过配置log4j.properties或log4j.xml文件,我们可以设置日志级别(DEBUG、INFO、WARN、ERROR等),选择日志输出目的地(控制台、文件、邮件等),并自定义日志格式。 事务控制在数据库操作中至关重要,确保数据的...

    SpringBoot AOP各种注解、自定义注解、鉴权使用案例(免费下载)

    SpringBoot AOP,即面向切面编程,是Spring框架中的一个重要特性,用于实现代码的横切关注点,如日志记录、事务管理、权限验证等。AOP通过使用代理模式,将这些关注点与核心业务逻辑分离,使得代码更加模块化,更...

    spring+hibernate+log4j所需要的jar包

    Log4j包含配置文件,可以自定义日志输出的位置、格式以及滚动策略,便于调试和监控应用状态。 在提供的压缩包中,可能包含了以下关键的JAR文件: 1. Spring的核心库:如`spring-context`、`spring-beans`、`spring...

    自定义注解实现伪动态传参的小demo

    在这个“自定义注解实现伪动态传参的小demo”中,我们将探讨如何创建一个自定义注解,以允许在注解中传递类似于动态参数的数据。 首先,自定义注解的定义通常以`@interface`关键字开始,我们可以定义一些元素(也...

    SpringMVC+Log4j

    1. **配置灵活性**:通过配置文件(如log4j.properties或log4j.xml)可定制日志级别、输出位置、格式等。 2. **Appenders**:负责将日志信息输出到特定目的地,如ConsoleAppender、FileAppender等。 3. **Layouts**...

    ssm框架,aop,mybatis,log4j所需的jar.rar

    Log4j是一个广泛使用的日志记录工具,它允许开发者自定义日志级别、输出格式和目标,如控制台、文件、网络等。在SSM项目中,Log4j可以帮助记录系统运行时的详细信息,便于调试和问题排查。 在给定的"ssm框架,aop,...

    Struts2+Spring+Hibernet+JPA+sitemesh+log4j

    Struts2、Spring、Hibernate、JPA、SiteMesh和Log4j是Java Web开发中非常重要的六个组件,它们各自承担着不同的职责,并且在实际项目中经常被整合使用,形成了所谓的"SSH2"(Struts2 + Spring + Hibernate)框架组合...

    自定义注解annotation及用法

    - AOP(面向切面编程),如Spring框架中的`@Transactional`。 - 编译时代码生成,如Lombok的`@Data`。 - 配置管理,如`@Component`, `@Service`, `@Repository`等Spring组件注解。 - 数据库映射,如Hibernate的`@...

    spring AOP日志框架

    在实际应用中,通常我们会选择日志框架,如Log4j、Logback或Java内置的`java.util.logging`,来更高效地处理日志输出。Spring AOP可以与这些框架无缝集成,通过配置适配器(如`CommonsLoggingAdapter`)将日志输出到...

    log4j和ApplicationContext、ehcache

    Log4j是一款广泛使用的Java日志框架,由Apache软件基金会开发。它允许程序员在应用程序中插入日志语句,以跟踪程序运行时的行为,帮助调试和优化代码。Log4j的核心功能包括定义日志级别(如DEBUG、INFO、WARN、ERROR...

    使用kafka进行自定义注解日志存储,日志存储使用的是influxDB数据库

    例如,我们可以创建一个名为`@LogEvent`的自定义注解,将其应用于关键业务逻辑的方法上,当方法执行时,自动记录相关日志。这通常需要结合AOP(面向切面编程)实现,例如在Spring框架中,可以使用AspectJ来拦截带有...

    SSH的配置、事务管理和Log4j的配置

    对于初学者,可以从理解各个框架的基本概念开始,逐步学习如何配置和整合,以及如何利用Log4j进行日志调试,从而提升开发效率和问题排查能力。通过不断的实践和经验积累,可以更好地应对复杂项目的需求。

    仿springAOP框架

    本篇文章将深入探讨仿Spring AOP框架,通过实例讲解自定义注解、切面和通知等关键概念,为学习AOP的学员提供详尽的参考资料。 首先,让我们了解**自定义注解**在AOP中的作用。自定义注解可以作为元数据,标记在方法...

    Spring aop 记录操作日志 Aspect 源码

    这可以通过集成其他日志框架如Log4j、Logback或SLF4J来实现。 总之,Spring AOP提供了一种优雅的方式来实现记录操作日志的需求,通过自定义Aspect和注解,我们可以灵活地控制哪些方法需要记录日志,以及记录什么样...

    Spring MVC AOP通过注解方式拦截Controller等实现日志管理demo版本2

    "spring-mvc-log4j"这个名字暗示了项目使用了Log4j作为日志库。Log4j提供了一个灵活的配置系统,允许我们定义不同的日志级别(如DEBUG、INFO、WARN),以及控制日志输出的目的地(如控制台、文件、网络等)。 通过...

    SpringBoot使用AOP注解记录操作日志

    在Spring Boot应用中,日志记录是至关重要的,它能够帮助开发者追踪系统行为...在实际开发中,我们还可以考虑使用像Logback或Log4j这样的日志框架,结合Spring Boot的自动配置,来进一步优化日志的输出格式和存储方式。

    Spring Boot之AOP配自定义注解的最佳实践过程

    Spring Boot之AOP配自定义注解的最佳实践过程 一、AOP概述 AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架的大杀器之一。AOP通过将那些反复出现的代码抽取出来,放在一个地方统一处理,从而...

    SpringBoot+AOP日志服务

    Spring Boot支持各种日志框架,如Logback、Log4j2等。它们提供了更高级的功能,如自定义日志格式、日志文件滚动和异步日志记录。在项目中,我们可以通过设置`logging.level`属性来控制不同包的日志级别。 综上所述...

    Spring之AOP在鉴权和日志记录中的应用

    3. **日志框架集成**:Spring AOP可以与各种日志框架(如Log4j、Logback、SLF4J)配合使用,只需配置合适的日志适配器。 4. **动态日志配置**:通过Spring的Profile特性,可以根据环境动态调整日志输出级别。 **五...

Global site tag (gtag.js) - Google Analytics