Fork me on GitHub

【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:11.定制化Log输出


欢迎阅读我的开源项目《迷你微信》服务器《迷你微信》客户端

前言

《迷你微信》服务器中,我们用了Log4J来进行输出,这可以在我们程序出现异常的时候找到错误发生时的上下文。然而吗,在项目的组件迭代过程中,我们发现,log出来的内容越来越多,往往在储蓄出现异常去查Log的时候,会被一大堆不相干的Log给淹没,这时,我们想:如果能够只输出我要的那部分岂不是很爽,这样就能快速的找到我们要的Log了,所以博主自己别编写了一个定制化输出的方法。

Log4J的Name

Log4J在创建时是需要设置名称的,可以把它理解成一个索引,以此在其他地方拿到它。

public class MyLogTest {
	private Logger logger = Logger.getLogger(this.getClass().getSimpleName());
p
	public void method_1(){
		logger.info("abcdefg");
	}
}

public class Test_2 {
	public Logger getMyLogger(String name){
		return Logger.getLogger(name);
	}

}

上述MyLogTest类中,我们用类名创建了一个Log4J的Logger对象,在第二个类Test_2中,我们有一个getMyLogger方法,如果我们调用时传入的是MyLogTest的类名,则会返回MyLogTest中的那个Logger对象。

注:Logger.getLogger(name) 方法在以name命名对象不存在时,会创建一个新的返回,当已经被创建后调用则会返回之前创建过的那个Logger对象。

定制化Log输出

配置XML

想要做到定制化Log输出,我们需要有一个配置文件来设置哪些Logger输出,哪些不输出。
(详细源码请参考《迷你微信》服务器)

<?xml version=""1.0" encoding="UTF-8"?>
<LoggerRule>
	<TypeContain>False</TypeContain>
	<TypeList>
		<ProtoHead logType="class">server.Server_User</ProtoHead>
	</TypeList>
</LoggerRule>

先来解释一下上述代码所代表的意义:

  • LoggerRule : 代表Log规则的根,内不包含所有规则
  • TypeContain :参数为True时,代表包含,只输出列表中所述Logger的内容,Flase代表“除外”,即下面列表所述的Logger都不输出。
  • ProtoHead :每一个(不)要输出的条目(注:叫这个名字时历史遗留问题)

整体来说,就是在项目运行时,叫server.Server_User这个名字的Logger将被忽略输出。

编写读取类

private void readLogRule() {
        DocumentBuilderFactory domfac=DocumentBuilderFactory.newInstance();
		try {
            DocumentBuilder dombuilder=domfac.newDocumentBuilder();
            InputStream is=new FileInputStream(LoggerXML);    
            Document doc=dombuilder.parse(is);        

			Element root = doc.getDocumentElement(); // 获取根元素
			String typeContain = root.getElementsByTagName("TypeContain").item(0).getFirstChild().getNodeValue().toString();

			loggerRule = new LoggerRule(typeContain.equals("True") ? LoggerType.Contain
					: LoggerType.Ignore);
			
			NodeList nodeList = root.getElementsByTagName("ProtoHead");
			Node node1, node2;
			NamedNodeMap namedNodeMap;
			String nodeValue;
			for (int i=0; i<nodeList.getLength(); i++) {
				node1 = nodeList.item(i);
				namedNodeMap = node1.getAttributes();
				for (int j=0; j<namedNodeMap.getLength(); j++) {
					node2 = namedNodeMap.item(j);
					if (node2.getNodeName().equals("logType")) {
						nodeValue = node1.getFirstChild().getNodeValue().toString();
						if (node2.getNodeValue().equals("Network")) {	// 显示服务器发了什么包的Log
							loggerRule.loggerSet.add(ProtoHead.ENetworkMessage.valueOf(nodeValue));
						} else if (node2.getNodeValue().equals("class")) {
							loggerRule.logCalssList.add(nodeValue);
						}
					}	
				}
			}
		} catch (Exception e) {
			logger.error("MyLogger : load " + LoggerXML + " file error!\n" + MyException.getStackStr(e.getStackTrace()));
		}
	}
	
class LoggerRule {
	public static enum LoggerType {
		Contain, Ignore
	};

	public LoggerType loggerType;
	HashSet<ProtoHead.ENetworkMessage> loggerSet;
	public ArrayList<String> logCalssList;

	public LoggerRule(LoggerType loggerType) {
		this.loggerType = loggerType;
		loggerSet = new HashSet<ProtoHead.ENetworkMessage>();
		logCalssList = new ArrayList<String>();
	}
}

通过上面这个方法,我们从配置文件中读出了我们制定的输出规则,存储在一个ArrayList中,接着,只要进行以此遍历,将指定的Logger关掉即可:

public void closeLoggerNotWant(){
	for (String className : loggerRule.logCalssList)
		Logger.getLogger(className).setAdditivity(false);
}

优点

通过这种方式,我们实现了配置编程,在不修改项目源码的前提下,我们可以通过修改配置文件的方式来设置哪些Logger不惊醒输出。甚至,在对上述程序做一些修改后,可以动态的进行变化(如:定时读取XML),即可以在不重启整个进程的前提下修改输出设置。

欢迎阅读我的开源项目《迷你微信》服务器《迷你微信》客户端

posted @ 2015-05-15 08:38  枫露霜阳  Views(1609)  Comments(2Edit  收藏  举报
View My Stats