打造一个基于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文件,内容如下:
经过以上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
最后总结一下,LogService和LogReaderService是OSGi规范中提倡的日志标准,在equinox内部实现中大量使用了这种日志,而commons logging是我们开发常规程序时所常用的日志方式。在你的bundle代码中,具体要采用哪一种日志方式,并没有强制的要求,大家可以根据各人喜好来选用。
顺便提一句,LogService有些美中不足的是,不能像commons logging那样,显示出日志具体是从哪个java类的第几行输出的,不知道各位大虾是否有人知道该如何解决呢,希望不吝赐教:)
在前面的配置中,我们的应用中只含有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 }: % L - % 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 % c - % 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中增加如下配置:
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 }: % L - % 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 % c - % m % n
13
14 log4j.rootLogger = info, stdout
15
16 log4j.logger.org.dbstar = debug, OSGiLog
17 log4j.logger.org.eclipse = debug, OSGiLog
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的依赖。
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个步骤,我们在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
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 >
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
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 }
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接管了。
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"
最后总结一下,LogService和LogReaderService是OSGi规范中提倡的日志标准,在equinox内部实现中大量使用了这种日志,而commons logging是我们开发常规程序时所常用的日志方式。在你的bundle代码中,具体要采用哪一种日志方式,并没有强制的要求,大家可以根据各人喜好来选用。
顺便提一句,LogService有些美中不足的是,不能像commons logging那样,显示出日志具体是从哪个java类的第几行输出的,不知道各位大虾是否有人知道该如何解决呢,希望不吝赐教:)