log4j.properties文件

#根设置,输出级别为DEBUG级别, 输出文件为 ERRORA,stdout,DEBUGA
log4j.rootLogger=DEBUG,ERRORA,stdout,DEBUGA

#过滤掉spring框架下的额外日志
#log4j.category.org.springframework = WARN

#输出到控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c{1}:%L - %m%n

#输出DEBUG信息到指定文件
log4j.appender.DEBUGA=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DEBUGA.layout=org.apache.log4j.PatternLayout
log4j.appender.DEBUGA.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n
log4j.appender.DEBUGA.datePattern=yyyy-MM-dd'.log'
log4j.appender.DEBUGA.Threshold = DEBUG
log4j.appender.DEBUGA.append=true
log4j.appender.DEBUGA.File=d:/log/debug_log.log
log4j.appender.DEBUGA.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.DEBUGA.filter.F1.LevelMin=DEBUG
log4j.appender.DEBUGA.filter.F1.LevelMax=INFO

#输出error到指定文件
log4j.appender.ERRORA=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ERRORA.layout=org.apache.log4j.PatternLayout
log4j.appender.ERRORA.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n
log4j.appender.ERRORA.datePattern=yyyy-MM-dd'.log'
log4j.appender.ERRORA.Threshold = ERROR
log4j.appender.ERRORA.append=true
log4j.appender.ERRORA.File=d:/errorlog/error_log.log

#打印sql语句
log4j.logger.com.ibatis=DEBUG
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

为了对每个请求的进行日志输出,我们需要配置拦截器

<!-- 拦截器配置,拦截顺序:先执行后定义的,排在第一位的最后执行。-->
<mvc:interceptors>
    <!-- API访问日志记录拦截器 -->
    <mvc:interceptor>
        <mvc:mapping path="/user/**" />
        <bean class="com.utils.ApiLogInterceptor" />
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/childs/**" />
        <bean class="com.utils.ApiLogInterceptor" />
    </mvc:interceptor>

</mvc:interceptors>

开发自定义的拦截器,实现 HandlerInterceptor 接口

public class ApiLogInterceptor  implements HandlerInterceptor {

   private static final Logger logger = org.apache.log4j.Logger.getLogger(ApiLogInterceptor.class);

   private static final ThreadLocal<Long> startTimeThreadLocal =
         new NamedThreadLocal<Long>("ThreadLocal StartTime");
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
      if (logger.isDebugEnabled()){
         long beginTime = System.currentTimeMillis();//1、开始时间
           startTimeThreadLocal.set(beginTime);      //线程绑定变量(该数据只有当前请求的线程可见)
           logger.debug("开始计时: {"+new SimpleDateFormat("HH:mm:ss.SSS").format(beginTime)+"}  URI: {"+request.getRequestURI()+"}");
      }
      return true;
   }

   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
      if (modelAndView != null){
         logger.info("ViewName: " + modelAndView.getViewName());
      }
   }

   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {

      logger.debug("Header: " + request.getHeader("user-agent"));
      logger.debug("RequestURI: " + request.getRequestURI());
      logger.debug("Method: " + request.getMethod());
      logger.debug("ParameterMap: " + request.getParameterMap().toString());

      // 打印JVM信息
      if (logger.isDebugEnabled()){
         long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
         long endTime = System.currentTimeMillis();     //2、结束时间
         logger.debug("计时结束:{"+new SimpleDateFormat("HH:mm:ss.SSS").format(endTime)+"}  耗时:{"+DateUtils.formatDateTime(endTime - beginTime)+"}  URI: {"+request.getRequestURI()+"}  最大内存: {"+(Runtime.getRuntime().maxMemory()/1024/1024)+"}m  已分配内存: {"+(Runtime.getRuntime().totalMemory()/1024/1024)+"}m  已分配内存中的剩余空间: {"+(Runtime.getRuntime().freeMemory()/1024/1024+"}m  最大可用内存: {"+(Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory()+Runtime.getRuntime().freeMemory())/1024/1024)+"}m") ;
      }
   }
}

自定义拦截器会针对 /user/** 和 /childs/** 的请求进行拦截,输出日志。

测试后发现,错误日志既没有在控制台打印出来也没有输出到文件,因为我们需要配置spring-mvc的全局异常处理器,实现 HandlerExceptionResolver  接口

public class DefaultExceptionHandler implements HandlerExceptionResolver {

    private static final Logger logger = org.apache.log4j.Logger.getLogger(DefaultExceptionHandler.class);

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        logger.error("异常信息", ex);

        String url = request.getRequestURL().toString();

        if (url.contains("api")) {
            try {
                /**
                 * 来自app的请求异常处理
                 */
                response.setStatus(HttpStatus.OK.value());
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.setCharacterEncoding("UTF-8");
                response.getWriter().write(new Gson().toJson("系统错误"));
            } catch (IOException e) {
                logger.error("", e);
            }
        } else {
            ModelAndView mv = new ModelAndView();
            mv.setViewName("error");
            return mv;
        }

        return null;
    }

}

还需要在spring-mvc.xml中配置这个bean

<bean id="exceptionHandler" class="com.utils.DefaultExceptionHandler" />

再次运行项目,系统出现错误的时候,会跳转到 error.jsp,错误信息会被输出到 error_log 文件。

01-18 20:40