Start work on exposing the collected metrics on a dashboard view

Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/259d5e1a
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/259d5e1a
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/259d5e1a

Branch: refs/heads/master
Commit: 259d5e1af4bd3aa86d5907f923432ebd538ce3fc
Parents: e764c95
Author: Howard M. Lewis Ship <[email protected]>
Authored: Sat Apr 20 19:13:15 2013 -0400
Committer: Howard M. Lewis Ship <[email protected]>
Committed: Sat Apr 20 19:13:15 2013 -0400

----------------------------------------------------------------------
 .../java/org/apache/tapestry5/TapestryFilter.java  |    2 +
 .../apache/tapestry5/corelib/pages/AppMetrics.java |   48 +++++++++++++
 .../apache/tapestry5/modules/DashboardModule.java  |    1 +
 .../tapestry5/modules/RootMetricsFilter.java       |   52 +++++++++++++++
 .../apache/tapestry5/modules/TapestryModule.java   |   12 ++--
 .../apache/tapestry5/modules/WebMetricsModule.java |   34 ++++++++++
 .../apache/tapestry5/corelib/pages/AppMetrics.tml  |    6 ++
 .../services/metrics/MetricCollectorImpl.java      |   49 ++++++++++++--
 .../tapestry5/ioc/services/metrics/Metric.java     |   30 +++++++-
 9 files changed, 219 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java 
b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java
index 98fea52..ed9795f 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java
@@ -165,7 +165,9 @@ public class TapestryFilter implements Filter
                     (HttpServletResponse) response);
 
             if (!handled)
+            {
                 chain.doFilter(request, response);
+            }
         } finally
         {
             registry.cleanupThread();

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/AppMetrics.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/AppMetrics.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/AppMetrics.java
new file mode 100644
index 0000000..d57b75c
--- /dev/null
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/AppMetrics.java
@@ -0,0 +1,48 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.corelib.pages;
+
+import org.apache.tapestry5.annotations.WhitelistAccessOnly;
+import org.apache.tapestry5.beaneditor.BeanModel;
+import org.apache.tapestry5.ioc.Messages;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.services.metrics.Metric;
+import org.apache.tapestry5.ioc.services.metrics.MetricCollector;
+import org.apache.tapestry5.services.BeanModelSource;
+
+import java.util.List;
+
+@WhitelistAccessOnly
+/**
+ * Contributes to the {@link T5Dashboard} page, providing application metrics 
from the {@link org.apache.tapestry5.ioc.services.metrics.MetricCollector}.
+ */
+public class AppMetrics
+{
+    @Inject
+    MetricCollector collector;
+
+    @Inject
+    BeanModelSource beanModelSource;
+
+    @Inject
+    private Messages messages;
+
+    public final BeanModel<Metric> metricModel = 
beanModelSource.createDisplayModel(Metric.class, messages);
+
+    public List<Metric> getRootMetrics()
+    {
+        return collector.getRootMetrics();
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java
index 22abd21..9655457 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java
@@ -32,6 +32,7 @@ public class DashboardModule
     public static void defaultTabs(OrderedConfiguration<DashboardTab> 
configuration)
     {
         configuration.add("Pages", new DashboardTab("Pages", 
"core/PageCatalog"));
+        configuration.add("Metrics", new DashboardTab("Metrics", 
"core/AppMetrics"));
         configuration.add("Services", new DashboardTab("Services", 
"core/ServiceStatus"));
     }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-core/src/main/java/org/apache/tapestry5/modules/RootMetricsFilter.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/modules/RootMetricsFilter.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/RootMetricsFilter.java
new file mode 100644
index 0000000..3d08f30
--- /dev/null
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/RootMetricsFilter.java
@@ -0,0 +1,52 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.modules;
+
+import org.apache.tapestry5.ioc.services.metrics.Metric;
+import org.apache.tapestry5.ioc.services.metrics.MetricCollector;
+import org.apache.tapestry5.services.HttpServletRequestFilter;
+import org.apache.tapestry5.services.HttpServletRequestHandler;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class RootMetricsFilter implements HttpServletRequestFilter
+{
+
+    private final Metric count, requestTime;
+
+    public RootMetricsFilter(MetricCollector collector)
+    {
+        count = collector.createRootMetric("request-count", Metric.Type.TOTAL, 
Metric.Units.COUNTER);
+        requestTime = collector.createRootMetric("request-time", 
Metric.Type.RATE, Metric.Units.MILLISECONDS);
+    }
+
+    public boolean service(HttpServletRequest request, HttpServletResponse 
response, HttpServletRequestHandler handler) throws IOException
+    {
+        long start = System.currentTimeMillis();
+
+        boolean handled = handler.service(request, response);
+
+        // We don't count the unhandled requests
+
+        if (handled) {
+            count.increment();
+            requestTime.accumulate(System.currentTimeMillis() - start);
+        }
+
+        return handled;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
index 0c7d291..4f567b9 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
@@ -108,7 +108,7 @@ import java.util.regex.Pattern;
  */
 @Marker(Core.class)
 @SubModule(
-        {InternalModule.class, AssetsModule.class, PageLoadModule.class, 
JavaScriptModule.class, CompatibilityModule.class, DashboardModule.class})
+        {InternalModule.class, AssetsModule.class, PageLoadModule.class, 
JavaScriptModule.class, CompatibilityModule.class, DashboardModule.class, 
WebMetricsModule.class})
 public final class TapestryModule
 {
     private final PipelineBuilder pipelineBuilder;
@@ -235,8 +235,10 @@ public final class TapestryModule
             Response response = new ResponseImpl(servletRequest, 
servletResponse);
 
             // TAP5-257: Make sure that the "initial guess" for 
request/response
-            // is available, even if
-            // some filter in the RequestHandler pipeline replaces them.
+            // is available, even ifsome filter in the RequestHandler pipeline 
replaces them.
+            // Which just goes to show that there should have been only one 
way to access the Request/Response:
+            // either functionally (via parameters) or global (via 
ReqeuestGlobals) but not both.
+            // That ship has sailed.
 
             requestGlobals.storeRequestResponse(request, response);
 
@@ -428,8 +430,8 @@ public final class TapestryModule
 
     @Contribute(ComponentClassResolver.class)
     public static void 
provideCoreAndAppLibraries(Configuration<LibraryMapping> configuration,
-                                                
@Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
-                                                String appRootPackage)
+                                                  
@Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
+                                                  String appRootPackage)
     {
         configuration.add(new LibraryMapping(InternalConstants.CORE_LIBRARY, 
"org.apache.tapestry5.corelib"));
         configuration.add(new LibraryMapping("", appRootPackage));

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-core/src/main/java/org/apache/tapestry5/modules/WebMetricsModule.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/modules/WebMetricsModule.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/WebMetricsModule.java
new file mode 100644
index 0000000..7778e94
--- /dev/null
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/WebMetricsModule.java
@@ -0,0 +1,34 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.modules;
+
+import org.apache.tapestry5.ioc.OrderedConfiguration;
+import org.apache.tapestry5.ioc.annotations.Contribute;
+import org.apache.tapestry5.services.HttpServletRequestFilter;
+import org.apache.tapestry5.services.HttpServletRequestHandler;
+
+/**
+ * Responsible for contributing filters in various places, to handle metrics 
collection.
+ *
+ * @since 5.4
+ */
+public class WebMetricsModule
+{
+    @Contribute(HttpServletRequestHandler.class)
+    public static void 
addRootFilter(OrderedConfiguration<HttpServletRequestFilter> configuration)
+    {
+        configuration.addInstance("RootMetrics", RootMetricsFilter.class, 
"before:GZip");
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/AppMetrics.tml
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/AppMetrics.tml
 
b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/AppMetrics.tml
new file mode 100644
index 0000000..736698a
--- /dev/null
+++ 
b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/AppMetrics.tml
@@ -0,0 +1,6 @@
+<t:block id="content" 
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd";
+        >
+
+    <t:grid source="rootMetrics" model="metricModel"/>"
+
+</t:block>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/metrics/MetricCollectorImpl.java
----------------------------------------------------------------------
diff --git 
a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/metrics/MetricCollectorImpl.java
 
b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/metrics/MetricCollectorImpl.java
index 65db295..14ff478 100644
--- 
a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/metrics/MetricCollectorImpl.java
+++ 
b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/metrics/MetricCollectorImpl.java
@@ -1,8 +1,23 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.ioc.internal.services.metrics;
 
 import com.google.common.util.concurrent.AtomicDouble;
 import org.apache.tapestry5.func.F;
 import org.apache.tapestry5.ioc.annotations.PostInjection;
+import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration;
 import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
@@ -13,15 +28,19 @@ import org.apache.tapestry5.ioc.services.metrics.Metric;
 import org.apache.tapestry5.ioc.services.metrics.MetricCollector;
 import org.apache.tapestry5.ioc.services.metrics.MetricsSymbols;
 import org.apache.tapestry5.ioc.util.ExceptionUtils;
+import org.rrd4j.ConsolFun;
+import org.rrd4j.DsType;
 import org.rrd4j.core.*;
 import org.slf4j.Logger;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+@PreventServiceDecoration
 public class MetricCollectorImpl extends LockSupport implements 
MetricCollector, Runnable
 {
     private final boolean inMemory;
@@ -38,7 +57,7 @@ public class MetricCollectorImpl extends LockSupport 
implements MetricCollector,
 
     public static final String DS_NAME = "data";
 
-    private class MetricImpl extends LockSupport implements Metric, 
Comparable<MetricImpl>, Runnable
+    public class MetricImpl extends LockSupport implements Metric, 
Comparable<MetricImpl>, Runnable
     {
         private final MetricImpl parent;
 
@@ -82,6 +101,8 @@ public class MetricCollectorImpl extends LockSupport 
implements MetricCollector,
                         path,
                         ExceptionUtils.toMessage(ex)), ex);
             }
+
+            updates.add(this);
         }
 
         private RrdDb createDb() throws IOException
@@ -110,7 +131,13 @@ public class MetricCollectorImpl extends LockSupport 
implements MetricCollector,
 
         private RrdDef createDef(String filePath)
         {
-            return null;
+            RrdDef result = new RrdDef(filePath, HEARTBEAT);
+
+            result.addDatasource(DS_NAME, DsType.COUNTER, HEARTBEAT, 0, 
Double.NaN);
+            // One archive: average for each new data point, 10 minutes worth.
+            result.addArchive(new ArcDef(ConsolFun.AVERAGE, .5, 1, 60));
+
+            return result;
         }
 
         public int compareTo(MetricImpl o)
@@ -175,16 +202,16 @@ public class MetricCollectorImpl extends LockSupport 
implements MetricCollector,
 
         public void increment()
         {
-            store(1);
+            accumulate(1);
         }
 
-        public void store(double value)
+        public void accumulate(double value)
         {
             accumulator.addAndGet(value);
 
             if (parent != null)
             {
-                parent.store(value);
+                parent.accumulate(value);
             }
         }
 
@@ -201,11 +228,21 @@ public class MetricCollectorImpl extends LockSupport 
implements MetricCollector,
             }
         }
 
-        public void run()
+        public Date getLastUpdateTime()
         {
             try
             {
+                return new Date(db.getLastUpdateTime());
+            } catch (IOException ex)
+            {
+                return null;
+            }
+        }
 
+        public void run()
+        {
+            try
+            {
                 db.createSample().setValue(DS_NAME, 
accumulator.getAndSet(0)).update();
             } catch (IOException ex)
             {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/259d5e1a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/metrics/Metric.java
----------------------------------------------------------------------
diff --git 
a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/metrics/Metric.java
 
b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/metrics/Metric.java
index 83f82da..71cda59 100644
--- 
a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/metrics/Metric.java
+++ 
b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/metrics/Metric.java
@@ -1,5 +1,20 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.ioc.services.metrics;
 
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -64,21 +79,28 @@ public interface Metric
     Units getUnits();
 
     /**
-     * Stores a value of 1; useful  when the metric's type is {@link 
Units#COUNTER}.
+     * Accumulates a value of 1; useful  when the metric's type is {@link 
Units#COUNTER}.
      */
     void increment();
 
     /**
-     * Stores a value into the current time interval. This may be called 
multiple times during
-     * a time interval, and the values will accumulate. In addition, {@link 
#store(double)}
+     * Adds the provided value to the current time interval's value. This may 
be called multiple times during
+     * a time interval, and the values will accumulate. In addition, {@link 
#accumulate(double)}
      * propagates the value up to the parent metrics, if any, all the way up 
to the root metric.
      *
      * @param value
      */
-    void store(double value);
+    void accumulate(double value);
 
     /**
      * Returns the children of this metric, sorted by name.
      */
     List<Metric> getChildren();
+
+    /**
+     * Returns the last time the archive was updated (this happens a regular 
intervals called heartbeats).
+     *
+     * @return archive update time, or null if there is an exception accessing 
the time
+     */
+    Date getLastUpdateTime();
 }

Reply via email to