I'm quite confused on how to properly setup a logger in an Java EE OSGi environment. Here are my requirements:
First I tried to use SLF4j on top of log4j like our other Java EE applications do, however nothing could find my log4j.properties. I tried variations of importing SLF4j, and also had issues where loading it in 1 bundle prevented it from loading in another.
Next I looked into PAX logger, but it appears to log globally, not per EBA.
Attempting to use the OSGi LogService prevents my bundle from deploying to WebSphere, plus I'm not sure how I could get it to meet my requirements anyway.
I'm at the point where the only option I can see is to write my own bundle that keeps a registry for bundle -> log file (using FrameworkUtil.getBundle on the client class) and implement a full logging framework within it. If that has classloader isolation issues then possibly push to an EJB to do the actual logging. I'm really hoping that's not my only solution.
Can anyone point me to some documentation that will help me out??
Thank you!
People are in general confused about the Log Service ... The Log Service does not store any logs, it just acts as a dispatcher. I understand the confusion since the Log Service is mandated to have a small buffer for the initial start up and provides an API to get the buffer.
For what you want, you should add a Log Listener with the Log Reader services. What you want is quite easy with Declarative Services. This is a component that implements your requirements :
@Component(provide = {}, immediate = true) public class Logger extends Thread
implements LogListener {
final BlockingQueue<LogEntry> queue = new ArrayBlockingQueue<LogEntry>(1000);
File root;
@Reference void setLR(LogReaderService lr) {
lr.addLogListener(this);
}
@Activate void activate(BundleContext context, Map<String,Object> props) {
if ( props.containsKey("root"))
root = new File((String) props.get("root"));
else
root = context.getDataFile("logs");
root.mkdirs();
start();
}
@Deactivate void deactivate() {
interrupt();
}
@Override public void logged(LogEntry entry) {
queue.offer(entry); // ignore full silently
}
public void run() {
while (true)
try {
LogEntry entry = queue.take();
File file = getPath(entry);
if (file.isFile()) {
long days = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()
- file.lastModified());
if (days > 2) file.delete();
}
try (OutputStream raf = new FileOutputStream(file, true)) {
String s = String.format("%tT [%03d] %s%n", entry.getTime(), entry
.getBundle().getBundleId(), entry.getMessage());
raf.write(s.getBytes("UTF-8"));
}
} catch (InterruptedException ie) {
return;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private File getPath(LogEntry entry) {
long rollover = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()) % 7;
String eba = "eba"; // fill in eba from entry.getBundle()?
return new File(root, "log-" + eba + "-" + rollover + ".msg");
}
}
This could of course be done a bit more efficient, but that is left as an exercise.