/*
* Copyright 2014 the original author or authors.
*
* 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 de.codecentric.batch.metrics;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.scope.context.StepContext;
import org.springframework.batch.core.scope.context.StepSynchronizationManager;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.util.ClassUtils;
import org.springframework.util.StopWatch;
/**
* This is a helper class for implementing method level profiling. See {@link ReaderProcessorWriterMetricsAspect} for an
* aspect extending this class. All calls to an adviced method are tracked in a RichGauge, so you'll see average duration time,
* maximum / minimum time, number of method calls and so on. For the name of the metric a special naming scheme is used so that
* our {@link MetricsListener} picks up the gauge and writes it to the ExecutionContext of the StepExecution and to the log.
*
* Job configurations need to enable auto-proxying so that aspects may be applied. In JavaConfig just add
* {@code @EnableAspectJAutoProxy(proxyTargetClass=true)} as a class level annotation. In xml add
* {@code <aop:aspectj-autoproxy proxy-target-class="true"/>} to the xml configuration file. This needs to be done because jobs reside
* in child application contexts and don't inherit this kind of configuration from the parent. proxyTargetClass=true means using
* CGLIB as proxy mechanism which allows us to proxy classes without interfaces.
*
* @author Tobias Flohre
*/
public abstract class AbstractBatchMetricsAspect {
private GaugeService gaugeService;
public static final String TIMER_PREFIX = "timer.batch.";
public AbstractBatchMetricsAspect(GaugeService gaugeService) {
this.gaugeService = gaugeService;
}
protected Object profileMethod(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = startStopWatch();
try {
return pjp.proceed();
} finally {
gaugeService.submit(TIMER_PREFIX + getStepIdentifier() + "." + ClassUtils.getShortName(pjp.getTarget().getClass()) + "."
+ pjp.getSignature().getName(), getTotalTimeMillis(stopWatch));
}
}
private long getTotalTimeMillis(StopWatch stopWatch) {
stopWatch.stop();
long duration = stopWatch.getTotalTimeMillis();
return duration;
}
private StopWatch startStopWatch() {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
return stopWatch;
}
private String getStepIdentifier() {
StepContext stepContext = StepSynchronizationManager.getContext();
StepExecution stepExecution = StepSynchronizationManager.getContext().getStepExecution();
return stepContext.getJobName() + "." + stepExecution.getStepName();
}
}