Merge branch 'maint-rel-1.6' into develop
This commit is contained in:
commit
b821750734
4 changed files with 361 additions and 1 deletions
|
@ -23,6 +23,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ModelSerialization
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ResultFormat;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ResultFormat;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging.LoggingRDFServiceFactory;
|
||||||
|
|
||||||
public class RDFServiceUtils {
|
public class RDFServiceUtils {
|
||||||
|
|
||||||
|
@ -34,7 +35,18 @@ public class RDFServiceUtils {
|
||||||
|
|
||||||
public static RDFServiceFactory getRDFServiceFactory(ServletContext context) {
|
public static RDFServiceFactory getRDFServiceFactory(ServletContext context) {
|
||||||
Object o = context.getAttribute(RDFSERVICEFACTORY_ATTR);
|
Object o = context.getAttribute(RDFSERVICEFACTORY_ATTR);
|
||||||
return (o instanceof RDFServiceFactory) ? (RDFServiceFactory) o : null;
|
if (o instanceof RDFServiceFactory) {
|
||||||
|
RDFServiceFactory factory = (RDFServiceFactory) o;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Every factory is wrapped in a logger, so we can dynamically
|
||||||
|
* enable or disable logging.
|
||||||
|
*/
|
||||||
|
return new LoggingRDFServiceFactory(context, factory);
|
||||||
|
} else {
|
||||||
|
log.error("Expecting an RDFServiceFactory on the context, but found " + o);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setRDFServiceFactory(ServletContext context,
|
public static void setRDFServiceFactory(ServletContext context,
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This RDFService wrapper adds instrumentation to the time-consuming methods of
|
||||||
|
* the inner RDFService.
|
||||||
|
*
|
||||||
|
* For the other methods, it just delegates to the inner RDFService.
|
||||||
|
*/
|
||||||
|
public class LoggingRDFService implements RDFService {
|
||||||
|
private final ServletContext ctx;
|
||||||
|
private final RDFService innerService;
|
||||||
|
|
||||||
|
LoggingRDFService(ServletContext ctx, RDFService innerService) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.innerService = innerService;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Timed methods
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean changeSetUpdate(ChangeSet changeSet)
|
||||||
|
throws RDFServiceException {
|
||||||
|
try (RDFServiceLogger l = new RDFServiceLogger(ctx, changeSet)) {
|
||||||
|
return innerService.changeSetUpdate(changeSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream sparqlConstructQuery(String query,
|
||||||
|
ModelSerializationFormat resultFormat) throws RDFServiceException {
|
||||||
|
try (RDFServiceLogger l = new RDFServiceLogger(ctx, resultFormat, query)) {
|
||||||
|
return innerService.sparqlConstructQuery(query, resultFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream sparqlDescribeQuery(String query,
|
||||||
|
ModelSerializationFormat resultFormat) throws RDFServiceException {
|
||||||
|
try (RDFServiceLogger l = new RDFServiceLogger(ctx, resultFormat, query)) {
|
||||||
|
return innerService.sparqlDescribeQuery(query, resultFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream sparqlSelectQuery(String query, ResultFormat resultFormat)
|
||||||
|
throws RDFServiceException {
|
||||||
|
try (RDFServiceLogger l = new RDFServiceLogger(ctx, resultFormat, query)) {
|
||||||
|
return innerService.sparqlSelectQuery(query, resultFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sparqlAskQuery(String query) throws RDFServiceException {
|
||||||
|
try (RDFServiceLogger l = new RDFServiceLogger(ctx, query)) {
|
||||||
|
return innerService.sparqlAskQuery(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Untimed methods
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void newIndividual(String individualURI, String individualTypeURI)
|
||||||
|
throws RDFServiceException {
|
||||||
|
innerService.newIndividual(individualURI, individualTypeURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void newIndividual(String individualURI, String individualTypeURI,
|
||||||
|
String graphURI) throws RDFServiceException {
|
||||||
|
innerService.newIndividual(individualURI, individualTypeURI, graphURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getGraphURIs() throws RDFServiceException {
|
||||||
|
return innerService.getGraphURIs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getGraphMetadata() throws RDFServiceException {
|
||||||
|
innerService.getGraphMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultWriteGraphURI() throws RDFServiceException {
|
||||||
|
return innerService.getDefaultWriteGraphURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerListener(ChangeListener changeListener)
|
||||||
|
throws RDFServiceException {
|
||||||
|
innerService.registerListener(changeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterListener(ChangeListener changeListener)
|
||||||
|
throws RDFServiceException {
|
||||||
|
innerService.unregisterListener(changeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChangeSet manufactureChangeSet() {
|
||||||
|
return innerService.manufactureChangeSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
innerService.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the RDFServiceFactory is wrapped in this, then all RDFServices will be
|
||||||
|
* wrapped in a LoggingRDFService.
|
||||||
|
*/
|
||||||
|
public class LoggingRDFServiceFactory implements RDFServiceFactory {
|
||||||
|
private final ServletContext ctx;
|
||||||
|
private final RDFServiceFactory factory;
|
||||||
|
|
||||||
|
public LoggingRDFServiceFactory(ServletContext ctx,
|
||||||
|
RDFServiceFactory factory) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RDFService getRDFService() {
|
||||||
|
return new LoggingRDFService(ctx, factory.getRDFService());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RDFService getShortTermRDFService() {
|
||||||
|
return new LoggingRDFService(ctx, factory.getShortTermRDFService());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerListener(ChangeListener changeListener)
|
||||||
|
throws RDFServiceException {
|
||||||
|
factory.registerListener(changeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterListener(ChangeListener changeListener)
|
||||||
|
throws RDFServiceException {
|
||||||
|
factory.unregisterListener(changeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the log message for the LoggingRDFService.
|
||||||
|
*
|
||||||
|
* If not enabled, or if the logging level is insufficient, this does nothing.
|
||||||
|
*
|
||||||
|
* If enabled, it checks for restrictions. If there is a restriction pattern
|
||||||
|
* (regular expression), the a log message will only be printed if one of the
|
||||||
|
* fully-qualified class names in the stack trace matches that pattern.
|
||||||
|
*
|
||||||
|
* If everything passes muster, the constructor will record the time that the
|
||||||
|
* instance was created.
|
||||||
|
*
|
||||||
|
* When close() is called, if a start time was recorded, then a log record is
|
||||||
|
* produced. This contains the elapsed time, the name of the method, and any
|
||||||
|
* arguments passed to the constructor. It may also include a stack trace, if
|
||||||
|
* requested.
|
||||||
|
*
|
||||||
|
* The stack trace is abbreviated. It will reach into this class, and will not
|
||||||
|
* extend past the first reference to the ApplicationFilterChain. Perhaps it
|
||||||
|
* should be abbreviated further?
|
||||||
|
*/
|
||||||
|
public class RDFServiceLogger implements AutoCloseable {
|
||||||
|
private static final Log log = LogFactory.getLog(RDFServiceLogger.class);
|
||||||
|
|
||||||
|
private static final String PROPERTY_ENABLED = "developer.loggingRDFService.enable";
|
||||||
|
private static final String PROPERTY_STACK_TRACE = "developer.loggingRDFService.stackTrace";
|
||||||
|
private static final String PROPERTY_RESTRICTION = "developer.loggingRDFService.restriction";
|
||||||
|
|
||||||
|
private final ServletContext ctx;
|
||||||
|
private final Object[] args;
|
||||||
|
|
||||||
|
private boolean isEnabled;
|
||||||
|
private boolean traceRequested;
|
||||||
|
private Pattern restriction;
|
||||||
|
|
||||||
|
private String methodName;
|
||||||
|
private List<StackTraceElement> trace = Collections.emptyList();
|
||||||
|
|
||||||
|
private long startTime;
|
||||||
|
|
||||||
|
public RDFServiceLogger(ServletContext ctx, Object... args) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.args = args;
|
||||||
|
|
||||||
|
getProperties();
|
||||||
|
|
||||||
|
if (isEnabled && log.isInfoEnabled()) {
|
||||||
|
loadStackTrace();
|
||||||
|
if (passesRestrictions()) {
|
||||||
|
this.startTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getProperties() {
|
||||||
|
ConfigurationProperties props = ConfigurationProperties.getBean(ctx);
|
||||||
|
isEnabled = Boolean.valueOf(props.getProperty(PROPERTY_ENABLED));
|
||||||
|
traceRequested = Boolean.valueOf(props
|
||||||
|
.getProperty(PROPERTY_STACK_TRACE));
|
||||||
|
|
||||||
|
String restrictionString = props.getProperty(PROPERTY_RESTRICTION);
|
||||||
|
if (StringUtils.isNotBlank(restrictionString)) {
|
||||||
|
try {
|
||||||
|
restriction = Pattern.compile(restrictionString);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to compile the pattern for "
|
||||||
|
+ PROPERTY_RESTRICTION + " = " + restriction + " " + e);
|
||||||
|
isEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadStackTrace() {
|
||||||
|
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
|
||||||
|
List<StackTraceElement> list = new ArrayList<StackTraceElement>(
|
||||||
|
Arrays.asList(stack));
|
||||||
|
|
||||||
|
trimStackTraceAtBeginning(list);
|
||||||
|
trimStackTraceAtEnd(list);
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
this.methodName = "UNKNOWN";
|
||||||
|
} else {
|
||||||
|
this.methodName = list.get(0).getMethodName();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.trace = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void trimStackTraceAtBeginning(List<StackTraceElement> list) {
|
||||||
|
ListIterator<StackTraceElement> iter = list.listIterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
StackTraceElement ste = iter.next();
|
||||||
|
if (ste.getClassName().equals(LoggingRDFService.class.getName())) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void trimStackTraceAtEnd(List<StackTraceElement> list) {
|
||||||
|
ListIterator<StackTraceElement> iter = list.listIterator();
|
||||||
|
boolean trimming = false;
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
StackTraceElement ste = iter.next();
|
||||||
|
if (trimming) {
|
||||||
|
iter.remove();
|
||||||
|
} else if (ste.getClassName().contains("ApplicationFilterChain")) {
|
||||||
|
trimming = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean passesRestrictions() {
|
||||||
|
if (restriction == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (StackTraceElement ste : trace) {
|
||||||
|
if (restriction.matcher(ste.getClassName()).matches()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (startTime != 0L) {
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
float elapsedSeconds = (endTime - startTime) / 1000.0F;
|
||||||
|
String cleanArgs = Arrays.deepToString(args).replaceAll(
|
||||||
|
"[\\n\\r\\t]+", " ");
|
||||||
|
String formattedTrace = formatStackTrace();
|
||||||
|
|
||||||
|
log.info(String.format("%8.3f %s %s %s", elapsedSeconds,
|
||||||
|
methodName, cleanArgs, formattedTrace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatStackTrace() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
if (traceRequested) {
|
||||||
|
for (StackTraceElement ste : trace) {
|
||||||
|
sb.append(String.format("\n %d %s", ste.getLineNumber(),
|
||||||
|
ste.getClassName()));
|
||||||
|
}
|
||||||
|
sb.append("\n ...");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue