added automatic metrics for micrometer

This commit is contained in:
lucio 2023-02-01 17:18:13 +01:00
parent a76b823c49
commit e48f50e91b
9 changed files with 126 additions and 89 deletions

View File

@ -16,9 +16,11 @@ import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
@ -95,12 +97,14 @@ public class Bootstrap implements ServletContainerInitializer {
context = ProviderFactory.provider().containerContext();
PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
new ClassLoaderMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
//new JvmGcMetrics().bindTo(registry);
new JvmGcMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new JvmThreadMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
Metrics.addRegistry(registry);

View File

@ -98,6 +98,11 @@ public class Constants {
*/
public static final String request_validation = "request-validation";
/**
* The configuration name of {@link RequestMetrics}s.
*/
public static final String request_metrics = "request-metrics";
/**
* The configuration name of {@link RequestValidator}s.
*/

View File

@ -19,6 +19,7 @@ import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.handlers.application.lifecycle.ApplicationProfileManager;
import org.gcube.smartgears.handlers.application.request.RequestAccounting;
import org.gcube.smartgears.handlers.application.request.RequestMetrics;
import org.gcube.smartgears.handlers.application.request.RequestValidator;
/**
@ -66,6 +67,7 @@ public class ApplicationConfigurationBinder {
List<RequestHandler> requestHandlers = new LinkedList<RequestHandler>();
//ADDING BASE Handler (order is important)
requestHandlers.add(new RequestMetrics());
requestHandlers.add(new RequestValidator());
requestHandlers.add(new RequestAccounting());

View File

@ -6,7 +6,6 @@ import org.gcube.common.events.Hub;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.health.HealthManager;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;

View File

@ -22,6 +22,10 @@ public abstract class RequestHandler extends AbstractHandler implements Applicat
abstract public String getName();
public boolean isUnfiltrable() {
return false;
}
/**
* Initialises the handler.
*

View File

@ -83,8 +83,9 @@ public class ApplicationProfileManager extends ApplicationLifecycleHandler {
publishers = context.container().configuration().mode()!=Mode.offline?
ProviderFactory.provider().publishers():
Collections.emptyList();
registerObservers();
schedulePeriodicUpdates();
registerObservers();
schedulePeriodicUpdates();
}
// helpers

View File

@ -1,7 +1,5 @@
package org.gcube.smartgears.handlers.application.request;
import java.util.concurrent.TimeUnit;
import org.gcube.accounting.datamodel.UsageRecord.OperationResult;
import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord;
import org.gcube.accounting.persistence.AccountingPersistence;
@ -17,8 +15,6 @@ import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Metrics;
public class RequestAccounting extends RequestHandler {
private static Logger log = LoggerFactory.getLogger(RequestAccounting.class);
@ -36,24 +32,24 @@ public class RequestAccounting extends RequestHandler {
public void handleRequest(RequestEvent e) {
ApplicationContext appContext = e.context();
String context = getContext(appContext);
if (InnerMethodName.instance.get()==null) {
if (InnerMethodName.instance.get() == null) {
String calledMethod = e.request().getRequestURI().substring(e.request().getContextPath().length());
if (calledMethod.isEmpty())
calledMethod = "/";
calledMethod= e.request().getMethod()+" "+calledMethod;
calledMethod = e.request().getMethod() + " " + calledMethod;
InnerMethodName.instance.set(calledMethod);
}
String caller = SecretManagerProvider.instance.get()!=null ? SecretManagerProvider.instance.get().getOwner().getId():
UNKNOWN;
String caller = SecretManagerProvider.instance.get() != null
? SecretManagerProvider.instance.get().getOwner().getId()
: UNKNOWN;
startCallThreadLocal.set(System.currentTimeMillis());
log.info("REQUEST START ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} ",
appContext.configuration().name(),appContext.configuration().serviceClass(), InnerMethodName.instance.get(),
caller, e.request().getRemoteHost(), context );
log.info("REQUEST START ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} ", appContext.configuration().name(),
appContext.configuration().serviceClass(), InnerMethodName.instance.get(), caller,
e.request().getRemoteHost(), context);
}
@ -65,33 +61,41 @@ public class RequestAccounting extends RequestHandler {
String context = getContext(appContext);
String caller = SecretManagerProvider.instance.get()!=null ? SecretManagerProvider.instance.get().getOwner().getId():
UNKNOWN;
String caller = SecretManagerProvider.instance.get() != null
? SecretManagerProvider.instance.get().getOwner().getId()
: UNKNOWN;
String callerQualifier = UNKNOWN;
//retieves caller Ip when there is a proxy
// retieves caller Ip when there is a proxy
String callerIp = e.request().getHeader("x-forwarded-for");
if(callerIp==null)
callerIp=e.request().getRemoteHost();
if (callerIp == null)
callerIp = e.request().getRemoteHost();
boolean success = e.response().getStatus()<400;
boolean success = e.response().getStatus() < 400;
if (appContext.container().configuration().mode()!=Mode.offline)
generateAccounting(caller,callerQualifier,callerIp==null?UNKNOWN:callerIp , success, context, appContext);
if (appContext.container().configuration().mode() != Mode.offline)
generateAccounting(caller, callerQualifier, callerIp == null ? UNKNOWN : callerIp, success, context,
appContext);
long durationInMillis = System.currentTimeMillis()-startCallThreadLocal.get();
long durationInMillis = System.currentTimeMillis() - startCallThreadLocal.get();
Metrics.globalRegistry.timer("http.requests", "response",Integer.toString(e.response().getStatus())
, "context", context, "result", success?"SUCCEDED":"FAILED", "caller-ip", callerIp,
"caller-username", caller, "service-class", appContext.configuration().serviceClass(), "service-name", appContext.configuration().name(),
"method", InnerMethodName.instance.get()).record(durationInMillis, TimeUnit.MILLISECONDS);
/*
* Metrics.globalRegistry.timer("smartgears.requests",
* "response",Integer.toString(e.response().getStatus()) , "context", context,
* "result", success?"SUCCEDED":"FAILED", "caller-ip", callerIp,
* "caller-username", caller, "service-class",
* appContext.configuration().serviceClass(), "service-name",
* appContext.configuration().name(), "method",
* InnerMethodName.instance.get()).record(durationInMillis,
* TimeUnit.MILLISECONDS);
*/
log.info("REQUEST SERVED ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} {}(CODE {}) IN {} millis",
appContext.configuration().name(),appContext.configuration().serviceClass(), InnerMethodName.instance.get(),
caller, callerIp, context, success?"SUCCEDED":"FAILED", e.response().getStatus(),durationInMillis);
appContext.configuration().name(), appContext.configuration().serviceClass(),
InnerMethodName.instance.get(), caller, callerIp, context, success ? "SUCCEDED" : "FAILED",
e.response().getStatus(), durationInMillis);
}catch (Exception e1) {
log.error("error on accounting",e);
} catch (Exception e1) {
log.error("error on accounting", e);
throw e1;
} finally {
startCallThreadLocal.remove();
@ -100,11 +104,13 @@ public class RequestAccounting extends RequestHandler {
}
void generateAccounting(String caller, String callerQualifier, String remoteHost, boolean success, String gcubeContext, ApplicationContext appContext){
AccountingPersistenceFactory.setFallbackLocation(appContext.container().configuration().accountingFallbackLocation());
void generateAccounting(String caller, String callerQualifier, String remoteHost, boolean success,
String gcubeContext, ApplicationContext appContext) {
AccountingPersistenceFactory
.setFallbackLocation(appContext.container().configuration().accountingFallbackLocation());
AccountingPersistence persistence = AccountingPersistenceFactory.getPersistence();
ServiceUsageRecord serviceUsageRecord = new ServiceUsageRecord();
try{
try {
serviceUsageRecord.setConsumerId(caller);
serviceUsageRecord.setCallerQualifier(callerQualifier);
@ -112,21 +118,22 @@ public class RequestAccounting extends RequestHandler {
serviceUsageRecord.setServiceClass(appContext.configuration().serviceClass());
serviceUsageRecord.setServiceName(appContext.configuration().name());
serviceUsageRecord.setHost(appContext.container().configuration().hostname()+":"+appContext.container().configuration().port());
serviceUsageRecord.setHost(appContext.container().configuration().hostname() + ":"
+ appContext.container().configuration().port());
serviceUsageRecord.setCalledMethod(InnerMethodName.instance.get());
serviceUsageRecord.setCallerHost(remoteHost);
serviceUsageRecord.setOperationResult(success?OperationResult.SUCCESS:OperationResult.FAILED);
serviceUsageRecord.setDuration(System.currentTimeMillis()-startCallThreadLocal.get());
serviceUsageRecord.setOperationResult(success ? OperationResult.SUCCESS : OperationResult.FAILED);
serviceUsageRecord.setDuration(System.currentTimeMillis() - startCallThreadLocal.get());
persistence.account(serviceUsageRecord);
}catch(Exception ex){
log.warn("invalid record passed to accounting ",ex);
} catch (Exception ex) {
log.warn("invalid record passed to accounting ", ex);
}
}
private String getContext(ApplicationContext appContext) {
String infrastructure = appContext.container().configuration().infrastructure();
String context= "/"+infrastructure;
String context = "/" + infrastructure;
if (SecretManagerProvider.instance.get() != null)
context = SecretManagerProvider.instance.get().getContext();
return context;
@ -137,6 +144,4 @@ public class RequestAccounting extends RequestHandler {
return getName();
}
}

View File

@ -0,0 +1,48 @@
package org.gcube.smartgears.handlers.application.request;
import java.time.Duration;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.handlers.application.ResponseEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Metrics;
public class RequestMetrics extends RequestHandler {
private static Logger log = LoggerFactory.getLogger(RequestMetrics.class);
private static ThreadLocal<Long> startCallThreadLocal = new ThreadLocal<Long>();
private static final String HTTP_REQUEST_METRICS_NAME = "http.server.requests";
@Override
public String getName() {
return Constants.request_metrics;
}
@Override
public boolean isUnfiltrable() {
return true;
}
@Override
public void handleRequest(RequestEvent e) {
startCallThreadLocal.set(System.currentTimeMillis());
}
@Override
public void handleResponse(ResponseEvent e) {
String statusCode = Integer.toString(e.response().getStatus());
Metrics.globalRegistry.timer(HTTP_REQUEST_METRICS_NAME, "status", statusCode).record(Duration.ofMillis(System.currentTimeMillis() - startCallThreadLocal.get()));
Metrics.globalRegistry.counter(HTTP_REQUEST_METRICS_NAME, "status", statusCode);
startCallThreadLocal.remove();
log.debug("metrics for current call saved");
}
}

View File

@ -8,6 +8,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@ -139,19 +140,18 @@ public class RequestManager implements Filter {
for (Exclude exclude : context.configuration().excludes()){
String excludePath= exclude.getPath();
log.trace("exclude is {}",exclude);
if (
(WILDCARD).equals(excludePath) ||
if ((WILDCARD).equals(excludePath) ||
(excludePath.endsWith(WILDCARD) && path!=null && path.startsWith(excludePath.substring(0,excludePath.length()-2))) ||
excludePath.equals(path) || (path.endsWith("/") && excludePath.equals(path.substring(0, path.length()-1)))
){
//ALL handler are filtered
if (exclude.getHandlers().isEmpty()) return Collections.emptyList();
//ALL handler are filtered except for unfiltrable
if (exclude.getHandlers().isEmpty()) return handlersToFilter.stream().filter(RequestHandler::isUnfiltrable).toList();
List<RequestHandler> filteredHandlers = new ArrayList<>();
for (RequestHandler rh : handlersToFilter){
if (!exclude.getHandlers().contains(rh.getName()))
for (RequestHandler rh : handlersToFilter)
if (rh.isUnfiltrable() || !exclude.getHandlers().contains(rh.getName()))
filteredHandlers.add(rh);
}
return filteredHandlers;
}
}
@ -168,10 +168,10 @@ public class RequestManager implements Filter {
if (include.getHandlers().isEmpty()) return handlersToFilter;
List<RequestHandler> filteredHandlers = new ArrayList<>();
for (RequestHandler rh : handlersToFilter){
if (include.getHandlers().contains(rh.getName()))
for (RequestHandler rh : handlersToFilter)
if (rh.isUnfiltrable() || include.getHandlers().contains(rh.getName()))
filteredHandlers.add(rh);
}
return filteredHandlers;
}
}
@ -213,37 +213,6 @@ public class RequestManager implements Filter {
}
// helpers
/*
private boolean shouldExcludeRequest(HttpServletRequest request) {
String query = request.getQueryString();
log.debug("servletPath is {} and pathInfo is {}",request.getServletPath(), request.getPathInfo());
if ("wsdl".equals(query) || "wsdl=1".equals(query))
return true;
String path = request.getServletPath()==null?"":request.getServletPath();
path += request.getPathInfo() ==null?"":request.getPathInfo();
log.debug("check if should exclude call with path {}", path);
for (Exclude exclude : context.configuration().excludes()){
if (!exclude.getHandlers().isEmpty()) continue;
String excludePath= exclude.getPath();
log.trace("exclude is {}",exclude);
if (
(EXCLUDE_ALL).equals(exclude) ||
(excludePath.endsWith(EXCLUDE_ALL) && path!=null && path.startsWith(excludePath.substring(0,excludePath.length()-2))) ||
excludePath.equals(path) || (path.endsWith("/") && excludePath.equals(path.substring(0, path.length()-1)))
)
return true;
}
return false;
}*/
private void handleError(HttpServletRequest request, HttpServletResponse response,Throwable t) throws IOException {