/* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. Copyright (c) 2016 Payara Foundation. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (collectively, the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html or packager/legal/LICENSE.txt. See the License for the specific language governing permissions and limitations under the License. When distributing the software, include this License Header Notice in each file and include the License file at packager/legal/LICENSE.txt. */ package fish.payara.nucleus.requesttracing; import com.sun.enterprise.config.serverbeans.Domain; import com.sun.enterprise.config.serverbeans.Server; import fish.payara.nucleus.notification.NotificationService; import fish.payara.nucleus.notification.configuration.Notifier; import fish.payara.nucleus.requesttracing.configuration.RequestTracingServiceConfiguration; import fish.payara.nucleus.requesttracing.domain.EventType; import fish.payara.nucleus.requesttracing.domain.RequestEvent; import fish.payara.nucleus.requesttracing.domain.execoptions.NotifierExecutionOptions; import fish.payara.nucleus.requesttracing.domain.execoptions.NotifierExecutionOptionsFactory; import fish.payara.nucleus.requesttracing.domain.execoptions.RequestTracingExecutionOptions; import org.glassfish.api.StartupRunLevel; import org.glassfish.api.admin.ServerEnvironment; import org.glassfish.api.event.EventListener; import org.glassfish.api.event.EventTypes; import org.glassfish.api.event.Events; import org.glassfish.hk2.runlevel.RunLevel; import org.jvnet.hk2.annotations.Optional; import org.jvnet.hk2.annotations.Service; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; /** * @author mertcaliskan * Main service class that provides methods used by interceptors for tracing requests. */ @Service(name = "requesttracing-service") @RunLevel(StartupRunLevel.VAL) public class RequestTracingService implements EventListener { private static final Logger logger = Logger.getLogger(RequestTracingService.class.getCanonicalName()); @Inject @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME) @Optional RequestTracingServiceConfiguration configuration; @Inject private Events events; @Inject private Domain domain; @Inject private Server server; @Inject NotificationService notificationService; @Inject RequestEventStore requestEventStore; @Inject private RequestTracingNotificationEventFactory eventFactory; @Inject NotifierExecutionOptionsFactory executionOptionsFactory; private RequestTracingExecutionOptions executionOptions = new RequestTracingExecutionOptions(); @PostConstruct void postConstruct() { if (configuration != null) { executionOptions.setEnabled(Boolean.parseBoolean(configuration.getEnabled())); executionOptions.setThresholdValue(Long.parseLong(configuration.getThresholdValue())); executionOptions.setThresholdUnit(TimeUnit.valueOf(configuration.getThresholdUnit())); for (Notifier notifier : configuration.getNotifierList()) { executionOptions.addNotifierExecutionOption(executionOptionsFactory.build(notifier)); } } events.register(this); } @Override public void event(Event event) { if (event.is(EventTypes.SERVER_READY)) { bootstrapRequestTracingService(); } } private void bootstrapRequestTracingService() { if (executionOptions != null && executionOptions.isEnabled()) { logger.info("Payara Request Tracing Service Started with configuration: " + executionOptions); } } /** * Retrieves the current Conversation ID * @return */ public UUID getConversationID() { return requestEventStore.getConversationID(); } /** * Reset the conversation ID * This is especially useful for trace propagation across threads when * the event tracer can receive the conversation ID propagated to it * @param newID */ public void setConversationID(UUID newID) { requestEventStore.setConverstationID(newID); } public boolean isTraceInProgress() { return requestEventStore.isTraceInProgress(); } public UUID startTrace() { if (!isRequestTracingEnabled()) { return null; } RequestEvent requestEvent = new RequestEvent(EventType.TRACE_START, "StartTrace"); requestEvent.addProperty("Server", server.getName()); requestEvent.addProperty("Domain", domain.getName()); requestEventStore.storeEvent(requestEvent); return requestEvent.getId(); } public void traceRequestEvent(RequestEvent requestEvent) { if (isRequestTracingEnabled()) { requestEventStore.storeEvent(requestEvent); } } public void endTrace() { if (!isRequestTracingEnabled()) { return; } requestEventStore.storeEvent(new RequestEvent(EventType.TRACE_END, "TraceEnd")); Long thresholdValueInNanos = getThresholdValueInNanos(); long elapsedTime = requestEventStore.getElapsedTime(); long elapsedTimeInNanos = TimeUnit.NANOSECONDS.convert(elapsedTime, TimeUnit.MILLISECONDS); if (elapsedTimeInNanos - thresholdValueInNanos > 0) { for (NotifierExecutionOptions notifierExecutionOptions : getExecutionOptions().getNotifierExecutionOptionsList().values()) { if (notifierExecutionOptions.isEnabled()) { notificationService.notify(eventFactory.build(elapsedTime, notifierExecutionOptions.getNotifierType())); } } } requestEventStore.flushStore(); } public Long getThresholdValueInNanos() { if (getExecutionOptions() != null) { return TimeUnit.NANOSECONDS.convert(getExecutionOptions().getThresholdValue(), getExecutionOptions().getThresholdUnit()); } return null; } public boolean isRequestTracingEnabled() { return getExecutionOptions() != null && getExecutionOptions().isEnabled(); } public RequestTracingExecutionOptions getExecutionOptions() { return executionOptions; } }