随笔 - 19, 文章 - 1, 评论 - 21, 引用 - 0
数据加载中……

打造一个基于OSGi的Web Application——增加日志输出功能

到目前为止,我们的基于OSGi内核的Web Application还没有任何的日志输出功能,本章将介绍如何在这个Web应用中配置和输出日志。

在前面的配置中,我们的应用中只含有commons-logging.jar,而OSGi容器之外的代码中,均是通过配置commons logging的Log对象来输出日志的,在默认的配置下,系统将采用Jdk14Logger来作为输出日志的实现,这对我们来说是远远不够的。我们下一步将配置更加常用的Log4j在作为我们的日志输出实现,通过以下几个步骤:

一、为Web Application配置Log4j:
  1.在OSGi-Web项目的Java EE Module Dependencies中,增加对log4j.jar的依赖关系。
  2.在WEB-INF/config目录中,增加一个log4j.properties文件,内容如下:
 1 ### direct log messages to stdout ###
 2 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
 3 log4j.appender.stdout.Target=System.out
 4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
 5 log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%- %m%n
 6 
 7 #Default Log File Configuration For OSGi
 8 log4j.appender.OSGiLog=org.apache.log4j.DailyRollingFileAppender
 9 log4j.appender.OSGiLog.DatePattern='.'yyyy-MM-dd
10 log4j.appender.OSGiLog.File=${osgi.root}/logs/OSGi.log
11 log4j.appender.OSGiLog.layout=org.apache.log4j.PatternLayout
12 log4j.appender.OSGiLog.layout.ConversionPattern=%d [%t] %-5p %- %m%n
13 
14 log4j.rootLogger=info, stdout
15 
16 log4j.logger.org.dbstar=debug, OSGiLog
17 log4j.logger.org.eclipse=debug, OSGiLog
  3.采用Spring Web的Log4j配置方式,在web.xml中增加如下配置:
 1     <!-- Log4j configuration -->
 2     <context-param>
 3         <param-name>webAppRootKey</param-name>
 4         <param-value>osgi.root</param-value>
 5     </context-param>
 6     <context-param>
 7         <param-name>log4jConfigLocation</param-name>
 8         <param-value>/WEB-INF/config/log4j.properties</param-value>
 9     </context-param>
10     
11     <!-- Init log4j -->
12     <listener>
13         <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
14     </listener>
  4.在OSGi-Web项目的Java EE Module Dependencies中,增加spring相关jar的依赖。

经过以上4个步骤,我们在Web Application中使用commons logging输出的日志,都可以通过Log4j来显示了。但是作为OSGi容器内部来说,这还不够。OSGi规范中推荐使用org.osgi.service.log包中的LogService和LogReaderService来管理和显示OSGi日志。为了能正常显示OSGi容器内部的日志,我们还需要将LogService、LogReaderService和OSGi容器外部的Log4j结合起来才行,为了达到这个目的,我们还需要做以下几个步骤:
  1.为OSGi容器增加一个org.osgi.service.log的实现包。在equinox-SDK-3.6M5开发包中,这个实现jar是:org.eclipse.equinox.log_1.2.100.v20100118.jar,当然,还需要org.eclipse.osgi.services_3.2.100.v20100108.jar,都放置到OSGi-Web工程的WEB-INT/osgi/plugins目录下面。
  2.为OSGi容器增加Declarative Services支持。在equinox-SDK-3.6M5开发包中,包含了一个DS的实 现:org.eclipse.equinox.ds_1.2.0.v20100125.jar,将这个jar和一个依赖的 jar:org.eclipse.equinox.util_1.0.100.v20090520-1800.jar部署到OSGi容器中,就可以使用 DS服务了。同样也放到plugins目录下面去。
  3.新增一个plugin工程,名字为:org.dbstar.osgi.log,我们使用DS方式来获取服务,相关源代码如下:
    OSGI-INF/log.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" enabled="true" name="logListener" xsi:schemaLocation="http://www.osgi.org/xmlns/scr/v1.1.0 http://www.osgi.org/xmlns/scr/v1.1.0/scr.xsd">
3   <implementation class="org.dbstar.osgi.log.LogListenerImpl"/>
4   <reference cardinality="1..1" interface="org.osgi.service.log.LogReaderService" name="LogReaderService" policy="static"/>
5 </scr:component>
    LogListenerImpl.java
 1 package org.dbstar.osgi.log;
 2 
 3 import org.apache.commons.logging.Log;
 4 import org.apache.commons.logging.LogFactory;
 5 import org.osgi.service.component.ComponentContext;
 6 import org.osgi.service.log.LogEntry;
 7 import org.osgi.service.log.LogListener;
 8 import org.osgi.service.log.LogReaderService;
 9 import org.osgi.service.log.LogService;
10 
11 public class LogListenerImpl implements LogListener {
12     private static final Log logger = LogFactory.getLog(LogListenerImpl.class);
13 
14     protected void activate(ComponentContext context) {
15         LogReaderService service = (LogReaderService) context.locateService("LogReaderService");
16         service.addLogListener(this);
17     }
18 
19     protected void deactivate(ComponentContext context) {
20         LogReaderService service = (LogReaderService) context.locateService("LogReaderService");
21         service.removeLogListener(this);
22     }
23 
24     public void logged(LogEntry entry) {
25         String msg = getMessage(entry);
26 
27         switch (entry.getLevel()) {
28         case LogService.LOG_DEBUG:
29             if (logger.isDebugEnabled()) {
30                 if (entry.getException() == null) {
31                     logger.debug(msg);
32                 } else {
33                     logger.debug(msg, entry.getException());
34                 }
35             }
36             break;
37         case LogService.LOG_INFO:
38             if (logger.isInfoEnabled()) {
39                 if (entry.getException() == null) {
40                     logger.info(msg);
41                 } else {
42                     logger.info(msg, entry.getException());
43                 }
44             }
45             break;
46         case LogService.LOG_WARNING:
47             if (logger.isWarnEnabled()) {
48                 if (entry.getException() == null) {
49                     logger.warn(msg);
50                 } else {
51                     logger.warn(msg, entry.getException());
52                 }
53             }
54             break;
55         case LogService.LOG_ERROR:
56             if (logger.isErrorEnabled()) {
57                 if (entry.getException() == null) {
58                     logger.error(msg);
59                 } else {
60                     logger.error(msg, entry.getException());
61                 }
62             }
63             break;
64         }
65     }
66 
67     private String getMessage(LogEntry entry) {
68         StringBuilder msg = new StringBuilder();
69         if (entry.getBundle() != null) msg.append("[bundle:").append(entry.getBundle()).append("]");
70         if (entry.getServiceReference() != null) msg.append("[service:").append(entry.getServiceReference())
71                 .append("]");
72         msg.append(entry.getMessage());
73         return msg.toString();
74     }
75 }
    META-INF/MANIFEST.MF
 1 Manifest-Version: 1.0
 2 Bundle-ManifestVersion: 2
 3 Bundle-Name: Log Bundle
 4 Bundle-SymbolicName: org.dbstar.osgi.log
 5 Bundle-Version: 1.0.0
 6 Bundle-Vendor: dbstar
 7 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 8 Service-Component: OSGI-INF/log.xml
 9 Import-Package: org.apache.commons.logging;version="1.0.4",
10  org.osgi.framework;version="1.3.0",
11  org.osgi.service.component;version="1.1.0",
12  org.osgi.service.log;version="1.3.0"
好了,打包成bundle jar然后也扔到plugins目录下面,然后clean一下server,启动,现在能看到多了许多日志输出,现在OSGi内部通过LogService输出的日志也能由Log4j接管了。

最后总结一下,LogService和LogReaderService是OSGi规范中提倡的日志标准,在equinox内部实现中大量使用了这种日志,而commons logging是我们开发常规程序时所常用的日志方式。在你的bundle代码中,具体要采用哪一种日志方式,并没有强制的要求,大家可以根据各人喜好来选用。
顺便提一句,LogService有些美中不足的是,不能像commons logging那样,显示出日志具体是从哪个java类的第几行输出的,不知道各位大虾是否有人知道该如何解决呢,希望不吝赐教:)

posted on 2010-03-27 00:28 dbstar 阅读(3325) 评论(3)  编辑  收藏 所属分类: OSGi

评论

# re: 打造一个基于OSGi的Web Application——增加日志输出功能  回复  更多评论   

实际上把log4j应用在osgi领域是完全可能的,也很简单,为何舍近求远呢...如果粗糙一点设计,可以把log4j.properties作为fragment,如果做的精致一些,log4j.properties放在哪都行,然后启动时动态生成一个fragment就好了
2010-03-27 12:03 | romza

# re: 打造一个基于OSGi的Web Application——增加日志输出功能  回复  更多评论   

@romza
这么做的目的,主要也是为了bundle中使用log的代码不耦合与commons logging、Log4j,或者其他类似的实现。之所以采用OSGi的LogService,一方面,这是OSGi提出的日志标准,在equinox的实现代码中,这也是事实标准;另一方面,这种日志方式确实更加灵活,其抽象的API更适合于OSGi环境。将Log4j配置在OSGi之外,也是为了降低这种耦合。
2010-03-27 13:08 | dbstar

# re: 打造一个基于OSGi的Web Application——增加日志输出功能  回复  更多评论   

你好!
根据博主的博客进行了osgi日志的配置,但是在写日志的时候提示log4j的配置没有加载。
2013-09-02 17:04 | 菜鸟老了

只有注册用户登录后才能发表评论。


网站导航: