package org.springframework.roo.felix;
import static org.apache.commons.io.IOUtils.LINE_SEPARATOR;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.ReferenceStrategy;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.log.LogEntry;
import org.osgi.service.log.LogListener;
import org.osgi.service.log.LogReaderService;
import org.osgi.service.log.LogService;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.shell.osgi.AbstractFlashingObject;
import org.springframework.roo.support.logging.HandlerUtils;
/**
* Delegates OSGi log messages to the JDK logging infrastructure. This in turn
* makes it compatible with Spring Roo's standard approach to log messages
* appearing on the console.
* <p>
* For convenience all low-priority messages are output as flash messages. All
* high priority messages are sent to the JDK logger.
*
* @author Ben Alex
* @author Stefan Schmidt
* @since 1.1
*/
@Component
@Reference(name = "shell", strategy = ReferenceStrategy.EVENT, policy = ReferencePolicy.DYNAMIC,
referenceInterface = Shell.class, cardinality = ReferenceCardinality.OPTIONAL_UNARY)
public class JdkDelegatingLogListener extends AbstractFlashingObject implements LogListener {
public static final String DO_NOT_LOG = "#DO_NOT_LOG";
private final static Logger LOGGER = HandlerUtils.getLogger(JdkDelegatingLogListener.class);
@Reference(policy = ReferencePolicy.DYNAMIC)
private volatile LogReaderService logReaderService;
public static String cleanThrowable(final Throwable throwable) {
final StringBuilder result = new StringBuilder();
result.append(LINE_SEPARATOR);
result.append(throwable.toString().replace(DO_NOT_LOG, ""));
result.append(LINE_SEPARATOR);
for (final StackTraceElement ste : throwable.getStackTrace()) {
result.append(ste);
result.append(LINE_SEPARATOR);
}
return result.toString();
}
@SuppressWarnings("unchecked")
protected void activate(final ComponentContext context) {
logReaderService.addLogListener(this);
final Enumeration<LogEntry> latestLogs = logReaderService.getLog();
if (latestLogs.hasMoreElements()) {
logNow(latestLogs.nextElement(), false);
}
}
private String buildMessage(final LogEntry entry) {
final StringBuilder sb = new StringBuilder();
sb.append("[").append(entry.getBundle()).append("] ").append(entry.getMessage());
return sb.toString();
}
private boolean containsDoNotLogTag(final Throwable throwable) {
if (throwable == null) {
return false;
}
if (throwable.getMessage().contains(DO_NOT_LOG)) {
return true;
}
final StringWriter sw = new StringWriter();
throwable.printStackTrace(new PrintWriter(sw));
return sw.toString().contains(DO_NOT_LOG);
}
protected void deactivate(final ComponentContext context) {
logReaderService.removeLogListener(this);
}
public void logged(final LogEntry entry) {
if (containsDoNotLogTag(entry.getException())) {
// Only log Felix stack trace in development mode, discard log
// otherwise
if (isDevelopmentMode()) {
logNow(entry, true);
}
} else {
logNow(entry, false);
}
}
private void logNow(final LogEntry entry, final boolean removeDoNotLogTag) {
final int osgiLevel = entry.getLevel();
Level jdkLevel = Level.FINEST;
// Convert the OSGi level into a JDK logger level
if (osgiLevel == LogService.LOG_DEBUG) {
jdkLevel = Level.FINE;
} else if (osgiLevel == LogService.LOG_INFO) {
jdkLevel = Level.INFO;
} else if (osgiLevel == LogService.LOG_WARNING) {
jdkLevel = Level.WARNING;
} else if (osgiLevel == LogService.LOG_ERROR) {
jdkLevel = Level.SEVERE;
}
if (jdkLevel.intValue() <= Level.INFO.intValue()) {
// Not very important message, so just flash it if possible and
// we're in development mode
if (isDevelopmentMode()) {
flash(jdkLevel, buildMessage(entry), MY_SLOT);
// Immediately clear it once the timeout has been reached
flash(jdkLevel, "", MY_SLOT);
}
} else {
// Important log message, so log it via JDK
if (removeDoNotLogTag) {
LOGGER.log(jdkLevel, buildMessage(entry) + cleanThrowable(entry.getException()));
} else {
LOGGER.log(jdkLevel, buildMessage(entry), entry.getException());
}
}
}
}