/* Copyright (c) 2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.metrics;
import static org.locationtech.geogig.metrics.MetricsModule.COMMAND_STACK_LOGGER;
import static org.locationtech.geogig.metrics.MetricsModule.METRICS_ENABLED;
import static org.locationtech.geogig.metrics.MetricsModule.METRICS_LOGGER;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.locationtech.geogig.api.AbstractGeoGigOp;
import org.locationtech.geogig.api.Platform;
import org.locationtech.geogig.api.hooks.CannotRunGeogigOperationException;
import org.locationtech.geogig.api.hooks.CommandHook;
import org.locationtech.geogig.api.porcelain.ConfigException;
import org.locationtech.geogig.api.porcelain.ConfigException.StatusCode;
import org.locationtech.geogig.storage.ConfigDatabase;
public class MeteredCommandHook implements CommandHook {
private static final double toMillisFactor = 1.0 / TimeUnit.MILLISECONDS.toNanos(1L);
/**
* @return {@code true}, applies to all ops
*/
@Override
public boolean appliesTo(Class<? extends AbstractGeoGigOp<?>> clazz) {
return true;
}
@Override
public <C extends AbstractGeoGigOp<?>> C pre(C command)
throws CannotRunGeogigOperationException {
Boolean enabled;
if (command.context().repository() == null) {
return command;
}
ConfigDatabase configDb = command.context().configDatabase();
try {
enabled = configDb.get(METRICS_ENABLED, Boolean.class).or(Boolean.FALSE);
} catch (ConfigException e) {
if (StatusCode.INVALID_LOCATION.equals(e.statusCode)) {
enabled = Boolean.FALSE;
} else {
throw e;
}
}
if (!enabled.booleanValue()) {
return command;
}
final Platform platform = command.context().platform();
final long startTime = platform.currentTimeMillis();
final long nanoTime = platform.nanoTime();
final String name = command.getClass().getSimpleName();
CallStack stack = CallStack.push(name, startTime, nanoTime);
command.getClientData().put("metrics.callStack", stack);
return command;
}
@SuppressWarnings("unchecked")
@Override
public <T> T post(AbstractGeoGigOp<T> command, @Nullable Object retVal,
@Nullable RuntimeException exception) throws Exception {
CallStack stack = (CallStack) command.getClientData().get("metrics.callStack");
if (stack == null || command.context().repository() == null) {
return (T) retVal;
}
final Platform platform = command.context().platform();
long endTime = platform.nanoTime();
boolean success = exception == null;
stack = CallStack.pop(endTime, success);
long ellapsed = stack.getEllapsedNanos();
double millis = endTime * toMillisFactor;
METRICS_LOGGER.info("{}, {}, {}, {}", stack.getName(), stack.getStartTimeMillis(), millis,
success);
if (stack.isRoot()) {
COMMAND_STACK_LOGGER.info("{}", stack.toString(TimeUnit.MILLISECONDS));
}
return (T) retVal;
}
}