引言
在开发与生产环境中我们有时候需要对日志的配置进行动态切换要调试监控和检查系统的运行时信息
一般有两种方法
通过 Spring 的 LogjConfigListener 在启动时开启定时器进行定时加载配置文件
通过 JMX 动态控制
以上可以从我的《利用Spring来管理控制自己的应用程序》专题演讲资料中获取到更加详细的信息包括示例的源程序地址为
先说一下上面两种方法的不同与缺点
通过 Spring 的 LogjConfigListener则必须在后台打开线程现定时扫描然后来定时工作有点浪费
通过 JMX 动态控制的则必须供一个管理的端口不仅有可能端口被占用(当然有个 workaround 来解决它)还有存在防火墙等等需要配置这个管理端口进行对外暴露等等
虽然上述两种方法存在着一些不足但是这两种方法在特定的场合下都可以很好地来利用它进行完美地工作
现在利用它进行封装与扩展我们可以巧妙地进行定制并通过 Web Console 界面来更方便地进行动态切换配置信息而且不需要重新启动正在运行中的应用程序
二分析
通过分析 LogjConfigListener完整的类名为 orgspringframeworkwebutilLogjConfigListener 可以得到动态加载的过程与原理
LogjConfigListenerjava
public class LogjConfigListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
LogjWebConfigurerinitLogging(eventgetServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
LogjWebConfigurershutdownLogging(eventgetServletContext());
}
}
进而可以得知一切都是由 LogjWebConfigurer 来进行操作了再分析其中的代码可以得到 LogjWebConfigurer 的工作过程并由此进行到 LogjConfigurer 中
最后我们可以得到最直接有用的三个方法分别如下
LogjConfigurerinitLogging(location);
根据给定的配置文件进行初始化日志配置
LogjConfigurerinitLogging(location refreshInterval);
根据给定的配置文件和间隔时间进行初始化日志配置并定时重新加载配置文件
LogjConfigurershutdownLogging();
关闭日志
根据以上分析接下来就是需要进行重新封装的工作了我们同时保留原来的定时加载的功能但通过开关进行设置同时对整个功能进行封装与扩展
三封装与扩展
设计一个名为 LogJRefreshInterval 的 JavaBean
定义如下可配置选项
private String CLASSPATH = classpath:;
private String location = CLASSPATH + logjxml;
private String locationRunning = location;
private long refreshInterval = ;
private long refreshSecond = ;
private long refreshMinute = ;
private long refreshHour = ;
private boolean refreshDaemon = false;
增加 refreshDaemon 开关在配置里根据需要要打开是否定时进行加载日志的配置文件
增加一系列的时间配置参数毫秒秒分时然后对这些时间进行加和总和为定时的时间
refreshHour * * + refreshMinute * * + refreshSecond * + refreshInterval;
封装方法如下方法进行控制
public interface ILogJRefreshInterval
{
public void init(); // 根据配置信息初始化日志
public void destroy(); // 销毁日志
public void refreshIntervalThread(); // 定时加载日志的配置信息
public void refreshIntervalImmediately(); // 立即加载默认的日志配置信息
public void refreshIntervalImmediatelyByFilePath(String logjFilePath); // 立即加载指定的日志配置文件
public void refreshIntervalImmediately(boolean isXmlConfigString logjConfigInfo); // 立即加载指定的日志配置文件
public String getRunningConfing() throws Exception;//获取正在运行的日志配置信息
}
构建页面
由于将通过 Web Console 页面进行管理控制也更加方便当然也可以通过 Web Services 等等之类的因为 POJO 所以可以根据项目的实际情况来加以选择
创建 l 文件代码如下
<html>
<head>
<meta httpequiv=ContentType content=text/html; charset=UTF/>
<title>日志动态配置管理控制台</title>
<link rel=stylesheet type=text/css/>
</head>
<body>
<img src=loggerjpg alt=日志动态配置管理控制台/>
<form action=logJRefreshdo method=post>
<p>配置内容(两者只取其一)</p>
<p><input checked type=radio name=configMethod onclick=isXmlConfigdisabled=true;logjConfigInfovalue=;logjConfigInfodisabled=true;logjFilePathdisabled=false;logjFilePathfocus();/>1LogJ的文件路径格式如下<input name=logjFilePath ondblclick=thisvalue=/var/logjproperties size=/></p>
<ul>
<li>classpath:logjproperties 或者 classpath:logjxml</li>
<li>file:C:/logjproperties 或者 file:C:/logjxml</li>
<li>C:/logjproperties 或者 C:/logjxml</li>
</ul>
<p><input type=radio name=configMethod onclick=logjFilePathvalue=;logjFilePathdisabled=true;isXmlConfigdisabled=false;logjConfigInfodisabled=false;logjConfigInfofocus();/>2LogJ的详细配置信息</p>
<p>配置方式
<select name=isXmlConfig>
<option value= selected>Property</option>
<option value=>XML</option>
</select>(请根据实际的配置内容选择相应的类型)</p>
<textarea name=logjConfigInfo rows= cols= ondblclick=thisvalue=>
# defaultlayoutConversionPattern = %t [%c{}][%p]:[%L]:%m%n
defaultlayoutConversionPattern = %d{yyyyMMdd HH:mm:ss} [%t] [%c{}][%p]:[%L]:%m%n
</textarea>
<input type=submit value=提交/>
<input type=reset value=重置/>
</form>
<br>
<a title=查看正在运行的LogJ配置信息>查看配置信息</a>
<a title=立刻重新加载通过配置文件加载的LogJ配置信息>立刻重新加载</a>
<a title=立刻停止LogJ的服务所有日志关停>立刻停止日志</a>
<a title=调用Servlet来测试LogJ信息是否正常>测试是否正常</a>
</body>
</html>
构建 Controller
在此示例中直接采用简单易用的 Spring MVC 进行控制
直接 implements Controller 来创建三个 Controller 分别如下
LogJRefreshController 重新加载日志配置文件的控制器
LogJShutdownController 关闭日志的控制器
LogJRunningController 获取正在运行的日志配置信息的控制器
构建一个 Servlet
用来测试日志配置信息是否成功加载
public class HelloServlet extends HttpServlet
{
private static final long serialVersionUID = L;
private static final Logger logger = LoggergetLogger(HelloServletclass);
public void doGet(HttpServletRequest requestHttpServletResponse response)
{
try
{
ServletOutputStream out = responsegetOutputStream();
outprintln(<html xmlns=\\>);
outprintln(<head>);
outprintln(<meta httpequiv=\ContentType\ content=\text/html; charset=GBK\/>);
outprintln(<title>动态配置测试页面</title>);
outprintln(<link href=\facadecss\ rel=\stylesheet\ type=\text/css\/>);
outprintln(</head><body>);
outprintln(<img src=\loggerjpg\ alt=\日志动态配置管理控制台\/>);
outprintln(<p>Begin: + new Date() + <br/>);
if(loggerisDebugEnabled())
{
loggerdebug(DEBUG级别的信息debugging<p>);
outprintln(DEBUG级别的信息debugging</p>);
}
if(loggerisInfoEnabled())
{
(INFO级别的信息information<p>);
outprintln(INFO级别的信息information</p>);
}
loggerwarn(warning);
loggererror(error);
loggerfatal(fatal);
outprintln(End: + new Date() + <br/>);
outprintln(<p><a href=\JavaScript:historygo()\>返回</a>);
outprintln(</p></body></html>);
outflush();
}
catch(IOException e)
{
eprintStackTrace();
}
}
}
构建两个 JSP 页面
一个成功的页面 RefreshSuccessjsp代码如下
<%@page contentType=text/html; charset=UTF pageEncoding=UTF%>
<html xmlns=>
<head>
<meta httpequiv=ContentType content=text/html; charset=UTF/>
<title>动态配置操作成功</title>
<link rel=stylesheet type=text/css/>
</head>
<body>
<img src=loggerjpg alt=日志动态配置管理控制台/>
<p>
恭喜动态配置操作成功!
<p>
当前的配置为
<textarea rows= cols= ReadOnly><%=requestgetAttribute(RunningConfig)%></textarea>
<p>
<a JavaScript:historygo()>返回</a>
</body>
</html>
以及一个失败的页面 RefreshFailedjsp 代码略
配置 Spring 的 Bean 文件(Service)
beanRefLogJxml
<?xml version= encoding=UTF?>
<!DOCTYPE beans PUBLIC //SPRING//DTD BEAN//EN beansdtd>
<beans>
<bean id=propertyConfigurer class=orgspringframewonfigPropertyPlaceholderConfigurer>
<property name=locations>
<list>
<value>classpath:LoggerConsoleproperties</value>
</list>
</property>
</bean>
<bean id=logJRefreshInterval class=netagilespringtimeloggerutilLogJRefreshInterval initmethod=init destroymethod=destroy>
<property name=location value=${loggerlogjlocation}/>
<property name=refreshDaemon value=${loggerlogjrefreshDaemon}/>
<property name=refreshInterval value=${loggerlogjrefreshInterval}/>
<property name=refreshSecond value=${loggerlogjrefreshSecond}/><property name=refreshMinute value=${loggerlogjrefreshMinute}/><property name=refreshHour value=${loggerlogjrefreshHour}/></bean>
</beans>
配置 Spring 的 Bean 文件(MVC)
beanRefMVCxml
<?xml version= encoding=utf?>
<!DOCTYPE beans PUBLIC //SPRING//DTD BEAN//EN beansdtd>
<beans>
<bean id=urlMapping class=orgspringframeworkwebservlethandlerSimpleUrlHandlerMapping>
<property name=mappings>
<props>
<prop key=/logJRefreshdo>logJRefreshController</prop>
<prop key=/logJShutdowndo>logJShutdownController</prop>
<prop key=/logJRunningdo>logJRunningController</prop>
</props>
</property>
</bean>
<bean id=viewResolver class=orgspringframeworkwebservletviewInternalResourceViewResolver>
<property name=prefix>
<value>/WEBINF/jsp/</value>
</property>
<property name=suffix>
<value>jsp</value>
</property>
</bean>
<bean id=logJRefreshController class=netagilespringtimeloggerwebLogJRefreshController>
<property name=successView value=RefreshSuccess/>
<property name=failedView value=RefreshFailed/>
<property name=logJRefreshInterval ref=logJRefreshInterval/>
</bean>
<bean id=logJShutdownController class=netagilespringtimeloggerwebLogJShutdownController>
<property name=successView value=RefreshSuccess/>
<property name=failedView value=RefreshFailed/>
<property name=logJRefreshInterval ref=logJRefreshInterval/>
</bean>
<bean id=logJRunningController class=netagilespringtimeloggerwebLogJRunningController>
<property name=successView value=RefreshSuccess/>
<property name=failedView value=RefreshFailed/>
<property name=logJRefreshInterval ref=logJRefreshInterval/>
</bean>
</beans>
在你现有的应用中引入这两个 Spring 的配置文件或者根据你的项目结构进行调整即可
<?xml version= encoding=utf?>
<!DOCTYPE beans PUBLIC //SPRING//DTD BEAN//EN beansdtd>
<beans>
<import resource=beanRefLogJxml />
<import resource=beanRefMVCxml />
</beans>
配置 webxml 文件
在 webxml 中增加 Spring MVC 的配置以及测试用的 Servlet 的配置完整的配置如下
<!DOCTYPE webapp PUBLIC //Sun Microsystems Inc//DTD Web Application //EN app__dtd>
<webapp>
<displayname>Spring LogJ Refresh Web Application</displayname>
<description>Spring LogJ Refresh Web Application</description>
<contextparam>
<paramname>contextConfigLocation</paramname>
<paramvalue>/WEBINF/classes/beanRefApplicationxml</paramvalue>
</contextparam>
<listener>
<listenerclass>orgsprntextContextLoaderListener</listenerclass>
</listener>
<servlet>
<servletname>dispatcherServlet</servletname>
<servletclass>orgspringframeworkwebservletDispatcherServlet</servletclass>
<loadonstartup></loadonstartup>
</servlet>
<servlet>
<servletname>HelloServlet</servletname>
<displayname>HelloServlet</displayname>
<servletclass>netagilespringtimeloggerwebHelloServlet</servletclass>
</servlet>
<servletmapping>
<servletname>HelloServlet</servletname>
<urlpattern>/HelloServlet</urlpattern>
</servletmapping>
<servletmapping>
<servletname>dispatcherServlet</servletname>
<urlpattern>*do</urlpattern>
</servletmapping>
</webapp>
这样你的项目就具有了动态日志配置切换的功能了
四校验日志是否成功改变
启动应用访问//localhost:/LogJ_Spring_Web/ 如下图所示
可以选择LogJ的文件路径或者直接输入详细的配置文件的LogJ的详细配置信息方式进行动态切换
查看配置信息可以查看到当前正在运行的配置信息如下图
立刻重新加载
可以立即加载默认的日志配置
文件并返回默认的配置内容如下图
立刻停止日志
可以立即停止日志并显示停止前的日志配置信息如下图
测试是否正常
可通过此测试用的 Servlet 来查看日志是否成功在重新加载新的配置文件后建议都调用一下来测试是否切换成功如下图
先提交 INFO 等级的配置
然后查看测试的结果信息如下
只显示 INFO 级别的信息
更改为 DEBUG级别并提交如下
然后查看测试的结果信息如下
显示出 DEBUG 级别的信息出来了表明已经成功地进行动态切换
五总结
一个简单的封装与扩展实现了对日志的配置进行动态切换调试监控和检查系统的运行时信息方便了许多提升了开发效率