Platinum Partner
java,osgi,frameworks,log4j,logging,tools & methods,server-side,servicefactory

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>
{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}