This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.commons.metrics-0.0.2 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics.git
commit 66282a39d317a11fec65d9579cfc3b4518b4ed61 Author: Chetan Mehrotra <[email protected]> AuthorDate: Tue Jan 5 09:39:51 2016 +0000 SLING-4080 - API to capture/measure application-level metrics Metric based implementation for the API git-svn-id: https://svn.apache.org/repos/asf/sling/whiteboard/chetanm/metrics@1723024 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 7 +- .../apache/sling/metrics/internal/CounterImpl.java | 65 +++++++ .../sling/metrics/internal/HistogramImpl.java | 50 ++++++ .../apache/sling/metrics/internal/MeterImpl.java | 54 ++++++ .../sling/metrics/internal/MetricsServiceImpl.java | 198 +++++++++++++++++++++ .../apache/sling/metrics/internal/TimerImpl.java | 77 ++++++++ .../sling/metrics/internal/MetricServiceTest.java | 120 +++++++++++++ .../sling/metrics/internal/MetricWrapperTest.java | 132 ++++++++++++++ 8 files changed, 702 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b2856b..6c87f50 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ <parent> <groupId>org.apache.sling</groupId> <artifactId>sling</artifactId> - <version>22</version> + <version>26</version> </parent> <artifactId>org.apache.sling.metrics</artifactId> @@ -113,6 +113,11 @@ <artifactId>mockito-core</artifactId> <version>1.10.19</version> </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/src/main/java/org/apache/sling/metrics/internal/CounterImpl.java b/src/main/java/org/apache/sling/metrics/internal/CounterImpl.java new file mode 100644 index 0000000..1afa99b --- /dev/null +++ b/src/main/java/org/apache/sling/metrics/internal/CounterImpl.java @@ -0,0 +1,65 @@ +/* + * 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.sling.metrics.internal; + + +import org.apache.sling.metrics.Counter; + +final class CounterImpl implements Counter { + private final com.codahale.metrics.Counter counter; + + CounterImpl(com.codahale.metrics.Counter counter) { + this.counter = counter; + } + + @Override + public void inc() { + counter.inc(); + } + + @Override + public void dec() { + counter.dec(); + } + + @Override + public void inc(long n) { + counter.inc(n); + } + + @Override + public void dec(long n) { + counter.dec(n); + } + + @Override + public long getCount() { + return counter.getCount(); + } + + @SuppressWarnings("unchecked") + @Override + public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) { + if (type == com.codahale.metrics.Counter.class){ + return (AdapterType) counter; + } + return null; + } +} diff --git a/src/main/java/org/apache/sling/metrics/internal/HistogramImpl.java b/src/main/java/org/apache/sling/metrics/internal/HistogramImpl.java new file mode 100644 index 0000000..25be1a8 --- /dev/null +++ b/src/main/java/org/apache/sling/metrics/internal/HistogramImpl.java @@ -0,0 +1,50 @@ +/* + * 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.sling.metrics.internal; + + +import org.apache.sling.metrics.Histogram; + +final class HistogramImpl implements Histogram { + private final com.codahale.metrics.Histogram histogram; + + HistogramImpl(com.codahale.metrics.Histogram histogram) { + this.histogram = histogram; + } + + @Override + public void update(long value) { + histogram.update(value); + } + + @Override + public long getCount() { + return histogram.getCount(); + } + + @SuppressWarnings("unchecked") + @Override + public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) { + if (type == com.codahale.metrics.Histogram.class){ + return (AdapterType) histogram; + } + return null; + } +} diff --git a/src/main/java/org/apache/sling/metrics/internal/MeterImpl.java b/src/main/java/org/apache/sling/metrics/internal/MeterImpl.java new file mode 100644 index 0000000..95cac21 --- /dev/null +++ b/src/main/java/org/apache/sling/metrics/internal/MeterImpl.java @@ -0,0 +1,54 @@ +/* + * 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.sling.metrics.internal; + +import org.apache.sling.metrics.Meter; + +final class MeterImpl implements Meter { + private final com.codahale.metrics.Meter meter; + + MeterImpl(com.codahale.metrics.Meter meter) { + this.meter = meter; + } + + @Override + public void mark() { + meter.mark(); + } + + @Override + public void mark(long n) { + meter.mark(n); + } + + @Override + public long getCount() { + return meter.getCount(); + } + + @SuppressWarnings("unchecked") + @Override + public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) { + if (type == com.codahale.metrics.Meter.class){ + return (AdapterType)meter; + } + return null; + } +} diff --git a/src/main/java/org/apache/sling/metrics/internal/MetricsServiceImpl.java b/src/main/java/org/apache/sling/metrics/internal/MetricsServiceImpl.java new file mode 100644 index 0000000..3933e78 --- /dev/null +++ b/src/main/java/org/apache/sling/metrics/internal/MetricsServiceImpl.java @@ -0,0 +1,198 @@ +/* + * 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.sling.metrics.internal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.management.MBeanServer; + +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.MetricRegistry; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.sling.metrics.Counter; +import org.apache.sling.metrics.Histogram; +import org.apache.sling.metrics.Meter; +import org.apache.sling.metrics.Metric; +import org.apache.sling.metrics.MetricsService; +import org.apache.sling.metrics.Timer; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; + +@Component +public class MetricsServiceImpl implements MetricsService{ + private final List<ServiceRegistration> regs = new ArrayList<ServiceRegistration>(); + private final ConcurrentMap<String, Metric> metrics = new ConcurrentHashMap<String, Metric>(); + private final MetricRegistry registry = new MetricRegistry(); + + @Reference + private MBeanServer server; + + private JmxReporter reporter; + + @Activate + private void activate(BundleContext context, Map<String, Object> config) { + //TODO Domain name should be based on calling bundle + //For that we can register ServiceFactory and make use of calling + //bundle symbolic name to determine the mapping + + reporter = JmxReporter.forRegistry(registry) + .inDomain("org.apache.sling") + .registerWith(server) + .build(); + + final Dictionary<String, String> svcProps = new Hashtable<String, String>(); + svcProps.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Metrics Service"); + svcProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + regs.add(context.registerService(MetricsService.class.getName(), this, svcProps)); + + final Dictionary<String, String> regProps = new Hashtable<String, String>(); + regProps.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Metrics Registry"); + regProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + regs.add(context.registerService(MetricRegistry.class.getName(), registry, regProps)); + } + + @Deactivate + private void deactivate() throws IOException { + for (ServiceRegistration reg : regs) { + reg.unregister(); + } + regs.clear(); + + metrics.clear(); + + if (reporter != null) { + reporter.close(); + } + } + + @Override + public Timer timer(String name) { + return getOrAdd(name, MetricBuilder.TIMERS); + } + + @Override + public Histogram histogram(String name) { + return getOrAdd(name, MetricBuilder.HISTOGRAMS); + } + + @Override + public Counter counter(String name) { + return getOrAdd(name, MetricBuilder.COUNTERS); + } + + @Override + public Meter meter(String name) { + return getOrAdd(name, MetricBuilder.METERS); + } + + @SuppressWarnings("unchecked") + private <T extends Metric> T getOrAdd(String name, MetricBuilder<T> builder) { + final Metric metric = metrics.get(name); + if (builder.isInstance(metric)) { + return (T) metric; + } else if (metric == null) { + try { + return register(name, builder.newMetric(registry, name)); + } catch (IllegalArgumentException e) { + final Metric added = metrics.get(name); + if (builder.isInstance(added)) { + return (T) added; + } + } + } + throw new IllegalArgumentException(name + " is already used for a different type of metric"); + } + + private <T extends Metric> T register(String name, T metric) throws IllegalArgumentException { + final Metric existing = metrics.putIfAbsent(name, metric); + if (existing != null) { + throw new IllegalArgumentException("A metric named " + name + " already exists"); + } + return metric; + } + + /** + * A quick and easy way of capturing the notion of default metrics. + */ + private interface MetricBuilder<T extends Metric> { + MetricBuilder<Counter> COUNTERS = new MetricBuilder<Counter>() { + @Override + public Counter newMetric(MetricRegistry registry, String name) { + return new CounterImpl(registry.counter(name)); + } + + @Override + public boolean isInstance(Metric metric) { + return Counter.class.isInstance(metric); + } + }; + + MetricBuilder<Histogram> HISTOGRAMS = new MetricBuilder<Histogram>() { + @Override + public Histogram newMetric(MetricRegistry registry, String name) { + return new HistogramImpl(registry.histogram(name)); + } + + @Override + public boolean isInstance(Metric metric) { + return Histogram.class.isInstance(metric); + } + }; + + MetricBuilder<Meter> METERS = new MetricBuilder<Meter>() { + @Override + public Meter newMetric(MetricRegistry registry, String name) { + return new MeterImpl(registry.meter(name)); + } + + @Override + public boolean isInstance(Metric metric) { + return Meter.class.isInstance(metric); + } + }; + + MetricBuilder<Timer> TIMERS = new MetricBuilder<Timer>() { + @Override + public Timer newMetric(MetricRegistry registry, String name) { + return new TimerImpl(registry.timer(name)); + } + + @Override + public boolean isInstance(Metric metric) { + return Timer.class.isInstance(metric); + } + }; + + T newMetric(MetricRegistry registry, String name); + + boolean isInstance(Metric metric); + } +} diff --git a/src/main/java/org/apache/sling/metrics/internal/TimerImpl.java b/src/main/java/org/apache/sling/metrics/internal/TimerImpl.java new file mode 100644 index 0000000..49aaac3 --- /dev/null +++ b/src/main/java/org/apache/sling/metrics/internal/TimerImpl.java @@ -0,0 +1,77 @@ +/* + * 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.sling.metrics.internal; + +import java.util.concurrent.TimeUnit; + +import org.apache.sling.metrics.Timer; + + +final class TimerImpl implements Timer { + private final com.codahale.metrics.Timer timer; + + TimerImpl(com.codahale.metrics.Timer timer) { + this.timer = timer; + } + + @Override + public void update(long duration, TimeUnit unit) { + timer.update(duration, unit); + } + + @Override + public Context time() { + return new ContextImpl(timer.time()); + } + + @Override + public long getCount() { + return timer.getCount(); + } + + @SuppressWarnings("unchecked") + @Override + public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) { + if (type == com.codahale.metrics.Timer.class) { + return (AdapterType) timer; + } + return null; + } + + private static final class ContextImpl implements Context { + private final com.codahale.metrics.Timer.Context context; + + private ContextImpl(com.codahale.metrics.Timer.Context context) { + this.context = context; + } + + public long stop() { + return context.stop(); + } + + /** + * Equivalent to calling {@link #stop()}. + */ + @Override + public void close() { + stop(); + } + } +} diff --git a/src/test/java/org/apache/sling/metrics/internal/MetricServiceTest.java b/src/test/java/org/apache/sling/metrics/internal/MetricServiceTest.java new file mode 100644 index 0000000..e62daad --- /dev/null +++ b/src/test/java/org/apache/sling/metrics/internal/MetricServiceTest.java @@ -0,0 +1,120 @@ +/* + * 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.sling.metrics.internal; + +import java.lang.management.ManagementFactory; +import java.util.Collections; + +import javax.management.MBeanServer; + +import com.codahale.metrics.MetricRegistry; +import org.apache.sling.metrics.Counter; +import org.apache.sling.metrics.Histogram; +import org.apache.sling.metrics.Meter; +import org.apache.sling.metrics.MetricsService; +import org.apache.sling.metrics.Timer; +import org.apache.sling.testing.mock.osgi.MockOsgi; +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class MetricServiceTest { + @Rule + public final OsgiContext context = new OsgiContext(); + + private MetricsServiceImpl service = new MetricsServiceImpl(); + + @After + public void registerMBeanServer() { + context.registerService(MBeanServer.class, ManagementFactory.getPlatformMBeanServer()); + } + + @Test + public void defaultSetup() throws Exception{ + activate(); + + assertNotNull(context.getService(MetricRegistry.class)); + assertNotNull(context.getService(MetricsService.class)); + + MockOsgi.deactivate(service); + + assertNull(context.getService(MetricRegistry.class)); + assertNull(context.getService(MetricsService.class)); + } + + @Test + public void meter() throws Exception{ + activate(); + Meter meter = service.meter("test"); + + assertNotNull(meter); + assertTrue(getRegistry().getMeters().containsKey("test")); + + assertSame(meter, service.meter("test")); + } + + @Test + public void counter() throws Exception{ + activate(); + Counter counter = service.counter("test"); + + assertNotNull(counter); + assertTrue(getRegistry().getCounters().containsKey("test")); + + assertSame(counter, service.counter("test")); + } + + @Test + public void timer() throws Exception{ + activate(); + Timer timer = service.timer("test"); + + assertNotNull(timer); + assertTrue(getRegistry().getTimers().containsKey("test")); + + assertSame(timer, service.timer("test")); + } + + @Test + public void histogram() throws Exception{ + activate(); + Histogram histo = service.histogram("test"); + + assertNotNull(histo); + assertTrue(getRegistry().getHistograms().containsKey("test")); + + assertSame(histo, service.histogram("test")); + } + + private MetricRegistry getRegistry(){ + return context.getService(MetricRegistry.class); + } + + private void activate() { + MockOsgi.activate(service, context.bundleContext(), Collections.<String, Object>emptyMap()); + } + +} diff --git a/src/test/java/org/apache/sling/metrics/internal/MetricWrapperTest.java b/src/test/java/org/apache/sling/metrics/internal/MetricWrapperTest.java new file mode 100644 index 0000000..898626c --- /dev/null +++ b/src/test/java/org/apache/sling/metrics/internal/MetricWrapperTest.java @@ -0,0 +1,132 @@ +/* + * 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.sling.metrics.internal; + +import java.util.concurrent.TimeUnit; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.ExponentiallyDecayingReservoir; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +public class MetricWrapperTest { + private MetricRegistry registry = new MetricRegistry(); + + @Test + public void counter() throws Exception { + Counter counter = registry.counter("test"); + CounterImpl counterStats = new CounterImpl(counter); + + counterStats.inc(); + assertEquals(1, counterStats.getCount()); + assertEquals(1, counter.getCount()); + assertEquals(1, counterStats.getCount()); + + counterStats.inc(); + counterStats.inc(); + assertEquals(3, counterStats.getCount()); + + counterStats.dec(); + assertEquals(2, counterStats.getCount()); + assertEquals(2, counter.getCount()); + + counterStats.inc(7); + assertEquals(9, counterStats.getCount()); + assertEquals(9, counter.getCount()); + + counterStats.dec(5); + assertEquals(4, counterStats.getCount()); + assertEquals(4, counter.getCount()); + + assertSame(counter, counterStats.adaptTo(Counter.class)); + } + + @Test + public void meter() throws Exception { + Meter meter = registry.meter("test"); + MeterImpl meterStats = new MeterImpl(meter); + + meterStats.mark(); + assertEquals(1, meterStats.getCount()); + assertEquals(1, meter.getCount()); + + meterStats.mark(5); + assertEquals(6, meterStats.getCount()); + assertEquals(6, meter.getCount()); + assertSame(meter, meterStats.adaptTo(Meter.class)); + } + + @Test + public void timer() throws Exception { + Timer time = registry.timer("test"); + TimerImpl timerStats = new TimerImpl(time); + + timerStats.update(100, TimeUnit.SECONDS); + assertEquals(1, time.getCount()); + assertEquals(TimeUnit.SECONDS.toNanos(100), time.getSnapshot().getMax()); + + timerStats.update(100, TimeUnit.SECONDS); + assertEquals(2, timerStats.getCount()); + + assertSame(time, timerStats.adaptTo(Timer.class)); + } + + @Test + public void histogram() throws Exception { + Histogram histo = registry.histogram("test"); + HistogramImpl histoStats = new HistogramImpl(histo); + + histoStats.update(100); + assertEquals(1, histo.getCount()); + assertEquals(1, histoStats.getCount()); + assertEquals(100, histo.getSnapshot().getMax()); + + assertSame(histo, histoStats.adaptTo(Histogram.class)); + } + + @Test + public void timerContext() throws Exception{ + VirtualClock clock = new VirtualClock(); + Timer time = new Timer(new ExponentiallyDecayingReservoir(), clock); + + TimerImpl timerStats = new TimerImpl(time); + org.apache.sling.metrics.Timer.Context context = timerStats.time(); + + clock.tick = TimeUnit.SECONDS.toNanos(314); + context.close(); + + assertEquals(1, time.getCount()); + assertEquals(TimeUnit.SECONDS.toNanos(314), time.getSnapshot().getMax()); + } + + private static class VirtualClock extends com.codahale.metrics.Clock { + long tick; + @Override + public long getTick() { + return tick; + } + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
