Search in sources :

Example 6 with BeforeOperationEvent

use of kieker.common.record.flow.trace.operation.BeforeOperationEvent in project iobserve-analysis by research-iobserve.

the class TraceInterceptor method interceptMethodCall.

/**
 * Around advice configuration.
 *
 * @param context
 *            the invocation context of a bean call.
 * @return the return value of the next method in the chain
 * @throws Throwable
 */
@AroundInvoke
public Object interceptMethodCall(final InvocationContext context) throws Throwable {
    // (IllegalThrowsCheck)
    if (this.monitoringCtrl.isMonitoringEnabled()) {
        final String signature = context.getMethod().toString();
        if (this.monitoringCtrl.isProbeActivated(signature)) {
            // common fields
            TraceMetadata trace = TraceInterceptor.TRACEREGISTRY.getTrace();
            final boolean newTrace = trace == null;
            if (newTrace) {
                trace = TraceInterceptor.TRACEREGISTRY.registerTrace();
                this.monitoringCtrl.newMonitoringRecord(trace);
            }
            final long traceId = trace.getTraceId();
            final String clazz = context.getTarget().getClass().getCanonicalName();
            // measure before execution
            this.monitoringCtrl.newMonitoringRecord(new BeforeOperationEvent(this.timeSource.getTime(), traceId, trace.getNextOrderId(), signature, clazz));
            // execution of the called method
            try {
                final Object retval = context.proceed();
                // measure after successful execution
                this.monitoringCtrl.newMonitoringRecord(new AfterOperationEvent(this.timeSource.getTime(), traceId, trace.getNextOrderId(), signature, clazz));
                return retval;
            } catch (final Throwable th) {
                // NOPMD NOCS (catch throw might
                // ok here)
                // measure after failed execution
                this.monitoringCtrl.newMonitoringRecord(new AfterOperationFailedEvent(this.timeSource.getTime(), traceId, trace.getNextOrderId(), signature, clazz, th.toString()));
                throw th;
            } finally {
                if (newTrace) {
                    // close the trace
                    TraceInterceptor.TRACEREGISTRY.unregisterTrace();
                }
            }
        } else {
            return context.proceed();
        }
    } else {
        return context.proceed();
    }
}
Also used : AfterOperationFailedEvent(kieker.common.record.flow.trace.operation.AfterOperationFailedEvent) AfterOperationEvent(kieker.common.record.flow.trace.operation.AfterOperationEvent) TraceMetadata(kieker.common.record.flow.trace.TraceMetadata) BeforeOperationEvent(kieker.common.record.flow.trace.operation.BeforeOperationEvent) AroundInvoke(javax.interceptor.AroundInvoke)

Example 7 with BeforeOperationEvent

use of kieker.common.record.flow.trace.operation.BeforeOperationEvent in project iobserve-analysis by research-iobserve.

the class SessionAndTraceRegistrationFilterForJPetstore method doFilter.

/**
 * Register thread-local session and trace information, executes the given {@link FilterChain}
 * and unregisters the session/trace information. If configured, the execution of this filter is
 * also logged to the {@link IMonitoringController}. This method returns immediately if
 * monitoring is not enabled.
 *
 * @param request
 *            The request.
 * @param response
 *            The response.
 * @param chain
 *            The filter chain to be used.
 *
 * @throws IOException
 *             on io errors
 * @throws ServletException
 *             on servlet errors
 */
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
    if (SessionAndTraceRegistrationFilterForJPetstore.CTRLINST.isMonitoringEnabled()) {
        // if (CTRLINST.isProbeActivated(this.filterOperationSignatureString)) {
        String operationSignature;
        final String componentSignature;
        final String method;
        final String path;
        final String sessionId;
        final List<CallInformation> callInformations = new ArrayList<>();
        String query = null;
        String[] queryParameters = null;
        if (request instanceof HttpServletRequest) {
            final HttpServletRequest httpRequest = (HttpServletRequest) request;
            method = httpRequest.getMethod();
            final String requestPath = httpRequest.getRequestURI().replace('/', '.').substring(1);
            // remove sessionId from request Path
            path = requestPath.contains(";") ? requestPath.substring(0, requestPath.indexOf(';')) : requestPath;
            sessionId = httpRequest.getSession().getId();
            query = httpRequest.getQueryString();
            if (query == null) {
                query = "";
            } else {
                queryParameters = this.andPattern.split(query);
            }
        } else {
            method = "POST";
            path = request.getServletContext().getContextPath().replace('/', '.').substring(1);
            sessionId = "<no session>";
            query = "";
        }
        final String trimmedPath = path;
        TraceMetadata trace = SessionAndTraceRegistrationFilterForJPetstore.TRACEREGISTRY.getTrace();
        final boolean newTrace = trace == null;
        if (newTrace) {
            SessionRegistry.INSTANCE.storeThreadLocalSessionId(sessionId);
            trace = SessionAndTraceRegistrationFilterForJPetstore.TRACEREGISTRY.registerTrace();
            SessionAndTraceRegistrationFilterForJPetstore.CTRLINST.newMonitoringRecord(trace);
        }
        if ("GET".equals(method)) {
            if (queryParameters != null) {
                // pattern ="jpetstore\\.actions\\."
                operationSignature = trimmedPath + "." + this.equalsPattern.matcher(queryParameters[0]).replaceAll("") + "()";
                // is operation called with parameters
                if (queryParameters.length > 1) {
                    // then add the parameters as call informations
                    for (int i = 1; i < queryParameters.length; i++) {
                        final String[] queryParameterSplit = this.equalsPattern.split(queryParameters[i]);
                        callInformations.add(new CallInformation(queryParameterSplit[i], SessionAndTraceRegistrationFilterForJPetstore.INFORMATION_VALUE));
                    }
                }
                // pattern = "\\.action\\."
                operationSignature = this.removeActionOfOperationPattern.matcher(operationSignature).replaceAll("");
            } else {
                // pattern = "\\w*\\.actions\\."
                if (this.isActionPattern.matcher(trimmedPath).matches()) {
                    SessionAndTraceRegistrationFilterForJPetstore.LOGGER.debug("ACTION");
                    // pattern ="jpetstore\\.actions\\."
                    operationSignature = trimmedPath + "()";
                    // pattern = "\\.action\\("
                    operationSignature = this.removeActionOfIndexPattern.matcher(operationSignature).replaceAll(".index(");
                // pattern "\\w*\\.images\\."
                } else if (this.isImagePattern.matcher(trimmedPath).matches()) {
                    SessionAndTraceRegistrationFilterForJPetstore.LOGGER.debug("IMAGE");
                    operationSignature = trimmedPath;
                } else {
                    SessionAndTraceRegistrationFilterForJPetstore.LOGGER.debug("ELSE");
                    operationSignature = trimmedPath + "()";
                }
            }
        } else if ("POST".equals(method)) {
            operationSignature = trimmedPath + "()";
            // matches "(\\w*\\.)*Account\\..*" ?
            if (this.isAccountPattern.matcher(operationSignature).matches()) {
                // post on the account is always a login
                // pattern = "\\.action\\("
                operationSignature = this.removeActionOfIndexPattern.matcher(operationSignature).replaceAll(".login(");
            } else if (this.isCatalogPattern.matcher(operationSignature).matches()) {
                // post on the account is always a login
                // pattern = "\\.action\\("
                operationSignature = this.removeActionOfIndexPattern.matcher(operationSignature).replaceAll(".search(");
            }
            // pattern = "\\.action\\."
            operationSignature = this.removeActionOfIndexPattern.matcher(operationSignature).replaceAll(".index(");
        } else {
            chain.doFilter(request, response);
            return;
        }
        componentSignature = this.pathStrucPattern.matcher(operationSignature).replaceAll("");
        SessionAndTraceRegistrationFilterForJPetstore.LOGGER.info(operationSignature);
        final long traceId = trace.getTraceId();
        try {
            // mapps Object to String
            final ObjectMapper objectMapper = new ObjectMapper();
            SessionAndTraceRegistrationFilterForJPetstore.CTRLINST.newMonitoringRecord(new BeforeOperationEvent(SessionAndTraceRegistrationFilterForJPetstore.TIMESOURCE.getTime(), traceId, trace.getNextOrderId(), operationSignature, componentSignature));
            chain.doFilter(request, response);
            SessionAndTraceRegistrationFilterForJPetstore.CTRLINST.newMonitoringRecord(new ExtendedAfterOperationEvent(SessionAndTraceRegistrationFilterForJPetstore.TIMESOURCE.getTime(), traceId, trace.getNextOrderId(), operationSignature, componentSignature, objectMapper.writeValueAsString(callInformations)));
        } catch (final Throwable th) {
            // NOPMD NOCS (catch throw is ok here)
            SessionAndTraceRegistrationFilterForJPetstore.CTRLINST.newMonitoringRecord(new AfterOperationFailedEvent(SessionAndTraceRegistrationFilterForJPetstore.TIMESOURCE.getTime(), traceId, trace.getNextOrderId(), operationSignature, componentSignature, th.toString()));
            throw new ServletException(th);
        } finally {
            // is this correct?
            SessionAndTraceRegistrationFilterForJPetstore.SESSION_REGISTRY.unsetThreadLocalSessionId();
            // Reset the thread-local trace information
            if (newTrace) {
                // close the trace
                SessionAndTraceRegistrationFilterForJPetstore.TRACEREGISTRY.unregisterTrace();
            }
        }
    // } else {
    // chain.doFilter(request, response);
    // return;
    // }
    } else {
        chain.doFilter(request, response);
    }
}
Also used : CallInformation(org.iobserve.monitoring.probe.models.CallInformation) ArrayList(java.util.ArrayList) HttpServletRequest(javax.servlet.http.HttpServletRequest) ServletException(javax.servlet.ServletException) AfterOperationFailedEvent(kieker.common.record.flow.trace.operation.AfterOperationFailedEvent) ExtendedAfterOperationEvent(org.iobserve.common.record.ExtendedAfterOperationEvent) TraceMetadata(kieker.common.record.flow.trace.TraceMetadata) BeforeOperationEvent(kieker.common.record.flow.trace.operation.BeforeOperationEvent) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper)

Example 8 with BeforeOperationEvent

use of kieker.common.record.flow.trace.operation.BeforeOperationEvent in project iobserve-analysis by research-iobserve.

the class CalculateResponseTimeStage method execute.

/*
     * (non-Javadoc)
     *
     * @see teetime.framework.AbstractConsumerStage#execute(java.lang.Object)
     */
@Override
protected void execute(final IFlowRecord element) throws Exception {
    if (element instanceof BeforeOperationEvent) {
        final BeforeOperationEvent event = (BeforeOperationEvent) element;
        this.map.put(event.getTraceId(), event.getTimestamp());
    } else if (element instanceof AfterOperationFailedEvent) {
        final AfterOperationEvent event = (AfterOperationEvent) element;
        final long responseTime = event.getTimestamp() - this.map.remove(event.getTraceId());
        this.output.clear();
        this.output.put("time", event.getTimestamp());
        this.output.put("event-type", "AfterOperationFailedEvent");
        this.output.put("operation", event.getOperationSignature());
        this.output.put("task", event.getClassSignature());
        this.output.put("response-time", responseTime);
        this.outputPort.send(this.output);
    } else if (element instanceof AfterOperationEvent) {
        final AfterOperationEvent event = (AfterOperationEvent) element;
        final Long beforeTime = this.map.remove(event.getTraceId());
        if (beforeTime != null) {
            final long responseTime = event.getTimestamp() - beforeTime;
            this.output.clear();
            this.output.put("time", event.getTimestamp());
            this.output.put("event-type", "AfterOperationEvent");
            this.output.put("operation", event.getOperationSignature());
            this.output.put("task", event.getClassSignature());
            this.output.put("response-time", responseTime);
            this.outputPort.send(this.output);
        } else {
            this.logger.error("Event missing for AfterOperationEvent {} {} {}", event.getTimestamp(), event.getTraceId(), event.getOperationSignature());
        }
    }
}
Also used : AfterOperationFailedEvent(kieker.common.record.flow.trace.operation.AfterOperationFailedEvent) AfterOperationEvent(kieker.common.record.flow.trace.operation.AfterOperationEvent) BeforeOperationEvent(kieker.common.record.flow.trace.operation.BeforeOperationEvent)

Example 9 with BeforeOperationEvent

use of kieker.common.record.flow.trace.operation.BeforeOperationEvent in project iobserve-analysis by research-iobserve.

the class EntryCallStage method createEntryCall.

/**
 * This method is triggered for every deployment event.
 *
 * @param event
 *            all IFlowRecord like TraceMetadata, BeforeOperationEvent and AfterOperationEvent
 * @throws IOException
 * @throws JsonProcessingException
 */
private PayloadAwareEntryCallEvent createEntryCall(final TraceMetadata traceMetaData) throws JsonProcessingException, IOException {
    final BeforeOperationEvent beforeOperationEvent = this.matcher.getBeforeOperationEvent();
    final AfterOperationEvent afterOperationEvent = this.matcher.getAfterOperationEvent();
    if (beforeOperationEvent instanceof EntryLevelBeforeOperationEvent) {
        final EntryLevelBeforeOperationEvent entryLevelBeforeEvent = (EntryLevelBeforeOperationEvent) beforeOperationEvent;
        return new PayloadAwareEntryCallEvent(beforeOperationEvent.getTimestamp(), afterOperationEvent.getTimestamp(), beforeOperationEvent.getOperationSignature(), beforeOperationEvent.getClassSignature(), traceMetaData.getSessionId(), traceMetaData.getHostname(), entryLevelBeforeEvent.getParameters(), entryLevelBeforeEvent.getValues(), entryLevelBeforeEvent.getRequestType());
    } else if (afterOperationEvent instanceof ExtendedAfterOperationEvent) {
        final ExtendedAfterOperationEvent entryLevelAfterEvent = (ExtendedAfterOperationEvent) afterOperationEvent;
        final String parameters = entryLevelAfterEvent.getInformations();
        final ObjectMapper mapper = new ObjectMapper();
        final JsonNode actualObj = mapper.readTree(parameters);
        final List<String> parameterNames = new ArrayList<>();
        final List<String> parameterValues = new ArrayList<>();
        if (actualObj instanceof ArrayNode) {
            final ArrayNode values = (ArrayNode) actualObj;
            final Iterator<JsonNode> valuesIterator = values.elements();
            while (valuesIterator.hasNext()) {
                final JsonNode entry = valuesIterator.next();
                if (entry instanceof ObjectNode) {
                    final ObjectNode objectNode = (ObjectNode) entry;
                    final Iterator<String> fieldNames = objectNode.fieldNames();
                    String fieldName;
                    if (fieldNames.hasNext()) {
                        fieldName = fieldNames.next();
                        parameterNames.add(objectNode.get(fieldName).textValue());
                    }
                    if (fieldNames.hasNext()) {
                        fieldName = fieldNames.next();
                        parameterValues.add(objectNode.get(fieldName).textValue());
                    }
                }
            }
        }
        return new PayloadAwareEntryCallEvent(beforeOperationEvent.getTimestamp(), afterOperationEvent.getTimestamp(), beforeOperationEvent.getOperationSignature(), beforeOperationEvent.getClassSignature(), traceMetaData.getSessionId(), traceMetaData.getHostname(), parameterNames.toArray(new String[parameterNames.size()]), parameterValues.toArray(new String[parameterValues.size()]), 0);
    } else {
        return new PayloadAwareEntryCallEvent(beforeOperationEvent.getTimestamp(), afterOperationEvent.getTimestamp(), beforeOperationEvent.getOperationSignature(), beforeOperationEvent.getClassSignature(), traceMetaData.getSessionId(), traceMetaData.getHostname(), new String[0], new String[0], 0);
    }
}
Also used : ExtendedAfterOperationEvent(org.iobserve.common.record.ExtendedAfterOperationEvent) AfterOperationEvent(kieker.common.record.flow.trace.operation.AfterOperationEvent) ObjectNode(com.fasterxml.jackson.databind.node.ObjectNode) JsonNode(com.fasterxml.jackson.databind.JsonNode) PayloadAwareEntryCallEvent(org.iobserve.stages.general.data.PayloadAwareEntryCallEvent) EntryLevelBeforeOperationEvent(org.iobserve.common.record.EntryLevelBeforeOperationEvent) ExtendedAfterOperationEvent(org.iobserve.common.record.ExtendedAfterOperationEvent) Iterator(java.util.Iterator) ArrayList(java.util.ArrayList) List(java.util.List) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode) EntryLevelBeforeOperationEvent(org.iobserve.common.record.EntryLevelBeforeOperationEvent) BeforeOperationEvent(kieker.common.record.flow.trace.operation.BeforeOperationEvent) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper)

Example 10 with BeforeOperationEvent

use of kieker.common.record.flow.trace.operation.BeforeOperationEvent in project iobserve-analysis by research-iobserve.

the class DynamicEventDispatcherTest method initializeRecordSwitch.

/**
 * Initialize the record switch test setup.
 */
@Before
public void initializeRecordSwitch() {
    this.kiekerMetadataRecords.add(new KiekerMetadataRecord(DynamicEventDispatcherTest.VERSION, DynamicEventDispatcherTest.CONTROLLER_NAME, DynamicEventDispatcherTest.HOSTNAME, DynamicEventDispatcherTest.EXPERIMENT_ID, DynamicEventDispatcherTest.DEBUG_MODE, DynamicEventDispatcherTest.TIME_OFFSET, DynamicEventDispatcherTest.TIME_UNIT, DynamicEventDispatcherTest.NUMBER_OF_RECORDS));
    /**
     * allocation.
     */
    this.allocationRecords.add(new ContainerAllocationEvent(0, DynamicEventDispatcherTest.URL));
    /**
     * declare deployment records.
     */
    this.deploymentRecords.add(new ServletDeployedEvent(this.time++, DynamicEventDispatcherTest.SERVICE, DynamicEventDispatcherTest.CONTEXT, DynamicEventDispatcherTest.DEPLOYMENT_ID));
    this.deploymentRecords.add(new EJBDeployedEvent(this.time++, DynamicEventDispatcherTest.SERVICE, DynamicEventDispatcherTest.CONTEXT, DynamicEventDispatcherTest.DEPLOYMENT_ID));
    /**
     * geolocation.
     */
    this.geolocationRecords.add(new ServerGeoLocation(this.time++, DynamicEventDispatcherTest.COUNTRY_CODE, DynamicEventDispatcherTest.HOSTNAME, DynamicEventDispatcherTest.ADDRESS));
    /**
     * session event (start).
     */
    final SessionStartEvent sessionStartEvent = new SessionStartEvent(this.time++, DynamicEventDispatcherTest.HOSTNAME, DynamicEventDispatcherTest.SESSION_ID);
    this.sessionEventRecords.add(sessionStartEvent);
    /**
     * start trace.
     */
    final TraceMetadata traceMetadata = new TraceMetadata(DynamicEventDispatcherTest.TRACE_ID, DynamicEventDispatcherTest.THREAD_ID, DynamicEventDispatcherTest.SESSION_ID, DynamicEventDispatcherTest.HOSTNAME, 0, -1);
    this.traceMetadataRecords.add(traceMetadata);
    this.flowRecords.add(traceMetadata);
    /**
     * flow record.
     */
    this.flowRecords.add(new BeforeOperationEvent(this.time++, DynamicEventDispatcherTest.TRACE_ID, DynamicEventDispatcherTest.ORDER_INDEX, DynamicEventDispatcherTest.OPERATION_SIGNATURE, DynamicEventDispatcherTest.CLASS_SIGNATURE));
    this.flowRecords.add(new AfterOperationEvent(this.time++, DynamicEventDispatcherTest.TRACE_ID, DynamicEventDispatcherTest.ORDER_INDEX + 1, DynamicEventDispatcherTest.OPERATION_SIGNATURE, DynamicEventDispatcherTest.CLASS_SIGNATURE));
    /**
     * session event (end).
     */
    final SessionEndEvent sessionEndEvent = new SessionEndEvent(this.time++, DynamicEventDispatcherTest.HOSTNAME, DynamicEventDispatcherTest.SESSION_ID);
    this.sessionEventRecords.add(sessionEndEvent);
    /**
     * declare undeployment records.
     */
    this.undeploymentRecords.add(new ServletUndeployedEvent(this.time++, DynamicEventDispatcherTest.SERVICE, DynamicEventDispatcherTest.CONTEXT, DynamicEventDispatcherTest.DEPLOYMENT_ID));
    this.undeploymentRecords.add(new EJBUndeployedEvent(this.time++, DynamicEventDispatcherTest.SERVICE, DynamicEventDispatcherTest.CONTEXT, DynamicEventDispatcherTest.DEPLOYMENT_ID));
    /**
     * allocation.
     */
    this.deallocationRecords.add(new ContainerDeallocationEvent(0, DynamicEventDispatcherTest.URL));
    /**
     * other records.
     */
    this.otherRecords.add(new GCRecord(this.time++, DynamicEventDispatcherTest.HOSTNAME, DynamicEventDispatcherTest.VM_NAME, DynamicEventDispatcherTest.GC_NAME, DynamicEventDispatcherTest.COLLECTION_COUNT, DynamicEventDispatcherTest.COLLECTION_TIME_MS));
    /**
     * declare all input record types.
     */
    this.inputRecords.addAll(this.kiekerMetadataRecords);
    this.inputRecords.addAll(this.allocationRecords);
    this.inputRecords.addAll(this.deploymentRecords);
    this.inputRecords.addAll(this.geolocationRecords);
    this.inputRecords.add(sessionStartEvent);
    this.inputRecords.addAll(this.flowRecords);
    this.inputRecords.add(sessionEndEvent);
    this.inputRecords.addAll(this.undeploymentRecords);
    this.inputRecords.addAll(this.deallocationRecords);
    this.inputRecords.addAll(this.otherRecords);
/**
 * matcher.
 */
}
Also used : AfterOperationEvent(kieker.common.record.flow.trace.operation.AfterOperationEvent) EJBDeployedEvent(org.iobserve.common.record.EJBDeployedEvent) KiekerMetadataRecord(kieker.common.record.misc.KiekerMetadataRecord) EJBUndeployedEvent(org.iobserve.common.record.EJBUndeployedEvent) ContainerDeallocationEvent(org.iobserve.common.record.ContainerDeallocationEvent) GCRecord(kieker.common.record.jvm.GCRecord) ServletUndeployedEvent(org.iobserve.common.record.ServletUndeployedEvent) SessionStartEvent(org.iobserve.common.record.SessionStartEvent) SessionEndEvent(org.iobserve.common.record.SessionEndEvent) ContainerAllocationEvent(org.iobserve.common.record.ContainerAllocationEvent) ServerGeoLocation(org.iobserve.common.record.ServerGeoLocation) TraceMetadata(kieker.common.record.flow.trace.TraceMetadata) ServletDeployedEvent(org.iobserve.common.record.ServletDeployedEvent) BeforeOperationEvent(kieker.common.record.flow.trace.operation.BeforeOperationEvent) Before(org.junit.Before)

Aggregations

BeforeOperationEvent (kieker.common.record.flow.trace.operation.BeforeOperationEvent)11 TraceMetadata (kieker.common.record.flow.trace.TraceMetadata)8 AfterOperationEvent (kieker.common.record.flow.trace.operation.AfterOperationEvent)8 AfterOperationFailedEvent (kieker.common.record.flow.trace.operation.AfterOperationFailedEvent)6 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)3 ArrayList (java.util.ArrayList)3 ServletException (javax.servlet.ServletException)3 HttpServletRequest (javax.servlet.http.HttpServletRequest)3 ExtendedAfterOperationEvent (org.iobserve.common.record.ExtendedAfterOperationEvent)3 GCRecord (kieker.common.record.jvm.GCRecord)2 KiekerMetadataRecord (kieker.common.record.misc.KiekerMetadataRecord)2 ContainerAllocationEvent (org.iobserve.common.record.ContainerAllocationEvent)2 ContainerDeallocationEvent (org.iobserve.common.record.ContainerDeallocationEvent)2 EJBDeployedEvent (org.iobserve.common.record.EJBDeployedEvent)2 EJBUndeployedEvent (org.iobserve.common.record.EJBUndeployedEvent)2 EntryLevelBeforeOperationEvent (org.iobserve.common.record.EntryLevelBeforeOperationEvent)2 ServerGeoLocation (org.iobserve.common.record.ServerGeoLocation)2 ServletDeployedEvent (org.iobserve.common.record.ServletDeployedEvent)2 ServletUndeployedEvent (org.iobserve.common.record.ServletUndeployedEvent)2 SessionEndEvent (org.iobserve.common.record.SessionEndEvent)2