/*******************************************************************************
* Copyright 2015 Software Evolution and Architecture Lab, University of Zurich
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
******************************************************************************/
package eu.cloudwave.wp5.monitoring.tracing;
import java.lang.management.ManagementFactory;
import java.util.Collection;
import eu.cloudwave.wp5.common.debug.ProcedureExecutionPrinter;
import eu.cloudwave.wp5.common.model.MetricType;
import eu.cloudwave.wp5.common.model.factories.MetricFactory;
import eu.cloudwave.wp5.common.model.factories.MetricFactoryImpl;
import eu.cloudwave.wp5.common.model.impl.MetricTypeImpl;
import eu.cloudwave.wp5.monitoring.aop.handlers.AbstractAroundJoinPointHandlerTemplate;
import eu.cloudwave.wp5.monitoring.aop.joinpoints.ProcedureCallJoinPoint;
import eu.cloudwave.wp5.monitoring.debug.DebugMode;
import eu.cloudwave.wp5.monitoring.dto.RunningProcedureExecution;
import eu.cloudwave.wp5.monitoring.rest.FeedbackHandlerMonitoringClient;
/**
* A join-point handler that handles methods annotated with the Trace annotation.
*/
public class TracingHandler extends AbstractAroundJoinPointHandlerTemplate {
private RunningProcedureExecution runningProcedureExecution;
private long beforeUpTime;
private long beforeCpuTime;
private MetricFactory metricFactory;
private TracingHandler() {
this.metricFactory = new MetricFactoryImpl();
}
/**
* {@inheritDoc}
*/
@Override
protected void before(final ProcedureCallJoinPoint joinPoint) {
startProcedureExecution(joinPoint);
setBeforeTimes();
}
private void startProcedureExecution(final ProcedureCallJoinPoint joinPoint) {
runningProcedureExecution = new RunningProcedureExecution(joinPoint.getTargetProcedure(), System.currentTimeMillis());
addParametersCollectionSizes(joinPoint);
TraceStorage.INSTANCE.add(runningProcedureExecution);
}
private void setBeforeTimes() {
beforeUpTime = getCurrentUpTime();
beforeCpuTime = getCurrentCpuTime();
}
/**
* {@inheritDoc}
*/
@Override
protected void after(final ProcedureCallJoinPoint joinPoint, final Object result) {
addMetrics(result);
finish();
}
private void addMetrics(final Object result) {
addGeneralMetrics();
addReturnTypeCollectionSize(result);
}
private void addGeneralMetrics() {
addMetric(MetricTypeImpl.EXECUTION_TIME, executionTime());
addMetric(MetricTypeImpl.CPU_USAGE, cpuUsage());
}
private void addReturnTypeCollectionSize(final Object result) {
if (result instanceof Collection<?>) {
final int size = ((Collection<?>) result).size();
addMetric(MetricTypeImpl.COLLECTION_SIZE, size);
}
}
private void addParametersCollectionSizes(final ProcedureCallJoinPoint joinPoint) {
for (int i = 0; i < joinPoint.getArgs().length; i++) {
final Object argument = joinPoint.getArgs()[i];
if (argument instanceof Collection<?>) {
final int size = ((Collection<?>) argument).size();
addMetric(MetricTypeImpl.COLLECTION_SIZE, String.valueOf(i), size);
}
}
}
private void addMetric(final MetricType type, final Number value) {
runningProcedureExecution.addMetric(metricFactory.create(type, runningProcedureExecution, value));
}
private void addMetric(final MetricType type, final String additionalQualifier, final Number value) {
runningProcedureExecution.addMetric(metricFactory.create(type, runningProcedureExecution, additionalQualifier, value));
}
private void finish() {
runningProcedureExecution.finish();
setCaller();
sendDataIfFinished();
}
private void setCaller() {
final RunningProcedureExecution caller = TraceStorage.INSTANCE.getLastNotFinished();
if (caller != null) {
caller.addCallee(runningProcedureExecution);
}
runningProcedureExecution.setCaller(caller);
}
private void sendDataIfFinished() {
if (TraceStorage.INSTANCE.isFinished()) {
if (!TraceStorage.INSTANCE.getCallTrace().isEmpty()) {
final RunningProcedureExecution rootProcedureExecution = TraceStorage.INSTANCE.getCallTrace().get(0);
new Thread(new Runnable() {
@Override
public void run() {
if (DebugMode.isActive()) {
printData(rootProcedureExecution);
}
else {
System.out.println("Monitoring: Sending data to feedback handler");
sendData(rootProcedureExecution);
}
}
}).start();
}
TraceStorage.INSTANCE.clear();
}
}
private void sendData(final RunningProcedureExecution rootProcedureExecution) {
final boolean success = new FeedbackHandlerMonitoringClient().postData(rootProcedureExecution);
if (!success) {
printData(rootProcedureExecution);
}
}
private void printData(final RunningProcedureExecution rootProcedureExecution) {
ProcedureExecutionPrinter.print(rootProcedureExecution, true);
}
private long executionTime() {
return getCurrentUpTime() - beforeUpTime;
}
private float cpuUsage() {
final long executionTime = executionTime();
final long cpuTime = getCurrentCpuTime() - beforeCpuTime;
final int availableProcessors = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
if (executionTime > 0) {
return Math.min(99F, cpuTime / (executionTime * 10000F * availableProcessors));
}
return Float.NaN;
}
private long getCurrentCpuTime() {
return ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime();
}
private long getCurrentUpTime() {
return ManagementFactory.getRuntimeMXBean().getUptime();
}
public static TracingHandler of() {
return new TracingHandler();
}
}