package org.gcube.smartgears.handlers.application.request; import org.gcube.accounting.datamodel.UsageRecord.OperationResult; import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord; import org.gcube.accounting.persistence.AccountingPersistence; import org.gcube.accounting.persistence.AccountingPersistenceFactory; import org.gcube.common.security.providers.SecretManagerProvider; import org.gcube.smartgears.Constants; import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.handlers.application.RequestEvent; import org.gcube.smartgears.handlers.application.RequestHandler; import org.gcube.smartgears.handlers.application.ResponseEvent; import org.gcube.smartgears.utils.InnerMethodName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RequestAccounting extends RequestHandler { private static Logger log = LoggerFactory.getLogger(RequestAccounting.class); private static ThreadLocal startCallThreadLocal = new ThreadLocal(); private static final String UNKNOWN = "Unknown"; @Override public String getName() { return Constants.request_accounting; } @Override public void handleRequest(RequestEvent e) { ApplicationContext appContext = e.context(); String context = getContext(appContext); if (InnerMethodName.get() == null) { String calledMethod = e.request().getRequestURI().substring(e.request().getContextPath().length()); if (calledMethod.isEmpty()) calledMethod = "/"; calledMethod = e.request().getMethod() + " " + calledMethod; InnerMethodName.set(calledMethod); } String caller = SecretManagerProvider.get() != null ? SecretManagerProvider.get().getOwner().getId() : UNKNOWN; startCallThreadLocal.set(System.currentTimeMillis()); log.info("REQUEST START ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} ", appContext.configuration().name(), appContext.configuration().group(), InnerMethodName.get(), caller, e.request().getRemoteHost(), context); } @Override public void handleResponse(ResponseEvent e) { ApplicationContext appContext = e.context(); try { String context = getContext(appContext); String caller = SecretManagerProvider.get() != null ? SecretManagerProvider.get().getOwner().getId() : UNKNOWN; String callerQualifier = UNKNOWN; // retieves caller Ip when there is a proxy String callerIp = e.request().getHeader("x-forwarded-for"); if (callerIp == null) callerIp = e.request().getRemoteHost(); boolean success = e.response().getStatus() < 400; if (appContext.container().configuration().mode() != Mode.offline) generateAccounting(caller, callerQualifier, callerIp == null ? UNKNOWN : callerIp, success, context, appContext); long durationInMillis = System.currentTimeMillis() - startCallThreadLocal.get(); /* * 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().group(), InnerMethodName.get(), caller, callerIp, context, success ? "SUCCEDED" : "FAILED", e.response().getStatus(), durationInMillis); } catch (Exception e1) { log.error("error on accounting", e); throw e1; } finally { startCallThreadLocal.remove(); InnerMethodName.reset(); } } 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 { serviceUsageRecord.setConsumerId(caller); serviceUsageRecord.setCallerQualifier(callerQualifier); serviceUsageRecord.setScope(gcubeContext); serviceUsageRecord.setServiceClass(appContext.configuration().group()); serviceUsageRecord.setServiceName(appContext.configuration().name()); serviceUsageRecord.setHost(appContext.container().configuration().hostname() + ":" + appContext.container().configuration().port()); serviceUsageRecord.setCalledMethod(InnerMethodName.get()); serviceUsageRecord.setCallerHost(remoteHost); 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); } } private String getContext(ApplicationContext appContext) { String infrastructure = appContext.container().configuration().infrastructure(); String context = "/" + infrastructure; if (SecretManagerProvider.get() != null) context = SecretManagerProvider.get().getContext(); return context; } @Override public String toString() { return getName(); } }