diff --git a/core/src/main/java/com/kumuluz/ee/metrics/MetricsExtension.java b/core/src/main/java/com/kumuluz/ee/metrics/MetricsExtension.java index c9e8561..73b98c6 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/MetricsExtension.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/MetricsExtension.java @@ -32,13 +32,9 @@ import com.kumuluz.ee.common.dependencies.*; import com.kumuluz.ee.common.wrapper.KumuluzServerWrapper; import com.kumuluz.ee.configuration.utils.ConfigurationUtil; -import com.kumuluz.ee.metrics.api.CounterImpl; -import com.kumuluz.ee.metrics.api.HistogramImpl; -import com.kumuluz.ee.metrics.api.MeterImpl; -import com.kumuluz.ee.metrics.api.TimerImpl; +import com.kumuluz.ee.metrics.api.*; import com.kumuluz.ee.metrics.filters.InstrumentedFilter; import com.kumuluz.ee.metrics.producers.MetricRegistryProducer; -import com.kumuluz.ee.metrics.api.ForwardingCounter; import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.metrics.*; diff --git a/core/src/main/java/com/kumuluz/ee/metrics/MetricsExtensionCDI.java b/core/src/main/java/com/kumuluz/ee/metrics/MetricsExtensionCDI.java index 04ad60a..6f11ec1 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/MetricsExtensionCDI.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/MetricsExtensionCDI.java @@ -67,7 +67,7 @@ public class MetricsExtensionCDI implements Extension { new AnnotationLiteral() { }; - private void registerMetrics(@Observes @WithAnnotations({Counted.class, Metered.class, Timed.class, ConcurrentGauge.class}) + private void registerMetrics(@Observes @WithAnnotations({Counted.class, Metered.class, Timed.class, ConcurrentGauge.class, SimplyTimed.class}) ProcessAnnotatedType pat) { AnnotatedTypeDecorator decoratedType = new AnnotatedTypeDecorator<>(pat.getAnnotatedType(), REGISTER_METRICS_BINDING); diff --git a/core/src/main/java/com/kumuluz/ee/metrics/api/MetricRegistryImpl.java b/core/src/main/java/com/kumuluz/ee/metrics/api/MetricRegistryImpl.java index 94cc375..c60b41a 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/api/MetricRegistryImpl.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/api/MetricRegistryImpl.java @@ -185,6 +185,26 @@ public Timer timer(Metadata metadata, Tag... tags) { return getOrAdd(new TimerImpl(), Timer.class, metadata, tags); } + @Override + public SimpleTimer simpleTimer(String name) { + return simpleTimer(name, new Tag[0]); + } + + @Override + public SimpleTimer simpleTimer(String name, Tag... tags) { + return simpleTimer(Metadata.builder().withName(name).withType(MetricType.SIMPLE_TIMER).build(), tags); + } + + @Override + public SimpleTimer simpleTimer(Metadata metadata) { + return simpleTimer(metadata, new Tag[0]); + } + + @Override + public SimpleTimer simpleTimer(Metadata metadata, Tag... tags) { + return getOrAdd(new SimpleTimerImpl(), SimpleTimer.class, metadata, tags); + } + @Override public synchronized boolean remove(String name) { @@ -304,6 +324,16 @@ public SortedMap getTimers(MetricFilter metricFilter) { return getMetricsOfType(metricFilter, Timer.class); } + @Override + public SortedMap getSimpleTimers() { + return getSimpleTimers(MetricFilter.ALL); + } + + @Override + public SortedMap getSimpleTimers(MetricFilter metricFilter) { + return getMetricsOfType(metricFilter, SimpleTimer.class); + } + @Override public Map getMetrics() { return Collections.unmodifiableMap(this.metricsStorage); diff --git a/core/src/main/java/com/kumuluz/ee/metrics/api/SimpleTimerContextImpl.java b/core/src/main/java/com/kumuluz/ee/metrics/api/SimpleTimerContextImpl.java new file mode 100644 index 0000000..ea77797 --- /dev/null +++ b/core/src/main/java/com/kumuluz/ee/metrics/api/SimpleTimerContextImpl.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014-2020 Kumuluz and/or its affiliates + * and other contributors as indicated by the @author tags and + * the contributor list. + * + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/MIT + * + * The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. in no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the software or the use or other dealings in the + * software. See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.kumuluz.ee.metrics.api; + +import com.codahale.metrics.Clock; +import org.eclipse.microprofile.metrics.SimpleTimer; + +import java.util.concurrent.TimeUnit; + +/** + * Microprofile SimpleTimer.Context implementation. + * + * @author Aljaž Pavišič + * @since 2.3.0 + */ +public class SimpleTimerContextImpl implements SimpleTimer.Context, AutoCloseable { + + private final SimpleTimerImpl simpleTimer; + private final Clock clock; + private final long startTime; + + public SimpleTimerContextImpl(SimpleTimerImpl simpleTimer, Clock clock){ + this.simpleTimer = simpleTimer; + this.clock = clock; + this.startTime = clock.getTick(); + } + + @Override + public long stop() { + final long elapsed = clock.getTick() - startTime; + simpleTimer.update(elapsed, TimeUnit.NANOSECONDS); + return elapsed; + } + + @Override + public void close() { + stop(); + } +} diff --git a/core/src/main/java/com/kumuluz/ee/metrics/api/SimpleTimerImpl.java b/core/src/main/java/com/kumuluz/ee/metrics/api/SimpleTimerImpl.java new file mode 100644 index 0000000..bb48717 --- /dev/null +++ b/core/src/main/java/com/kumuluz/ee/metrics/api/SimpleTimerImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014-2020 Kumuluz and/or its affiliates + * and other contributors as indicated by the @author tags and + * the contributor list. + * + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/MIT + * + * The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. in no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the software or the use or other dealings in the + * software. See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.kumuluz.ee.metrics.api; + +import com.codahale.metrics.Clock; +import org.eclipse.microprofile.metrics.SimpleTimer; + +import java.time.Duration; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.LongAdder; + +/** + * Microprofile SimpleTimer implementation. + * + * @author Aljaž Pavišič + * @since 2.3.0 + */ +public class SimpleTimerImpl implements SimpleTimer { + + private Clock clock; + + private Duration elapsedTime; + + private LongAdder count; + + public SimpleTimerImpl() { + this.clock = Clock.defaultClock(); + this.elapsedTime = Duration.ZERO; + this.count = new LongAdder(); + } + + @Override + public void update(Duration duration) { + inc(1); + this.elapsedTime = this.elapsedTime.plus(duration); + } + + @Override + public T time(Callable callable) throws Exception { + final long startTime = clock.getTick(); + try { + return callable.call(); + } + finally { + update(clock.getTick() - startTime); + } + } + + @Override + public void time(Runnable runnable) { + final long startTime = clock.getTick(); + try { + runnable.run(); + } finally { + update(clock.getTick() - startTime); + } + } + + @Override + public Context time() { + return new SimpleTimerContextImpl(this, clock); + } + + @Override + public Duration getElapsedTime() { + return this.elapsedTime; + } + + @Override + public long getCount() { + return this.count.sum(); + } + + public void update(long duration, TimeUnit timeUnit){ + update(timeUnit.toNanos(duration)); + } + + public void inc(long n){ + this.count.add(n); + } + + public void dec(long n){ + this.count.add(-n); + } + + private void update(long duration) { + if (duration > 0) { + inc(1); + this.elapsedTime = this.elapsedTime.plusNanos(duration); + } + } +} diff --git a/core/src/main/java/com/kumuluz/ee/metrics/interceptors/RegisterMetricsInterceptor.java b/core/src/main/java/com/kumuluz/ee/metrics/interceptors/RegisterMetricsInterceptor.java index b3dead5..ddd3b8d 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/interceptors/RegisterMetricsInterceptor.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/interceptors/RegisterMetricsInterceptor.java @@ -20,19 +20,13 @@ */ package com.kumuluz.ee.metrics.interceptors; -import com.kumuluz.ee.metrics.api.ConcurrentGaugeImpl; -import com.kumuluz.ee.metrics.api.CounterImpl; -import com.kumuluz.ee.metrics.api.MeterImpl; -import com.kumuluz.ee.metrics.api.TimerImpl; +import com.kumuluz.ee.metrics.api.*; import com.kumuluz.ee.metrics.interceptors.utils.RegisterMetricsBinding; import com.kumuluz.ee.metrics.utils.AnnotationMetadata; import com.kumuluz.ee.metrics.utils.MetadataWithTags; import org.eclipse.microprofile.metrics.MetricRegistry; import org.eclipse.microprofile.metrics.MetricType; -import org.eclipse.microprofile.metrics.annotation.ConcurrentGauge; -import org.eclipse.microprofile.metrics.annotation.Counted; -import org.eclipse.microprofile.metrics.annotation.Metered; -import org.eclipse.microprofile.metrics.annotation.Timed; +import org.eclipse.microprofile.metrics.annotation.*; import javax.annotation.Priority; import javax.inject.Inject; @@ -97,6 +91,10 @@ private void registerMetrics(Class bean MetadataWithTags m = AnnotationMetadata.buildMetadata(bean, element, Timed.class, MetricType.TIMER); registry.register(m.getMetadata(), new TimerImpl(), m.getTags()); } + if (AnnotationMetadata.getAnnotation(bean, element, SimplyTimed.class) != null) { + MetadataWithTags m = AnnotationMetadata.buildMetadata(bean, element, SimplyTimed.class, MetricType.SIMPLE_TIMER); + registry.register(m.getMetadata(), new SimpleTimerImpl(), m.getTags()); + } if (AnnotationMetadata.getAnnotation(bean, element, Metered.class) != null) { MetadataWithTags m = AnnotationMetadata.buildMetadata(bean, element, Metered.class, MetricType.METERED); registry.register(m.getMetadata(), new MeterImpl(), m.getTags()); diff --git a/core/src/main/java/com/kumuluz/ee/metrics/interceptors/SimplyTimedInterceptor.java b/core/src/main/java/com/kumuluz/ee/metrics/interceptors/SimplyTimedInterceptor.java new file mode 100644 index 0000000..5ba91a7 --- /dev/null +++ b/core/src/main/java/com/kumuluz/ee/metrics/interceptors/SimplyTimedInterceptor.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014-2020 Kumuluz and/or its affiliates + * and other contributors as indicated by the @author tags and + * the contributor list. + * + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/MIT + * + * The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. in no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the software or the use or other dealings in the + * software. See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.kumuluz.ee.metrics.interceptors; + +import com.kumuluz.ee.metrics.utils.AnnotationMetadata; +import com.kumuluz.ee.metrics.utils.MetadataWithTags; +import org.eclipse.microprofile.metrics.MetricRegistry; +import org.eclipse.microprofile.metrics.MetricType; +import org.eclipse.microprofile.metrics.SimpleTimer; +import org.eclipse.microprofile.metrics.annotation.SimplyTimed; + +import javax.annotation.Priority; +import javax.enterprise.inject.Intercepted; +import javax.enterprise.inject.spi.Bean; +import javax.inject.Inject; +import javax.interceptor.AroundConstruct; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InvocationContext; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Member; + +/** + * Interceptor for SimplyTimed annotation. + * + * @author Aljaž Pavišič + * @since 2.3.0 + */ +@Interceptor +@SimplyTimed +@Priority(Interceptor.Priority.LIBRARY_BEFORE) +public class SimplyTimedInterceptor { + @Inject + private MetricRegistry applicationRegistry; + + private Bean bean; + + @Inject + private SimplyTimedInterceptor(@Intercepted Bean bean) { + this.bean = bean; + } + + @AroundConstruct + private Object simplyTimedConstructor(InvocationContext context) throws Exception { + return applyInterceptor(context, context.getConstructor()); + } + + @AroundInvoke + private Object simplyTimedMethod(InvocationContext context) throws Exception { + return applyInterceptor(context, context.getMethod()); + } + + private Object applyInterceptor(InvocationContext context, E member) + throws Exception { + MetadataWithTags metadata = AnnotationMetadata.buildMetadata(bean.getBeanClass(), member, SimplyTimed.class, MetricType.SIMPLE_TIMER); + SimpleTimer simpleTimer = applicationRegistry.getSimpleTimers().get(metadata.getMetricID()); + if (simpleTimer == null) { + throw new IllegalStateException("No simple timer with ID [" + metadata.getMetricID() + "] found in registry [" + + applicationRegistry + "]"); + } + + SimpleTimer.Context simpleTimerContext = simpleTimer.time(); + + try { + return context.proceed(); + } finally { + simpleTimerContext.stop(); + } + } +} diff --git a/core/src/main/java/com/kumuluz/ee/metrics/json/serializers/MetricsCollectionSerializer.java b/core/src/main/java/com/kumuluz/ee/metrics/json/serializers/MetricsCollectionSerializer.java index 214fdcf..7cc4a38 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/json/serializers/MetricsCollectionSerializer.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/json/serializers/MetricsCollectionSerializer.java @@ -101,6 +101,11 @@ private void serializeMetric(MetricID metricID, Metric metric, JsonGenerator jso Snapshot snapshot = timer.getSnapshot(); serializeMetered(timer, compositeSuffix, json); serializeSnapshot(snapshot, compositeSuffix, json); + } else if (metric instanceof SimpleTimer) { + SimpleTimer simpleTimer = (SimpleTimer) metric; + + json.writeObjectField("count" + compositeSuffix, simpleTimer.getCount()); + json.writeObjectField("elapsedTime" + compositeSuffix, simpleTimer.getElapsedTime()); } else if (metric instanceof ConcurrentGauge) { ConcurrentGauge concurrentGauge = (ConcurrentGauge) metric; diff --git a/core/src/main/java/com/kumuluz/ee/metrics/producers/MetricProducer.java b/core/src/main/java/com/kumuluz/ee/metrics/producers/MetricProducer.java index 0829903..b79d9ef 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/producers/MetricProducer.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/producers/MetricProducer.java @@ -59,6 +59,12 @@ public Timer produceTimer(InjectionPoint injectionPoint) { return applicationRegistry.timer(metadataWithTags.getMetadata(), metadataWithTags.getTags()); } + @Produces + public SimpleTimer produceSimpleTimer(InjectionPoint injectionPoint){ + MetadataWithTags metadataWithTags = AnnotationMetadata.buildProducerMetadata(injectionPoint, MetricType.SIMPLE_TIMER); + return applicationRegistry.simpleTimer(metadataWithTags.getMetadata(), metadataWithTags.getTags()); + } + @Produces public Counter produceCounter(InjectionPoint injectionPoint) { MetadataWithTags metadataWithTags = AnnotationMetadata.buildProducerMetadata(injectionPoint, MetricType.COUNTER); diff --git a/core/src/main/java/com/kumuluz/ee/metrics/prometheus/PrometheusBuilder.java b/core/src/main/java/com/kumuluz/ee/metrics/prometheus/PrometheusBuilder.java index 41b77dc..349c28f 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/prometheus/PrometheusBuilder.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/prometheus/PrometheusBuilder.java @@ -75,6 +75,17 @@ public static void buildTimer(StringBuilder builder, String name, Timer timer, S buildSampling(builder, name, timer, description, conversionFactor, tags, "_seconds"); } + public static void buildSimpleTimer(StringBuilder builder, String name, SimpleTimer simpleTimer, String description, String tags) { + buildCounting(builder, name, simpleTimer, description, tags); + double conversionFactor = 0.000000001; + + String lineName = name + "_elapsedTime"; + double value = simpleTimer.getElapsedTime().toNanos() * conversionFactor; + + getPromTypeLine(builder, lineName, "simpleTimer"); + getPromValueLine(builder, lineName, value, tags, "_seconds"); + } + public static void buildHistogram(StringBuilder builder, String name, Histogram histogram, String description, Double conversionFactor, String tags, String appendUnit) { // Build Histogram diff --git a/core/src/main/java/com/kumuluz/ee/metrics/prometheus/PrometheusMetricWriter.java b/core/src/main/java/com/kumuluz/ee/metrics/prometheus/PrometheusMetricWriter.java index cde924a..54d34d4 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/prometheus/PrometheusMetricWriter.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/prometheus/PrometheusMetricWriter.java @@ -212,6 +212,8 @@ private void writeMetricMapAsPrometheus(StringBuilder builder, String registryNa conversionFactor, tags, appendUnit); } else if (Timer.class.isInstance(metric)) { PrometheusBuilder.buildTimer(builder, metricNamePrometheus, (Timer) metric, description, tags); + } else if (SimpleTimer.class.isInstance(metric)){ + PrometheusBuilder.buildSimpleTimer(builder, metricNamePrometheus, (SimpleTimer) metric, description, tags); } else if (Histogram.class.isInstance(metric)) { PrometheusBuilder.buildHistogram(builder, metricNamePrometheus, (Histogram) metric, description, conversionFactor, tags, appendUnit); diff --git a/core/src/main/java/com/kumuluz/ee/metrics/utils/AnnotationMetadata.java b/core/src/main/java/com/kumuluz/ee/metrics/utils/AnnotationMetadata.java index 7f590c3..f62ff14 100644 --- a/core/src/main/java/com/kumuluz/ee/metrics/utils/AnnotationMetadata.java +++ b/core/src/main/java/com/kumuluz/ee/metrics/utils/AnnotationMetadata.java @@ -104,6 +104,16 @@ private static MetadataWithTags buildMe description = a.description(); unit = a.unit(); reusable = a.reusable(); + } else if (annotation instanceof SimplyTimed) { + SimplyTimed a = (SimplyTimed) annotation; + type = MetricType.SIMPLE_TIMER; + absolute = a.absolute(); + name = a.name(); + tags = a.tags(); + displayName = a.displayName(); + description = a.description(); + unit = a.unit(); + reusable = a.reusable(); } else if (annotation instanceof Metered) { Metered a = (Metered) annotation; type = MetricType.METERED; diff --git a/pom.xml b/pom.xml index f1606de..ec3fd83 100644 --- a/pom.xml +++ b/pom.xml @@ -30,10 +30,10 @@ 2.3.1 - 3.6.0 + 3.8.0 1.3.0 - 2.2.1 + 2.3 4.1.0 2.9.9