Service Factory and OSGI Logging Proposal
I've been doing some thinking lately about how can we log different bundles or the same bundle with different versions with different log level. Logging is an important part of any application thus Osgi gives developers logService to use when possible, but standard logging api (slf4j, log4j) are simple to use.
Why I'm asking this question :
1 -Working with osgi is a bit different regarding simple java programming: The same class can exist on many bundle with different version (different loader).
2- Making an upgrade on some bundles by changing the bundle versions : we should leave the two running versions if some other bundles are not updated to use the new milestone version.
I try here to present ServiceFactory utilities and I will present some brainstorming about possible logging enhancement :
ServiceFactory :
We create here three bundles :
1- logger : contains the api and impl
2- client 1 : using logger service with version 1, as stable implementation the log level is ERROR
3- client 2 : using logger service with version 2.0.0.m1 as unstable implementation the log level is Debug to catch all messages.
Here is the draft implementation of logger class :
package com.jtunisie.osgi.logger.impl;
import com.jtunisie.osgi.logger.Level;
import java.util.Dictionary;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.Version;
public class Logger implements com.jtunisie.osgi.logger.Logger, ServiceFactory {
@Override
public void log(Class clazz, Level level, String msg, Object... params) {
}
@Override
public Object getService(Bundle bundle, ServiceRegistration arg1) {
Dictionary dictionary = bundle.getHeaders();
Version version = Version.parseVersion((String)dictionary.get(org.osgi.framework.Constants.BUNDLE_VERSION));
String name = (String) dictionary.get(org.osgi.framework.Constants.BUNDLE_NAME);
com.jtunisie.osgi.logger.Logger logger = getLogger(name, version);
return logger;
}
private com.jtunisie.osgi.logger.Logger getLogger(String name, Version version) {
if (version.toString().contains("m")) {
return new ErrorLogger();
} else {
return new DebugLogger();
}
}
@Override
public void ungetService(Bundle arg0, ServiceRegistration arg1, Object arg2) {
}
}
package com.jtunisie.osgi.logger.impl;
import com.jtunisie.osgi.logger.Level;
class ErrorLogger implements com.jtunisie.osgi.logger.Logger {
@Override
public void log(Class clazz, Level level, String msg, Object... params) {
if (level.compareTo(Level.ERROR) == 0) {
msg = LoggerUtils.processMsg(params, msg);
System.out.println("----" +clazz+ " :>"+LoggerUtils.getMessage()+"//"+msg);
}
}
}
package com.jtunisie.osgi.logger.impl;
import com.jtunisie.osgi.logger.Level;
class DebugLogger implements com.jtunisie.osgi.logger.Logger{
@Override
public void log(Class clazz, Level level, String msg, Object... params) {
msg = LoggerUtils.processMsg(params, msg);
System.out.println("----" + clazz + " :>"+ LoggerUtils.getMessage()+"//"+msg);
}
}
client 1 and 2 are to simple activator class as
//client 1
package com.jtunisie.osgi.logger.client;
import com.jtunisie.osgi.logger.*;
import static com.jtunisie.osgi.logger.Level.*;
public class Activator {
private Logger logger;
public Activator(Logger logger) {
this.logger = logger;
}
public void init(){
logger.log(Activator.class ,DEBUG, "DEBUG : 1>>>>>My Id is ## version is ## ", 1,1);
logger.log(Activator.class ,ERROR, "ERROR : 1>>>>>My Id is ## version is ## ", 1,1);
}
}
package com.jtunisie.osgi.logger.client;
import com.jtunisie.osgi.logger.*;
import static com.jtunisie.osgi.logger.Level.*;
public class Activator {
private Logger logger;
public Activator(Logger logger) {
this.logger = logger;
}
public void init(){
logger.log(Activator.class ,DEBUG, "DEBUG : 2>>>>>My Id is ## version is ## ", 2,2);
logger.log(Activator.class ,ERROR, "ERROR : 2>>>>>My Id is ## version is ## ", 2,2);
}
}
if we run these examples the out put will be like :
----class com.jtunisie.osgi.logger.client.Activator :>Wed Oct 08 03:25:18 CEST 2008//ERROR : 2>>>>>My Id is 2 version is 2
----class com.jtunisie.osgi.logger.client.Activator :>Wed Oct 08 03:25:18 CEST 2008//DEBUG : 1>>>>>My Id is 1 version is 1
----class com.jtunisie.osgi.logger.client.Activator :>Wed Oct 08 03:25:18 CEST 2008//ERROR : 1>>>>>My Id is 1 version is 1
==> ServiceFactory gives new implementation per bundle client depends on it's version.
Logging proposal :
Many considerations should be taken when osgi is your framework :
-Class implementation can be found in different bundles version with same package version.
-Class implementation can be found in different bundles version with different package version.
log4j.dtd does not have the notion of bundle and version and my proposition is :
<!ELEMENT log4j:configuration (renderer*, appender*,(category|logger)*,root?,
categoryFactory?)>
Will be
<!ELEMENT log4j:configuration (renderer*, appender*,(bundle|category|logger)*,root?,
categoryFactory?)>
and
we add version to category and logger element
so our log4j.xml file could be :
<category name="com.jtunisie.osgi.logger.client">
<priority value="ERROR" version="[0.0.0,2.0.0("/>
<priority value="DEBUG" version="2.0.0"/>
</category>
or in our case
<bundle name="com.jtunisie.osgi.client">
<priority value="ERROR" version="[0.0.0,1.0.0("/>
<priority value="DEBUG" version="2.0.0"/>
</bundle>
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Bernd Eckenfels replied on Thu, 2008/10/09 - 8:27pm
Hanen Ben Rhouma replied on Fri, 2008/10/10 - 5:04pm
Interesting post Slim but needs further patience by your side to clarify many details that may seem parachuted for an OSGi newbie.Taking into consideration your project arborescence, your building and deployment steps are mandatory to keep your reader on the right direction, otherwise more than what OSGi word still hold with strangeness for some, your jumping "to the target" won't make things easier for them neither.
PS: maybe you can host your example sources on any SVN or CVS server and share it with us for pertinent feedbacks
Slim Ouertani replied on Sun, 2008/10/12 - 8:48am
in response to:
Hanen Ben Rhouma
thanks,
your are right. I haven't a lot of time to do it clear, it was to late to more explain my purpose. thing are more precise in my blog http://www.jroller.com/ouertani/
and for svn source they are available under this URL :
svn checkout http://logosgi.googlecode.com/svn/trunk/ logosgi-read-only