Osgi中对于Log的提供,在规范中规定了几种方式,felix也提供了一个LogService实现osgi中的日志输出。在利用这个日志是遇到了两个方面的问题,一是LogService的可管理功能比较弱,二是对于大多数的java开发人员而言,log4j是一个既好用又熟悉的工具了,为什么不继续利用log4j作为自己的日志输出工具呢?
在Osgi的bundle中,是可以直接利用传统的jar包的!只要把这个jar包放在bundle的classpath中。利用这个方式下面对log4j做了一个proxy的bundle,并发布出来一个日志服务,这样,在其他的bundle中只要引用这个服务,就可以利用熟悉的log4j日志的功能了。
首先是建立一个bundle:org.osgi.log.proxy,利用eclipse的向导建立就可以了。具体实现的步骤如下:
1、建立一个lib目录,在其中加入log4j和它的支持jar包,然后,把它们加入到MANIFEST.MF文件的bundle-classpath中:
Bundle-ClassPath: lib/log4j-2.1.8.jar, ., lib/xmlParserAPIs-2.6.2.jar, lib/xercesImpl-2.6.2.jar, lib/
2、建立一个log4j.xml配置文件,放在lib目录下,这样在bundle中log4j才能够找到这个文件。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" > <log4j:configuration> <appender name="logfile" class="org.apache.log4j.RollingFileAppender"> <param name="File" value="d:/temp/felix_log4j.log"/> <param name="MaxFileSize" value="4096KB"/> <param name="MaxBackupIndex" value="20"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %p [%c] - %m%n"/> </layout> </appender> <appender name="stdout" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %p [%c] - %m%n" /> </layout> </appender> <logger name = "com.example.time.web.action" additivity = "false"> <level value = "DEBUG"/> <appender-ref ref = "logfile"/> </logger> <root> <level value="DEBUG"/> <appender-ref ref="logfile" /> <appender-ref ref="stdout" /> </root> </log4j:configuration>
3、建立一个log4j日志的代理接口,这个接口是应用这个日志的服务的入口,由于是利用log4j 输出日志,接口中定义的函数和log4j中名字完成一致,只是在输入参数中加了一个日志输出位置,这样可以保证输出的结果和log4j完成相同:
package org.osgi.log.proxy.log;
public interface Log4jProxy {
public void trace(Object message,String position);
public void trace(Object message, Throwable t,String position);
public void debug(Object message,String position);
public void debug(Object message, Throwable t,String position);
public void info(Object message,String position);
public void info(Object message, Throwable t,String position);
public void warn(Object message,String position);
public void warn(Object message, Throwable t,String position);
public void error(Object message,String position);
public void error(Object message, Throwable t,String position);
public void fatal(Object message,String position);
public void fatal(Object message, Throwable t,String position);
}
4、实现Log4J的代理服务,在一个代理的实现类中,对上面的接口中定义的方法进行实现,完成对于log4j函数的调用。在其中做了一个Classloader的转换,在普通的OSGI环境中,这一步是不需要的,但是在把Felix嵌入到其它的应用中时,并且调用的应用中已经应用了Log4j,这样的转换就非常必要了:
package org.osgi.log.proxy.log.impl;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.osgi.log.proxy.Activator;
import org.osgi.log.proxy.log.Log4jProxy;
public class Log4jProxyImpl implements Log4jProxy {
private final String TRACE = "trace";
private final String DEBUG = "debug";
private final String INFO = "info";
private final String WARN = "warn";
private final String ERROR = "error";
private final String FATAL = "fatal";
private void executeLog(String level,Object message,String position)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(Activator.class.getClassLoader());
Logger log = LogManager.getLogger(position);
if(level.equals(this.DEBUG))
{
log.debug(message);
}
else if(level.equals(this.INFO))
{
log.info(message);
}
else if(level.equals(this.WARN))
{
log.info(message);
}
else if(level.equals(this.ERROR))
{
log.error(message);
}
else if(level.equals(this.FATAL))
{
log.error(message);
}
else
{
log.debug(message);
}
Thread.currentThread().setContextClassLoader(cl);
}
private void executeLog(String level,Object message,Throwable t,String position)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(Activator.class.getClassLoader());
Logger log = LogManager.getLogger(position);
if(level.equals(this.DEBUG))
{
log.debug(message,t);
}
else if(level.equals(this.INFO))
{
log.info(message,t);
}
else if(level.equals(this.WARN))
{
log.info(message,t);
}
else if(level.equals(this.ERROR))
{
log.error(message,t);
}
else if(level.equals(this.FATAL))
{
log.error(message,t);
}
else
{
log.debug(message,t);
}
Thread.currentThread().setContextClassLoader(cl);
}
@Override
public void debug(Object message, String position) {
// TODO Auto-generated method stub
this.executeLog(this.DEBUG, message, position);
}
@Override
public void debug(Object message, Throwable t, String position) {
// TODO Auto-generated method stub
this.executeLog(this.DEBUG, message, t, position);
}
@Override
public void error(Object message, String position) {
// TODO Auto-generated method stub
this.executeLog(this.ERROR, message, position);
}
@Override
public void error(Object message, Throwable t, String position) {
// TODO Auto-generated method stub
this.executeLog(this.ERROR, message, t, position);
}
@Override
public void fatal(Object message, String position) {
// TODO Auto-generated method stub
this.executeLog(this.FATAL, message, position);
}
@Override
public void fatal(Object message, Throwable t, String position) {
// TODO Auto-generated method stub
this.executeLog(this.FATAL, message, t, position);
}
@Override
public void info(Object message, String position) {
// TODO Auto-generated method stub
this.executeLog(this.INFO, message, position);
}
@Override
public void info(Object message, Throwable t, String position) {
// TODO Auto-generated method stub
this.executeLog(this.INFO, message, t, position);
}
@Override
public void trace(Object message, String position) {
// TODO Auto-generated method stub
this.executeLog(this.TRACE, message, position);
}
@Override
public void trace(Object message, Throwable t, String position) {
// TODO Auto-generated method stub
this.executeLog(this.TRACE, message, t, position);
}
@Override
public void warn(Object message, String position) {
// TODO Auto-generated method stub
this.executeLog(this.WARN, message, position);
}
@Override
public void warn(Object message, Throwable t, String position) {
// TODO Auto-generated method stub
this.executeLog(this.WARN, message, t, position);
}
}
5、在Activator的Start方法中,注册日志服务:
context.registerService(Log4jProxy.class.getName(), new Log4jProxyImpl() , null);
6、这个日志服务在其他的bundle中的应用方式为:
ServiceTracker st = new ServiceTracker(context,Log4jProxy.class.getName(),null);
st.open();
Log4jProxy log = (Log4jProxy) st.getService();
log.debug("felix log use call log4jProxy", this.getClass().getName());