You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by st...@apache.org on 2018/02/09 21:25:06 UTC
[3/5] deltaspike git commit: DELTASPIKE-1316 add @InvocationMonitored
DELTASPIKE-1316 add @InvocationMonitored
wip, still misses tests
Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/9423d746
Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/9423d746
Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/9423d746
Branch: refs/heads/master
Commit: 9423d7466d9507bc243ca3e7bea5a97986bd3bb6
Parents: 8cc2a7f
Author: Mark Struberg <st...@apache.org>
Authored: Fri Feb 9 20:43:19 2018 +0100
Committer: Mark Struberg <st...@apache.org>
Committed: Fri Feb 9 20:43:19 2018 +0100
----------------------------------------------------------------------
.../api/monitoring/InvocationMonitored.java | 43 +++++
.../core/api/monitoring/MonitorResultEvent.java | 81 ++++++++++
.../InvocationMonitorInterceptor.java | 47 ++++++
.../impl/monitoring/InvocationResultLogger.java | 160 +++++++++++++++++++
.../monitoring/RequestInvocationCounter.java | 105 ++++++++++++
5 files changed, 436 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/9423d746/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/monitoring/InvocationMonitored.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/monitoring/InvocationMonitored.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/monitoring/InvocationMonitored.java
new file mode 100644
index 0000000..48170c8
--- /dev/null
+++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/monitoring/InvocationMonitored.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.deltaspike.core.api.monitoring;
+
+import javax.interceptor.InterceptorBinding;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Use this annotation to mark a bean as being monitored.
+ * This will activate a CDI Interceptor which will track
+ * all method invocations on that class and measure how
+ * often they got invoked and how much time they consume.
+ *
+ * At the end of a request the final times will get sent out
+ * as {@link MonitorResultEvent}.
+ */
+@Inherited
+@InterceptorBinding
+@Target( { ElementType.TYPE, ElementType.METHOD } )
+@Retention(RetentionPolicy.RUNTIME)
+public @interface InvocationMonitored
+{
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/9423d746/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/monitoring/MonitorResultEvent.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/monitoring/MonitorResultEvent.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/monitoring/MonitorResultEvent.java
new file mode 100644
index 0000000..7c4db7b
--- /dev/null
+++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/monitoring/MonitorResultEvent.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.deltaspike.core.api.monitoring;
+
+import javax.enterprise.inject.Typed;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * This class will be used as event to transport final monitor values
+ *
+ * @see InvocationMonitored
+ */
+@Typed()
+public class MonitorResultEvent
+{
+ private Map<String, AtomicInteger> methodInvocations = new HashMap<String, AtomicInteger>();
+
+ private Map<String, AtomicInteger> classInvocations = new HashMap<String, AtomicInteger>();
+
+ private Map<String, AtomicLong> methodDurations = new HashMap<String, AtomicLong>();
+
+ public MonitorResultEvent(Map<String, AtomicInteger> methodInvocations,
+ Map<String, AtomicInteger> classInvocations,
+ Map<String, AtomicLong> methodDurations)
+ {
+ this.methodInvocations = methodInvocations;
+ this.classInvocations = classInvocations;
+ this.methodDurations = methodDurations;
+ }
+
+
+ /**
+ * @return Map with Counters for all method invocations
+ * key = fully qualified method name (includes class)
+ * value = AtomicInteger with invocation count value
+ */
+ public Map<String, AtomicInteger> getMethodInvocations()
+ {
+ return methodInvocations;
+ }
+
+ /**
+ * @return Map with Counter for all class invocations
+ * key = fully qualified class name
+ * value = AtomicInteger with invocation count value
+ */
+ public Map<String, AtomicInteger> getClassInvocations()
+ {
+ return classInvocations;
+ }
+
+
+ /**
+ * @return Map with duration for all method invocations
+ * key = fully qualified method name (includes class)
+ * value = AtomicLong with duration nanos
+ */
+ public Map<String, AtomicLong> getMethodDurations()
+ {
+ return methodDurations;
+ }
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/9423d746/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/InvocationMonitorInterceptor.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/InvocationMonitorInterceptor.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/InvocationMonitorInterceptor.java
new file mode 100644
index 0000000..54b3b7d
--- /dev/null
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/InvocationMonitorInterceptor.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.deltaspike.core.impl.monitoring;
+
+import org.apache.deltaspike.core.api.monitoring.InvocationMonitored;
+
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import java.io.Serializable;
+
+@Interceptor
+@InvocationMonitored
+public class InvocationMonitorInterceptor implements Serializable
+{
+ @Inject
+ private RequestInvocationCounter requestInvocationCounter;
+
+ @AroundInvoke
+ public Object track(InvocationContext ic) throws Exception
+ {
+ long start = System.nanoTime();
+ Object retVal = ic.proceed();
+ long end = System.nanoTime();
+ requestInvocationCounter.count(ic.getTarget().getClass().getName(), ic.getMethod().getName(), end - start);
+
+ return retVal;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/9423d746/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/InvocationResultLogger.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/InvocationResultLogger.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/InvocationResultLogger.java
new file mode 100644
index 0000000..52639e2
--- /dev/null
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/InvocationResultLogger.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.deltaspike.core.impl.monitoring;
+
+import org.apache.deltaspike.core.api.monitoring.MonitorResultEvent;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Logger;
+
+/**
+ * This class will observe all {@link MonitorResultEvent}s
+ * and log them accordingly
+ */
+@ApplicationScoped
+public class InvocationResultLogger
+{
+ private static final Logger logger = Logger.getLogger(InvocationResultLogger.class.getName());
+
+ private static final int DEFAULT_MAX_LOG_LINES = 8;
+ private static final String PROPERTY_MAX_LOG_LINES = "MAX_LOG_LINES";
+
+ private int maxLogLines = DEFAULT_MAX_LOG_LINES + 1;
+
+ @PostConstruct
+ private void init()
+ {
+ String maxLogLinesProp = System.getProperty(PROPERTY_MAX_LOG_LINES);
+ if (maxLogLinesProp != null)
+ {
+ maxLogLines = Integer.parseInt(maxLogLinesProp) + 1;
+ }
+
+ logger.info("Using MAX_LOG_LINE=" + maxLogLines);
+ }
+
+ public void logMonitorResultEvents(@Observes MonitorResultEvent mre)
+ {
+ // we copy them because we don't like to make the event data dirty.
+ // there might be other observers interested in the result...
+ List<ResultEntry> methodInvocations
+ = createMethodResultEntries(mre.getMethodInvocations(), mre.getMethodDurations());
+ List<ResultEntry> classInvocations
+ = createClassResultEntries(mre.getClassInvocations());
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Top Class Invocations:\n");
+ for (int i = 1; i < maxLogLines && i <= classInvocations.size(); i++)
+ {
+ ResultEntry re = classInvocations.get(classInvocations.size() - i);
+ sb.append(" count: ").append(re.getCount()).append("\t").append(re.getToken()).append("\n");
+ }
+ logger.info(sb.toString());
+
+ sb = new StringBuilder();
+ sb.append("Top Method Invocations:\n");
+ for (int i = 1; i < maxLogLines && i <= methodInvocations.size(); i++)
+ {
+ ResultEntry re = methodInvocations.get(methodInvocations.size() - i);
+ sb.append(" dur[ms]: ").append(re.getDuration() / 1e6f).append("\tcount: ").
+ append(re.getCount()).append("\t").append(re.getToken()).append("\n");
+ }
+ logger.info(sb.toString());
+ }
+
+
+ private List<ResultEntry> createMethodResultEntries(Map<String, AtomicInteger> invocations,
+ Map<String, AtomicLong> durations)
+ {
+ List<ResultEntry> resultEntries = new ArrayList<ResultEntry>(invocations.size());
+
+
+
+ for (Map.Entry<String, AtomicInteger> entry : invocations.entrySet())
+ {
+ long dur = durations.get(entry.getKey()).longValue();
+ resultEntries.add(new ResultEntry(entry.getValue().intValue(), entry.getKey(), dur));
+ }
+
+ Collections.sort(resultEntries);
+
+ return resultEntries;
+ }
+
+ private List<ResultEntry> createClassResultEntries(Map<String, AtomicInteger> invocations)
+ {
+ List<ResultEntry> resultEntries = new ArrayList<ResultEntry>(invocations.size());
+
+ for (Map.Entry<String, AtomicInteger> entry : invocations.entrySet())
+ {
+ resultEntries.add(new ResultEntry(entry.getValue().intValue(), entry.getKey(), 0L));
+ }
+
+ Collections.sort(resultEntries);
+
+ return resultEntries;
+ }
+
+ private static class ResultEntry implements Comparable<ResultEntry>
+ {
+ private Integer count;
+ private String token;
+ private long duration;
+
+ private ResultEntry(Integer count, String token, long duration)
+ {
+ this.count = count;
+ this.token = token;
+ this.duration = duration;
+ }
+
+ public Integer getCount()
+ {
+ return count;
+ }
+
+ public String getToken()
+ {
+ return token;
+ }
+
+ public long getDuration()
+ {
+ return duration;
+ }
+
+ public int compareTo(ResultEntry o)
+ {
+ if (duration == 0 && o.duration == 0)
+ {
+ return count.compareTo(o.count);
+ }
+
+ return duration < o.duration ? -1 : (duration == o.duration ? 0 : 1);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/deltaspike/blob/9423d746/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/RequestInvocationCounter.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/RequestInvocationCounter.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/RequestInvocationCounter.java
new file mode 100644
index 0000000..8f46c37
--- /dev/null
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/monitoring/RequestInvocationCounter.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.deltaspike.core.impl.monitoring;
+
+import org.apache.deltaspike.core.api.monitoring.MonitorResultEvent;
+
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * This bean will get used to count invocations for a single request
+ */
+@RequestScoped
+public class RequestInvocationCounter
+{
+ @Inject
+ private Event<MonitorResultEvent> mre;
+
+
+ /**
+ * Counter for all method invocations
+ * key = fully qualified method name (includes class)
+ * value = Integer with value
+ */
+ private Map<String, AtomicInteger> methodInvocations = new HashMap<String, AtomicInteger>();
+
+ /**
+ * Duration of all method invocations
+ * key = fully qualified method name (includes class)
+ * value = Integer with value
+ */
+ private Map<String, AtomicLong> methodDurations = new HashMap<String, AtomicLong>();
+
+ /**
+ * Counter for all class invocations
+ * key = fully qualified class name
+ * value = Integer with value
+ */
+ private Map<String, AtomicInteger> classInvocations = new HashMap<String, AtomicInteger>();
+
+
+ @PreDestroy
+ public void postUsage()
+ {
+ mre.fire(new MonitorResultEvent(methodInvocations, classInvocations, methodDurations));
+ }
+
+ /**
+ * increment the respective counters
+ * @param className the getName() of the class
+ * @param methodName the invoked methods name
+ * @param duration duration of the method invocation in nano time
+ */
+ public void count(String className, String methodName, long duration)
+ {
+ AtomicInteger classCount = classInvocations.get(className);
+ if (classCount == null)
+ {
+ classCount = new AtomicInteger(0);
+ classInvocations.put(className, classCount);
+ }
+ classCount.incrementAndGet();
+
+
+ String methodKey = className + "#" + methodName;
+
+ AtomicInteger methCount = methodInvocations.get(methodKey);
+ if (methCount == null)
+ {
+ methCount = new AtomicInteger(0);
+ methodInvocations.put(methodKey, methCount);
+ }
+ methCount.incrementAndGet();
+
+ AtomicLong methDur = methodDurations.get(methodKey);
+ if (methDur == null)
+ {
+ methDur = new AtomicLong(0);
+ methodDurations.put(methodKey, methDur);
+ }
+ methDur.addAndGet(duration);
+ }
+}