注册本站  论坛  繁體中文

电脑技巧
手机 | MP3 | MP4 | 显卡 | 主板 | 显示器 | 光存储 | 笔记本 | 网络设备 | 移动存储 | 数码相机
键鼠 | CPU | 音箱 | GPS | 电视 | 服务器 | 投影机 | 机箱电源 | 品牌电脑 | 办公打印 |
| 网站首页 | Cisco | Windows | Linux | Java | Dotnet | Oracle | 网页设计 | 平面设计 | 安全 | 软件应用 | 电脑维修 | 办公维修 |
您现在的位置: 电脑技巧 >> Java >> 开源技术 >> Spring >> Java正文

用Spring与Log4J进行动态日志配置切换

文章来源:《IT实验室周报》 作者:俞黎敏 更新时间:2008-6-28 20:35:45 【 】 【加入收藏

      版权声明:本作品已刊登在《IT实验室周报》第03期第06版,作者俞黎敏。版权属于《IT实验室周报》与中国IT实验室网站共同所有,禁止任何媒体、网站或个人在未经书面授权的情况下转载、摘编或利用其它方式使用上述作品。已经授权使用作品的,应在授权范围内使用,并注明“来源:IT实验室周报”。违反上述声明者将追究其相关法律责任。

 

  利用 Spring 与 Log4J 巧妙地进行动态日志配置切换并立即生效

    引言:

    在开发与生产环境中,我们有时候需要对日志的配置进行动态切换,要调试、监控和检查系统的运行时信息。

    一般有两种方法

    1、通过 Spring 的 Log4jConfigListener 在启动时开启定时器进行定时加载配置文件
    2、通过 JMX 动态控制

    以上可以从我的《利用Spring来管理控制自己的应用程序》专题演讲资料中获取到更加详细的信息,包括示例的源程序,地址为
    http://yulimin.javaeye.com/blog/52354

    先说一下上面两种方法的不同与缺点:

    1、通过 Spring 的 Log4jConfigListener,则必须在后台打开线程,现定时扫描,然后来定时工作,有点浪费;
    2、通过 JMX 动态控制的则必须供一个管理的端口,不仅有可能端口被占用(当然有个 workaround 来解决它),还有存在防火墙等等需要配置这个管理端口进行对外暴露等等。

    虽然上述两种方法存在着一些不足,但是这两种方法在特定的场合下,都可以很好地来利用它进行完美地工作。

    现在,利用它进行封装与扩展,我们可以巧妙地进行定制,并通过 Web Console 界面来更方便地进行动态切换配置信息,而且不需要重新启动正在运行中的应用程序。

    二、分析

    通过分析 Log4jConfigListener,完整的类名为 org.springframework.web.util.Log4jConfigListener ,可以得到动态加载的过程与原理。

    Log4jConfigListener.java

    

public class Log4jConfigListener implements ServletContextListener {

 

    public void contextInitialized(ServletContextEvent event) {

       Log4jWebConfigurer.initLogging(event.getServletContext());

    }

 

    public void contextDestroyed(ServletContextEvent event) {

       Log4jWebConfigurer.shutdownLogging(event.getServletContext());

    }

}

    进而可以得知,一切都是由 Log4jWebConfigurer 来进行操作了,再分析其中的代码,可以得到 Log4jWebConfigurer 的工作过程,并由此进行到 Log4jConfigurer 中。

    最后我们可以得到最直接有用的三个方法,分别如下:

    1、Log4jConfigurer.initLogging(location);

    根据给定的配置文件进行初始化日志配置

    2、Log4jConfigurer.initLogging(location, refreshInterval);

    根据给定的配置文件和间隔时间,进行初始化日志配置并定时重新加载配置文件,

    3、Log4jConfigurer.shutdownLogging();

    关闭日志

    根据以上分析,接下来就是需要进行重新封装的工作了,我们同时保留原来的定时加载的功能,但通过开关进行设置,同时对整个功能进行封装与扩展。

    三、封装与扩展

    1、设计一个名为 Log4JRefreshInterval 的 JavaBean

    1.1 定义如下可配置选项:

   

     private String CLASSPATH = "classpath:";

    private String location = CLASSPATH + "log4j.xml";

    private String locationRunning = location;

    private long refreshInterval = 60000;

    private long refreshSecond = 0;  

    private long refreshMinute = 0;   

    private long refreshHour = 0;    

    private boolean refreshDaemon = false;

    增加 refreshDaemon 开关,在配置里根据需要要打开是否定时进行加载日志的配置文件;

    增加一系列的时间配置参数,毫秒、秒、分、时,然后对这些时间进行加和,总和为定时的时间

    refreshHour * 3600 * 1000 + refreshMinute * 60 * 1000 + refreshSecond * 1000 + refreshInterval;

    1.2 封装方法如下方法进行控制

   

 public interface ILog4JRefreshInterval

{  

    public void init(); // 根据配置信息初始化日志

    public void destroy(); // 销毁日志

    public void refreshIntervalThread(); // 定时加载日志的配置信息

    public void refreshIntervalImmediately(); // 立即加载默认的日志配置信息

   public void refreshIntervalImmediatelyByFilePath(String log4jFilePath); // 立即加载指定的日志配置文件

   

    public void refreshIntervalImmediately(boolean isXmlConfig,String log4jConfigInfo); // 立即加载指定的日志配置文件

    public String getRunningConfing() throws Exception;//获取正在运行的日志配置信息

}

    1.3 构建页面

    由于将通过 Web Console 页面进行管理控制,也更加方便,当然也可以通过 Web Services 等等之类的,因为 POJO 所以可以根据项目的实际情况来加以选择。

    创建 index.html 文件,代码如下:

   

 

</head>

<body>

<img src="logger.jpg" alt="日志动态配置管理控制台"/>

<form action="log4JRefresh.do" method="post">

    <p>配置内容:(两者只取其一)</p>

    <p><input checked type="radio" name="configMethod"

onclick="isXmlConfig.disabled=true;log4jConfigInfo.value

='';log4jConfigInfo.disabled=true;log4jFilePath.disabled

=false;log4jFilePath.focus();"/>1、Log4J的文件路径,格式如下:

<input name="log4jFilePath" ondblclick="this.value='/var/

log4j.properties'" size="100"/></p>

    <ul>

    <li>classpath:log4j.properties 或者 classpath:log4j.xml</li>

    <li>file:C:/log4j.properties 或者 file:C:/log4j.xml</li>

    <li>C:/log4j.properties 或者 C:/log4j.xml</li>

    </ul>

    <p><input type="radio" name="configMethod" onclick="log4jFilePath.value='';log4jFilePath.disabled=

true;isXmlConfig.disabled=false;log4jConfigInfo.disabled=

false;log4jConfigInfo.focus();"/>2、Log4J的详细配置信息:</p>

    <p>配置方式:

    <select name="isXmlConfig">

       <option value="0" selected>Property</option>

       <option value="1">XML</option>

    </select>(请根据实际的配置内容选择相应的类型)</p>

    <textarea name="log4jConfigInfo" rows="23" cols="123" ondblclick="this.value=''">

 

# default.layout.ConversionPattern = %t [%c{3}]-[%-5p]:[%L]:%m%n

default.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%t] [%c{3}]-[%-5p]:[%L]:%m%n

 

</textarea>

    <input type="submit" value="提 交"/>  

    <input type="reset" value="重 置"/>

</form>

<br>

<a href="log4JRunning.do" title="查看正在运行的Log4J配置信息。">查看配置信息</a>

  

<a href="log4JRefresh.do" title="立刻重新加载通过配置文件加载的Log4J配置信息。">立刻重新加载</a>

  

<a href="log4JShutdown.do" title="立刻停止Log4J的服务,所有日志关停。">立刻停止日志</a>

  

<a href="HelloServlet" title="调用Servlet来测试Log4J信息是否正常。">测试是否正常</a>

</body>

</html>

    1.4 构建 Controller

      在此示例中直接采用简单易用的 Spring MVC 进行控制。
      直接 implements Controller 来创建三个 Controller ,分别如下:
     Log4JRefreshController  重新加载日志配置文件的控制器
     Log4JShutdownController 关闭日志的控制器
     Log4JRunningController  获取正在运行的日志配置信息的控制器

    1.5 构建一个 Servlet

    用来测试日志配置信息是否成功加载

   

 public class HelloServlet extends HttpServlet

{

    private static final long serialVersionUID = -4506255419343502640L;

    private static final Logger logger = Logger.getLogger(HelloServlet.class);

 

    public void doGet(HttpServletRequest request,HttpServletResponse response)

    {

       try

       {

           ServletOutputStream out = response.getOutputStream();

       out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");

           out.println("<head>");

           out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=GBK\"/>");

           out.println("<title>动态配置测试页面</title>");

           out.println("<link href=\"facade.css\" rel=\"stylesheet\" type=\"text/css\"/>");

           out.println("</head><body>");

           out.println("<img src=\"logger.jpg\" alt=\"日志动态配置管理控制台\"/>");

           out.println("<p>Begin:" + new Date() + "<br/>");

           if(logger.isDebugEnabled())

           {

              logger.debug("DEBUG级别的信息:debugging<p>");

              out.println("DEBUG级别的信息:debugging</p>");

                         }

           if(logger.isInfoEnabled())

           {

              logger.info("INFO级别的信息:information<p>");

              out.println("INFO级别的信息:information</p>");

                         }

           logger.warn("warning");

           logger.error("error");

           logger.fatal("fatal");

           out.println("End:" + new Date() + "<br/>");

           out.println("<p><a href=\"JavaScript:history.go(-1)\">返  回</a>");

           out.println("</p></body></html>");

           out.flush();

       }

       catch(IOException e)

       {

           e.printStackTrace();

       }

    }

}

    1.6 构建两个 JSP 页面

    一个成功的页面 RefreshSuccess.jsp,代码如下

   

 <%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

<title>动态配置操作成功</title>

<link href="facade.css" rel="stylesheet" type="text/css"/>

</head>

 

<body>

<img src="logger.jpg" alt="日志动态配置管理控制台"/>

<p>

恭喜:动态配置操作成功!

<p>

当前的配置为:

<textarea rows="23" cols="123" ReadOnly><%=request.getAttribute("RunningConfig")%></textarea>

<p>

<a href="JavaScript:history.go(-1)">返  回</a>

</body>

</html>

    以及一个失败的页面 RefreshFailed.jsp ,代码略

    1.7 配置 Spring 的 Bean 文件(Service)

    beanRefLog4J.xml

   

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.

PropertyPlaceholderConfigurer">

       <property name="locations">

           <list>

              <value>classpath:LoggerConsole.properties</value>

           </list>

       </property>

    </bean>

    <bean id="log4JRefreshInterval" class="net.agile.springtime.logger.util.Log4JRefreshInterval" init-method="init" destroy-method="destroy">

       <property name="location" value="${logger.log4j.location}"/>

       <property name="refreshDaemon" value="${logger.log4j.refreshDaemon}"/>

       <property name="refreshInterval" value="${logger.log4j.refreshInterval}"/>

       <property name="refreshSecond" value="${logger.log4j.refreshSecond}"/>       <property name="refreshMinute" value="${logger.log4j.refreshMinute}"/>       <property name="refreshHour" value="${logger.log4j.refreshHour}"/>    </bean>

</beans>

    1.8 配置 Spring 的 Bean 文件(MVC)

    beanRefMVC.xml

  

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

       <bean id="urlMapping" class="org.springframework.web.servlet.handler.

SimpleUrlHandlerMapping">

       <property name="mappings">

           <props>

              <prop key="/log4JRefresh.do">log4JRefreshController</prop>

              <prop key="/log4JShutdown.do">log4JShutdownController</prop>

              <prop key="/log4JRunning.do">log4JRunningController</prop>

           </props>

       </property>

    </bean>

    <bean id="viewResolver" class="org.springframework.web.servlet.

view.InternalResourceViewResolver">

       <property name="prefix">

           <value>/WEB-INF/jsp/</value>

       </property>

       <property name="suffix">

           <value>.jsp</value>

       </property>

    </bean>

    <bean id="log4JRefreshController" class="net.agile.springtime.logger.web.Log4JRefreshController">

       <property name="successView" value="RefreshSuccess"/>

       <property name="failedView" value="RefreshFailed"/>

       <property name="log4JRefreshInterval"

ref="log4JRefreshInterval"/>

    </bean>

       <bean id="log4JShutdownController" class="net.agile.springtime.logger.web.Log4JShutdownController">

       <property name="successView" value="RefreshSuccess"/>

       <property name="failedView" value="RefreshFailed"/>

       <property name="log4JRefreshInterval"

 ref="log4JRefreshInterval"/>

    </bean>

       <bean id="log4JRunningController" class="net.agile.springtime.logger.web.Log4JRunningController">

       <property name="successView" value="RefreshSuccess"/>

       <property name="failedView" value="RefreshFailed"/>

       <property name="log4JRefreshInterval"

ref="log4JRefreshInterval"/>

    </bean>

</beans>

    1.9 在你现有的应用中引入这两个 Spring 的配置文件,或者根据你的项目结构进行调整即可

   

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <import resource="beanRefLog4J.xml" />

    <import resource="beanRefMVC.xml" />

</beans>

    1.10 配置 web.xml 文件

    在 web.xml 中增加 Spring MVC 的配置以及测试用的 Servlet 的配置,完整的配置如下:

   

 

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

    <display-name>Spring Log4J Refresh Web Application</display-name>

    <description>Spring Log4J Refresh Web Application</description>

       <context-param>

       <param-name>contextConfigLocation</param-name>

       <param-value>/WEB-INF/classes/beanRefApplication.xml</param-value>

    </context-param>

    <listener>

        <listener-class>org.springframework.web.context.ContextLoaderListener

</listener-class>

    </listener>

       <servlet>

       <servlet-name>dispatcherServlet</servlet-name>

       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

       <load-on-startup>50</load-on-startup>

    </servlet>

   <servlet>

       <servlet-name>HelloServlet</servlet-name>

       <display-name>HelloServlet</display-name>

       <servlet-class>net.agile.springtime.logger.web.HelloServlet</servlet-class>

    </servlet>

    <servlet-mapping>

       <servlet-name>HelloServlet</servlet-name>

       <url-pattern>/HelloServlet</url-pattern>

    </servlet-mapping>

    <servlet-mapping>

       <servlet-name>dispatcherServlet</servlet-name>

       <url-pattern>*.do</url-pattern>

    </servlet-mapping>

</web-app>

    这样,你的项目就具有了动态日志配置切换的功能了。

    四、校验日志是否成功改变

    1. 启动应用,访问 http://localhost:8080/Log4J_Spring_Web/  如下图所示

   

    可以选择"Log4J的文件路径"或者直接输入详细的配置文件的"Log4J的详细配置信息"方式进行动态切换

    2.查看配置信息

      可以查看到当前正在运行的配置信息,如下图:

   

    3.立刻重新加载

      可以立即加载默认的日志配置 文件,并返回默认的配置内容,如下图:

   

    4.立刻停止日志

      可以立即停止日志,并显示停止前的日志配置信息,如下图:


   

    5.测试是否正常

     可通过此测试用的 Servlet 来查看日志是否成功,在重新加载新的配置文件后,建议都调用一下来测试是否切换成功,如下图:

        先提交 INFO 等级的配置

   

    然后查看测试的结果信息,如下:

 

   

    只显示 INFO 级别的信息

   更改为 DEBUG级别,并提交,如下:

   

    然后查看测试的结果信息,如下:


    五、总结

    一个简单的封装与扩展,实现了对日志的配置进行动态切换,调试、监控和检查系统的运行时信息,方便了许多,提升了开发效率。

  • 上一篇Java:

  • 下一篇Java:
  • 最 新 热 门
     手机开发平台指南、教程和资料介绍
     关于什么叫面向接口编程
     编写高级JavaScript应用代码
     不要验证,直接转化科学计数法
     Eclipse插件开发中实现刷新和重编译介绍
     Java开源技术:Eclipse的使用技巧详解
     配置eclipse 3.2 使用JDK1.5中文JavaAPI
     集成Windows本地应用到Eclipse RCP 程序中
     hibernate.cfg.xml配置文件的说明
     eclipse开发jface时,main.class解决方法
    最 新 推 荐
     ibatis+spring 集成
     结合Spring2.0和ActiveMQ进行异步消息调用
     servlet中如何访问spring容器
     Spring MVC与struts比较
     Spring security 命名空间的使用
     spring AOP面向切面编程
     关于String对象解析
     关于Spring切点函数@args()
     Spring中事务的传播属性详解
     Spring+Ibatis+事务处理
    相 关 文 章

    ibatis+spring 集成
    结合Spring2.0和ActiveMQ进行异步消息调用
    servlet中如何访问spring容器
    Spring MVC与struts比较
    Spring security 命名空间的使用
    spring AOP面向切面编程
    关于Spring切点函数@args()
    Spring中事务的传播属性详解
    Spring+Ibatis+事务处理
    基于Spring事务的集成测试

    | 设为首页 | 加入收藏 | 联系站长 | 友情链接 | 版权申明 | 网站公告

     

    Copyright 2006-2008 pcjx.com All Rights Reserved
    电脑技巧 版权所有 粤ICP备06059145号 地图
    本网站所有内容未经许可不得转载或做其他使用