TigrisCoordinator.java
Home
/
tigris /
src /
main /
java /
br /
ufrgs /
inf /
prosoft /
tigris /
monitoring /
aspects /
TigrisCoordinator.java
package br.ufrgs.inf.prosoft.tigris.monitoring.aspects;
import br.ufrgs.inf.prosoft.tigris.configuration.annotation.ComponentScan;
import br.ufrgs.inf.prosoft.tigris.configuration.annotation.TigrisConfiguration;
import br.ufrgs.inf.prosoft.tigris.configuration.annotation.TigrisCriteria;
import br.ufrgs.inf.prosoft.tigris.exceptions.ConfigurationException;
import br.ufrgs.inf.prosoft.tigris.metrics.DataFiltering;
import br.ufrgs.inf.prosoft.tigris.metrics.LightweightMetrics;
import br.ufrgs.inf.prosoft.tigris.metrics.StaticMetrics;
import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.Key;
import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.LogTrace;
import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.MethodInfo;
import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
import br.ufrgs.inf.prosoft.tigris.monitoring.storage.RepositoryFactory;
import br.ufrgs.inf.prosoft.tigris.monitoring.util.threads.NamedThreads;
import br.ufrgs.inf.prosoft.tigris.sampling.Granularity;
import br.ufrgs.inf.prosoft.tigris.sampling.Sampling;
import br.ufrgs.inf.prosoft.tigris.utils.ConfigurationUtils;
import ca.uqac.lif.bullwinkle.BnfParser;
import ca.uqac.lif.bullwinkle.ParseNode;
import com.google.common.io.Resources;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import static java.lang.System.currentTimeMillis;
public class TigrisCoordinator implements Runnable {
private static boolean enabled = true;
private boolean coarseMonitoringEnabled = true;
Logger logger = LoggerFactory.getLogger(TigrisCoordinator.class);
//traceable configuration
private Pattern allowedPattern;
private Pattern deniedPattern;
//tigris config
private final TigrisConfiguration tigrisConfiguration;
private final TigrisCriteria tigrisCriteria;
private final Sampling sampling;
private final Repository repository;
private Class<?> customMonitoringClass;
private CustomMonitoring customMonitoring;
private Map<String, LightweightMetrics> metrics;
private StaticMetrics staticMetrics;
private final BnfParser parser = new BnfParser(Resources.getResource("tigrisdsl.bnf").openStream());
private final ScheduledExecutorService samplingAdaptationExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreads(
"sampling-adaptation",
"adapting sampling rate and sample readiness"
));
private final ScheduledExecutorService lightweightAnalyzerExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreads(
"lightweight-analyzer",
"computing lightweight metrics and setting allowed methods"
));
public TigrisCoordinator() throws IOException, BnfParser.InvalidGrammarException {
Class<?> configClass = ConfigurationUtils.getAvailableConfigurationClass(TigrisConfiguration.class);
if (configClass == null) {
turnoff();
logger.info("Tigris tracing disabled, there is no annotations.");
throw new ConfigurationException("Tigris tracing disabled, there is no annotations.");
}
tigrisConfiguration = configClass.getAnnotation(TigrisConfiguration.class);
//getting criteria
tigrisCriteria = configClass.getAnnotation(TigrisCriteria.class);
//getting allowed packages from @ComponentScan
ComponentScan componentScanConfig = configClass.getAnnotation(ComponentScan.class);
if (componentScanConfig == null) {
//if not specified, it assumes the same package where @AdaptiveCaching were declared
allowedPattern = Pattern.compile(configClass.getPackage().getName() + ".*");
logger.info("ComponentScan for TigrisConfiguration not found, assuming the same package where @TigrisConfiguration were declared.");
}
allowedPattern = Pattern.compile(componentScanConfig.allowed());
deniedPattern = Pattern.compile(componentScanConfig.denied());
logger.info("@TigrisConfiguration will trace and cache methods into {} and deny {} package.", allowedPattern.pattern(), deniedPattern.pattern());
sampling = new Sampling(tigrisConfiguration.samplingPercentage(), tigrisConfiguration.cycleTimeInSeconds());
samplingAdaptationExecutor.scheduleWithFixedDelay(
sampling, 120000, 450000, TimeUnit.MILLISECONDS);
repository = RepositoryFactory.getRepository(null, tigrisConfiguration.logRepository());
if (tigrisConfiguration.clearMonitoringDataOnStart()) {
repository.removeAll();
logger.debug("Repository starting cleaned.");
}
if (coarseMonitoringEnabled) {
metrics = new ConcurrentHashMap<>();
staticMetrics = new StaticMetrics(tigrisConfiguration.staticMetricFile());
lightweightAnalyzerExecutor.scheduleWithFixedDelay(
this::run,
120000, 450000, TimeUnit.MILLISECONDS);
}
try {
ParseNode node = parser.parse(tigrisCriteria.criteria());
//recursive mapping between DSL and values
assert node.getChildren() != null;
} catch (BnfParser.ParseException ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
try {
customMonitoringClass = ConfigurationUtils.getAvailableCustomMonitoringClass();
if (customMonitoringClass != null) {
customMonitoring = (CustomMonitoring) customMonitoringClass.newInstance();
}
customMonitoring.initialize(repository);
} catch (ConfigurationException e) {
logger.info("AdaptiveCaching not found, disabling...");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
public boolean isAllowed(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return
isEnabled()
&& !signature.getName().contains("br.ufrgs.inf.prosoft")
&& allowedPattern.matcher(signature.getMethod().getDeclaringClass().getPackage().getName()).matches()
&& !deniedPattern.matcher(signature.getMethod().getDeclaringClass().getPackage().getName()).matches();
}
public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
String signature = joinPoint.getSignature().toString();
boolean detailedTrace = (!coarseMonitoringEnabled ||
(coarseMonitoringEnabled && allowedFineGrained
.contains(signature)));
logger.debug("Evaluating {}. Allowed events {}", signature, allowedFineGrained);
//generate a hash of the method that will be used as: key to cache and compare if the method is allowed or not
Key key = new Key(joinPoint);
boolean customProcess = customMonitoring != null &&
customMonitoring.beforeExecuting(key);
if (customProcess) {
Object returnResult = customMonitoring.returnResult(key);
if (returnResult != null)
return returnResult;
}
Object[] joinPointArgs = null;
//method calls can change the args, so it is better to get it at the beginning
if (detailedTrace)
joinPointArgs = joinPoint.getArgs();
long startTime = currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = currentTimeMillis();
if (coarseMonitoringEnabled) {
LightweightMetrics metric = metrics.get(signature);
if (metric == null) {
metric = new LightweightMetrics(signature);
metric.setStaticMetric(staticMetrics.getMetrics(metric.getName()));
}
metric.addTime(startTime, endTime - startTime);
metric.addReturnSize(result);
metric.incOccurrence();
metrics.put(signature, metric);
}
Granularity granularity = new Granularity(tigrisCriteria.granularity(), signature);
if (sampling.isPerformanceBaselineEnabled()) {
sampling.addPerformanceBaselineItem(granularity, endTime - startTime);
}
//trace only allowed by lightweight metrics
logger.debug("Is coarse monitoring enable? {}", coarseMonitoringEnabled);
logger.debug("Is {} in the allowed list? {}", signature, detailedTrace);
boolean shouldSample = sampling.simpleSamplingDecision();
logger.debug("Should sample it? {} ", shouldSample);
if (coarseMonitoringEnabled
&& detailedTrace
&& shouldSample) {
logger.debug("New trace: " + signature);
//we do not cache null returns, but we trace them
//maybe the method can sometimes return null... so there is not verification here
LogTrace logTrace = new LogTrace();
logTrace.setStartTime(startTime);
logTrace.setEndTime(endTime);
MethodInfo methodInfo = new MethodInfo(signature, joinPointArgs, result, key);
logTrace.setMethodInfo(methodInfo);
try {
repository.save(logTrace);
logger.debug("New trace entry: " + logTrace);
} catch (Exception e) {
logger.debug("Couldn't trace " + signature + " due to: " + e.getMessage());
}
}
if (detailedTrace && customProcess && result != null) {
customMonitoring.afterExecuting(key, result);
}
return result;
}
public void turnoff() {
enabled = false;
}
public static boolean isEnabled() {
return enabled;
}
public Repository getRepository() {
return repository;
}
/**
* The constant allowedFineGrained.
*/
public static Set<String> allowedFineGrained = new ConcurrentSkipListSet<>();
@Override
public void run() {
if (coarseMonitoringEnabled) {
logger.info("Analyzing {} lightweight methods for relevance...", metrics.size());
//cleaning selected methods and recalculating relevance
allowedFineGrained = DataFiltering.calculate(staticMetrics, metrics);
metrics.clear();
repository.removeAll();
logger.info("Selected methods for fine-grained ({}): {}", allowedFineGrained.size(), allowedFineGrained);
//analyze once
// coarseMonitoringEnabled = false;
}
}
}