在应用中通过页面动态调整log4j的logger级别
作为团队中的服务器开发人员,为了记录服务的执行信息以便跟踪,我们会在代码中添加大量不同级别的log信息.
在开发环境中,我们会添加许多debug或者trace级别的log, 用来记录服务端的执行逻辑来诊断遇到的问题, 但在上线环境中,考虑到性能原因,我们通常会关闭debug和trace级别日志,只保留info和更严重级别的日志.
系统上线后,我们偶尔会遇到一些莫名其妙的bug,通过仅有的info级别和更严重日志,有时候会很难诊断出问题所在.这时候,我们不禁希望能够在不重启服务器影响业务的情况下,把bug前后的debug甚至trace级别的日志记录下来.
使用log4j,这样的需求很容易得到满足:
一种方式是修改log4j配置文件,在应用不重启的情况下,让应用检测到配置文件的变化并重新加载log4j的配置文件.
另一种方式是通过log4j的Api动态的logger的日志级别.
第一种方式需要手工编写配置文件,在诊断应用时很不方便.
而通过后一种方式,我们可以编写一个管理页面,可以方便的随时对应用中所有的logger级别进行调整.
实现后一种方式,只需要简单几行代码:
private String changeLoggerLevel(String loggerName, String level) {
Logger logger = LogManager.exists(loggerName);
String result = null;
if (logger != null) {
logger.setLevel(Level.toLevel(level));
result = logger.getName() + "|" + logger.getLevel();
} else {
result = "logger not exist.";
}
return result;
}以上代码中,传入目标logger的名称和级别.使用log4j的Api,就能方便的动态调整日志级别.
而在改变logger级别之前,我们希望能方便的列出logger列表,以便能找到我们调整目标logger的名称.可以通过以下方法来实现:
private String listSubLoggerInfo(String loggerNamePrefix) {
Enumeration<Logger> currentLoggers = LogManager.getCurrentLoggers();
StringBuilder sb = new StringBuilder();
while (currentLoggers.hasMoreElements()) {
Logger logger = currentLoggers.nextElement();
if (logger.getName().startsWith(loggerNamePrefix)) {
sb.append(logger.getName()).append(":").append(logger.getLevel()).append("|");
}
}
return sb.toString();
}在以上方法中,我们传入loggerName的前缀,比如传入com.mycompany.myproject.service.方法将返回所有以此前缀开关的logger的信息:
com.mycompany.myproject.service.AService:INFO|com.mycompany.myproject.service.BService:INFO|.......
这样我们就能快速锁定目标logger的名称,比如com.mycompany.myproject.service.AService和当前日志级别:INFO.
通过以上两个简单的方法,我们就可以构建一个页面来显示logger列表,并调整日志logger的日志级别了.以下我编写的一个简单页面(用到了jquery):
<jsp:output omit-xml-declaration="yes"/>
<script type="text/javascript">
$(document).ready(function() {
$('#levelForm').submit(function(){
$.post('changeLoggerLevel', $('#levelForm').serialize(),function(data,status){
alert(data);
});
return false;
});
$('#info').click(function(){
var infoDiv = $('#loggerInfo');
infoDiv.empty();
$.post('infoLogger', $('#levelForm').serialize(),function(data,status){
$.each(data.split("|").sort(),function(){
infoDiv.append($('<p>'+this+'</p>'));
});
});
return false;
});
var lastP;
$("#loggerInfo p").live('click', function(){
if(lastP){
lastP.css("background-color","");
}
lastP = $(this);
$(this).css("background-color","yellow");
$('#loggerName').val($(this).text().split(":")[0]);
});
});
</script>
<form id="levelForm">
<p><label>LoggerName:</label><input id='loggerName' type="text" name="logger" style="width:600px" value="com.mycompany.myproject"/></p>
<p><label>LoggerLevel:</label>
<select name="level">
<option value="DEBUG">DEBUG</option>
<option value="TRACE">TRACE</option>
<option value="INFO">INFO</option>
<option value="WARN">WARN</option>
<option value="ERROR">ERROR</option>
<option value="FATAL">FATAL</option>
<option value="ALL">ALL</option>
<option value="OFF">OFF</option>
</select>
</p>
<input type="submit" value="Submit" />
<input id="info" type="button" value="Info"/>
</form>
<div id="loggerInfo">
</div>最后效果如图:
