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(); }
