- * If this number is exceeded, a warning will be printed and points will be rejected until space is free again.
- */
- @Min(1)
- @Builder.Default
- private int maxBufferedPoints = 16384;
-
- /**
- * Defines if this view should by default include all common tags.
- * Individual tags can still be disabled via {@link #tags}.
- */
- @Builder.Default
- private boolean withCommonTags = true;
-
- /**
- * Specifies which tags should be used for this view.
- */
- @Singular
- private Map<@NotBlank String, @NotNull Boolean> tags;
-
- public ViewDefinitionSettings getCopyWithDefaultsPopulated(String measureDescription, String unit, Duration defaultTimeWindow) {
- val result = toBuilder();
- if (description == null) {
- result.description(aggregation.getReadableName() + " of " + measureDescription + " [" + unit + "]");
- }
- if (timeWindow == null) {
- if (defaultTimeWindow == null && aggregation == Aggregation.QUANTILES) {
- throw new IllegalArgumentException("A default time window must be provided for quantile views");
- }
- result.timeWindow(defaultTimeWindow);
- }
- return result.build();
- }
-
- @AssertFalse(message = "When using QUANTILES aggregation you must specify the quantiles to use!") boolean isQuantilesNotSpecifiedForercentileType() {
- return enabled && aggregation == Aggregation.QUANTILES && CollectionUtils.isEmpty(quantiles);
- }
-
- @AssertFalse(message = "When using HISTOGRAM aggregation you must specify the bucket-boundaries!") boolean isBucketBoundariesNotSpecifiedForHistogram() {
- return enabled && aggregation == Aggregation.HISTOGRAM && CollectionUtils.isEmpty(bucketBoundaries);
- }
-
- @AssertTrue(message = "When using HISTOGRAM the specified bucket-boundaries must be sorted in ascending order and must contain each value at most once!") boolean isBucketBoundariesSorted() {
- if (enabled && aggregation == Aggregation.HISTOGRAM && !CollectionUtils.isEmpty(bucketBoundaries)) {
- Double previous = null;
- for (double boundary : bucketBoundaries) {
- if (previous != null && previous >= boundary) {
- return false;
- }
- previous = boundary;
- }
- }
- return true;
- }
-
- @AssertTrue(message = "The quantiles must be in the range [0,1]") boolean isQuantilesInRange() {
- return !enabled || aggregation != Aggregation.QUANTILES || quantiles.stream().noneMatch(q -> q < 0 || q > 1);
- }
-
-}
diff --git a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/selfmonitoring/SelfMonitoringSettings.java b/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/selfmonitoring/SelfMonitoringSettings.java
deleted file mode 100644
index 2b915e6..0000000
--- a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/selfmonitoring/SelfMonitoringSettings.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package rocks.inspectit.oce.eum.server.configuration.model.selfmonitoring;
-
-import lombok.Data;
-import lombok.Singular;
-import org.springframework.validation.annotation.Validated;
-import rocks.inspectit.oce.eum.server.configuration.model.metric.definition.MetricDefinitionSettings;
-
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * Self-monitoring settings.
- */
-@Data
-@Validated
-public class SelfMonitoringSettings {
-
- /**
- * If self-monitoring is enabled.
- */
- private boolean enabled;
-
- /**
- * Definition of the self-monitoring metrics.
- */
- @Singular
- private Map<@NotBlank String, @Valid @NotNull MetricDefinitionSettings> metrics = Collections.emptyMap();
-
- /**
- * The prefix used for the self-monitoring metrics.
- */
- private String metricPrefix;
-}
diff --git a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/tags/providers/EnvironmentTagsProviderSettings.java b/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/tags/providers/EnvironmentTagsProviderSettings.java
deleted file mode 100644
index 0539884..0000000
--- a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/tags/providers/EnvironmentTagsProviderSettings.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package rocks.inspectit.oce.eum.server.configuration.model.tags.providers;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@NoArgsConstructor
-public class EnvironmentTagsProviderSettings {
-
- /**
- * If providers is enabled.
- */
- private boolean enabled;
-
- /**
- * If true tries to resolve the host name using {@link java.net.InetAddress}.
- */
- private boolean resolveHostName;
-
- /**
- * If true tries to resolve the host address using {@link java.net.InetAddress}.
- */
- private boolean resolveHostAddress;
-
-}
diff --git a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/tags/providers/TagsProvidersSettings.java b/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/tags/providers/TagsProvidersSettings.java
deleted file mode 100644
index 3b55546..0000000
--- a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/tags/providers/TagsProvidersSettings.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package rocks.inspectit.oce.eum.server.configuration.model.tags.providers;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import jakarta.validation.Valid;
-
-@Data
-@NoArgsConstructor
-public class TagsProvidersSettings {
-
- /**
- * The environment tags providers.
- */
- @Valid
- private EnvironmentTagsProviderSettings environment;
-
-}
diff --git a/src/main/java/rocks/inspectit/oce/eum/server/events/RegisteredTagsEvent.java b/src/main/java/rocks/inspectit/oce/eum/server/events/RegisteredTagsEvent.java
deleted file mode 100644
index d0ecfec..0000000
--- a/src/main/java/rocks/inspectit/oce/eum/server/events/RegisteredTagsEvent.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package rocks.inspectit.oce.eum.server.events;
-
-import lombok.Getter;
-import org.springframework.context.ApplicationEvent;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Event that is sent when new tags are registered. This usually happens when new metric views are registered.
- */
-public class RegisteredTagsEvent extends ApplicationEvent {
-
- @Getter
- private final Set
- * Consumer thread for asynchronously processing measurement observations.
- */
-@Slf4j
-class AsyncMetricRecorder {
-
- private static final int QUEUE_CAPACITY = 8096;
-
- private final MetricConsumer metricConsumer;
-
- private volatile boolean overflowLogged = false;
-
- private volatile boolean isDestroyed = false;
-
- @VisibleForTesting
- final ArrayBlockingQueue
- * A metric producer which caches the metrics for a specified amount of time.
- */
-public class CachingMetricProducer extends MetricProducer {
-
- /**
- * The function invoked to generate the metrics.
- */
- private final Supplier
- * Holds the data for a given measurement splitted by a provided set of tags over a given time window.
- */
-@Slf4j
-public abstract class TimeWindowView {
-
- private static final Duration CLEANUP_INTERVAL = Duration.ofSeconds(1);
-
- /**
- * Defines the tags which are used for the view.
- * E.g. if the tag "http_path" is used, percentiles will be computed for each http_path individually.
- *
- * The tag values are stored in a fixed order in the keys of {@link #seriesValues} for each series.
- * The values within {@link #tagIndices} define at which position within these arrays the corresponding tag value is found.
- * E.g. if tagIndex["http_path"] = 2, this means that the values for http_path will be at index 2 in the keys of {@link #seriesValues}.
- */
- private Map
- * Allows the creation, update and removal of percentile views on metrics.
- * Note that these views coexist to opencensus {@link io.opencensus.stats.View}s.
- * For this reason observation must be reported via {@link #recordMeasurement(String, double)}
- * in addition to {@link MeasureMap#record()}.
- */
-@Component
-public class TimeWindowViewManager {
-
- /**
- * Maps the name of measures to registered percentile views.
- */
- private ConcurrentHashMap
* The impl depends heavily on the Boomerang compression of the resource timing entries in the beacon. Please read
- * ResourceTiming
+ * ResourceTiming
* Boomerang documentation first.
*/
@Component
-@ConditionalOnProperty(value = "inspectit-eum-server.resource-timing.enabled", havingValue = "true")
+@ConditionalOnProperty(value = "inspectit-eum-server.definitions.resource_time.enabled", havingValue = "true")
@RequiredArgsConstructor
@Slf4j
public class ResourceTimingBeaconRecorder implements BeaconRecorder {
@@ -50,10 +50,10 @@ public class ResourceTimingBeaconRecorder implements BeaconRecorder {
private final ObjectMapper objectMapper;
/**
- * {@link MeasuresAndViewsManager} for exposing metrics.
+ * {@link InstrumentManager} for exposing metrics
*/
@Autowired
- private final MeasuresAndViewsManager measuresAndViewsManager;
+ private final InstrumentManager instrumentManager;
@Autowired
private final EumServerConfiguration configuration;
@@ -64,38 +64,17 @@ public class ResourceTimingBeaconRecorder implements BeaconRecorder {
public final String RESOURCE_TIME_METRIC_NAME = "resource_time";
/**
- * Metric definition for the resource timing metric.
+ * The raw expression to extract resource timing data from the beacon.
+ * Default: {@code {restiming}}
*/
- private MetricDefinitionSettings RESOURCE_TIME;
+ private RawExpression resourceTimeExpression = new RawExpression("{restiming}");
- /**
- * Init metric(s).
- */
@PostConstruct
- public void initMetric() {
- Map
+ * If this number is exceeded, a warning will be printed and points will be rejected until space is free again.
+ */
+ @Min(1)
+ @Builder.Default
+ private int maxBufferedPoints = 16384;
+
+ /**
+ * Defines if this view should by default include all common attributes.
+ * Individual attributes can still be disabled via {@link #attributes}.
+ */
+ @Builder.Default
+ private boolean withCommonAttributes = true;
+
+ /**
+ * Specifies which attributes should be used for this view.
+ */
+ @Singular
+ private Map<@NotBlank String, @NotNull Boolean> attributes;
+
+ public ViewDefinitionSettings getCopyWithDefaultsPopulated(String metricDescription, String unit) {
+ val result = toBuilder();
+ if (description == null) {
+ result.description(aggregation.getReadableName() + " of " + metricDescription + " [" + unit + "]");
+ }
+ return result.build();
+ }
+
+ @AssertFalse(message = "When using QUANTILES aggregation you must specify the quantiles to use!")
+ boolean isQuantilesNotSpecifiedForQuantileType() {
+ return enabled && aggregation == AggregationType.QUANTILES && CollectionUtils.isEmpty(quantiles);
+ }
+
+ @AssertFalse(message = "When using HISTOGRAM aggregation you must specify the bucket-boundaries!")
+ boolean isBucketBoundariesNotSpecifiedForHistogram() {
+ return enabled && aggregation == AggregationType.HISTOGRAM && CollectionUtils.isEmpty(bucketBoundaries);
+ }
+
+ @AssertTrue(message = "When using HISTOGRAM the specified bucket-boundaries must be sorted in ascending order and must contain each value at most once!")
+ boolean isBucketBoundariesSorted() {
+ if (enabled && aggregation == AggregationType.HISTOGRAM && !CollectionUtils.isEmpty(bucketBoundaries)) {
+ Double previous = null;
+ for (double boundary : bucketBoundaries) {
+ if (previous != null && previous >= boundary) {
+ return false;
+ }
+ previous = boundary;
+ }
+ }
+ return true;
+ }
+
+ @AssertTrue(message = "The quantiles must be in the range [0,1]")
+ boolean isQuantilesInRange() {
+ return !enabled || aggregation != AggregationType.QUANTILES || quantiles.stream().noneMatch(q -> q < 0 || q > 1);
+ }
+}
diff --git a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/SecuritySettings.java b/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/SecuritySettings.java
similarity index 76%
rename from src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/SecuritySettings.java
rename to src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/SecuritySettings.java
index 617b003..28d6eab 100644
--- a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/SecuritySettings.java
+++ b/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/SecuritySettings.java
@@ -1,8 +1,8 @@
-package rocks.inspectit.oce.eum.server.configuration.model.security;
+package rocks.inspectit.ocelot.eum.server.configuration.model.security;
import lombok.Data;
import org.springframework.validation.annotation.Validated;
-import rocks.inspectit.oce.eum.server.configuration.model.security.authProvider.AuthenticationProviderSettings;
+import rocks.inspectit.ocelot.eum.server.configuration.model.security.authProvider.AuthenticationProviderSettings;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
diff --git a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/authProvider/AuthenticationProviderSettings.java b/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/authProvider/AuthenticationProviderSettings.java
similarity index 75%
rename from src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/authProvider/AuthenticationProviderSettings.java
rename to src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/authProvider/AuthenticationProviderSettings.java
index 2b85a31..73ddb63 100644
--- a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/authProvider/AuthenticationProviderSettings.java
+++ b/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/authProvider/AuthenticationProviderSettings.java
@@ -1,4 +1,4 @@
-package rocks.inspectit.oce.eum.server.configuration.model.security.authProvider;
+package rocks.inspectit.ocelot.eum.server.configuration.model.security.authProvider;
import lombok.Data;
import org.springframework.validation.annotation.Validated;
diff --git a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/authProvider/SimpleApiTokenAuthenticationProviderSettings.java b/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/authProvider/SimpleApiTokenAuthenticationProviderSettings.java
similarity index 81%
rename from src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/authProvider/SimpleApiTokenAuthenticationProviderSettings.java
rename to src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/authProvider/SimpleApiTokenAuthenticationProviderSettings.java
index 9db2ee7..979be33 100644
--- a/src/main/java/rocks/inspectit/oce/eum/server/configuration/model/security/authProvider/SimpleApiTokenAuthenticationProviderSettings.java
+++ b/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/security/authProvider/SimpleApiTokenAuthenticationProviderSettings.java
@@ -1,4 +1,4 @@
-package rocks.inspectit.oce.eum.server.configuration.model.security.authProvider;
+package rocks.inspectit.ocelot.eum.server.configuration.model.security.authProvider;
import lombok.Data;
import org.hibernate.validator.constraints.time.DurationMin;
@@ -6,6 +6,8 @@
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.AssertTrue;
+import rocks.inspectit.ocelot.eum.server.security.authprovider.SimpleApiTokenAuthenticationProvider;
+
import java.time.Duration;
@Data
@@ -13,7 +15,7 @@
public class SimpleApiTokenAuthenticationProviderSettings {
/**
- * Flag indicates if the {@link rocks.inspectit.oce.eum.server.security.authprovider.SimpleApiTokenAuthenticationProvider} should be enabled.
+ * Flag indicates if the {@link SimpleApiTokenAuthenticationProvider} should be enabled.
*/
private boolean enabled;
diff --git a/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/selfmonitoring/SelfMonitoringSettings.java b/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/selfmonitoring/SelfMonitoringSettings.java
new file mode 100644
index 0000000..a15eed6
--- /dev/null
+++ b/src/main/java/rocks/inspectit/ocelot/eum/server/configuration/model/selfmonitoring/SelfMonitoringSettings.java
@@ -0,0 +1,66 @@
+package rocks.inspectit.ocelot.eum.server.configuration.model.selfmonitoring;
+
+import lombok.Data;
+import lombok.Singular;
+import org.springframework.validation.annotation.Validated;
+import rocks.inspectit.ocelot.eum.server.configuration.model.metrics.definition.MetricDefinitionSettings;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import rocks.inspectit.ocelot.eum.server.configuration.model.metrics.definition.view.ViewDefinitionSettings;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Self-monitoring settings.
+ */
+@Data
+@Validated
+public class SelfMonitoringSettings {
+
+ /**
+ * If self-monitoring is enabled.
+ */
+ private boolean enabled;
+
+ /**
+ * Definition of the self-monitoring metrics.
+ */
+ @Singular
+ private Map<@NotBlank String, @Valid @NotNull MetricDefinitionSettings> metrics = Collections.emptyMap();
+
+ /**
+ * The prefix used for the self-monitoring metrics.
+ */
+ private String metricPrefix;
+
+ /**
+ * Marker to tell if the {@link #metricPrefix} has been applied to views
+ */
+ private boolean prefixNotApplied = true;
+
+ /**
+ * Adds the {@link #metricPrefix} for all view names. The prefix to the metrics has to be added manually.
+ *
+ * @return the metrics with prefixed views
+ */
+ public Map
+ * The attribute values are stored in a fixed order in the keys of {@link #seriesValues} for each series.
+ * The values here define at which position within these arrays the corresponding attribute value is found.
+ * E.g. if tagIndex["http_path"] = 2, this means that the values for http_path will be at index 2 in the keys of {@link #seriesValues}.
+ */
+ private final Map
* A circular, array based FIFO-queue for remembering measurement values in a sliding window over time.
*
* A queue covers a fixed time range (e.g. 15 seconds).
@@ -51,7 +51,7 @@ public class WindowedDoubleQueue {
/**
* The size of the time window covered by this queue.
*/
- private long timeRange;
+ private final long timeRange;
/**
* Creates a new queue, covering the given amount of time.
@@ -60,10 +60,10 @@ public class WindowedDoubleQueue {
* The unit must be the same as for timestamps given to {@link #insert(double, long)}
* and {@link #removeStaleValues(long)}
*/
- public WindowedDoubleQueue(long timeRange) {
+ public WindowedDoubleQueue(Duration timeRange) {
values = new double[MIN_CAPACITY];
timeStamps = new long[MIN_CAPACITY];
- this.timeRange = timeRange;
+ this.timeRange = timeRange.toMillis();
}
/**
@@ -73,7 +73,7 @@ public WindowedDoubleQueue(long timeRange) {
* The queue expects that all inserts happen ordered in time!
* You should never insert data which is older than the latest element in the queue.
*
- * This method has an amortized O(1) runtime, with a worst case of O(n).
+ * This method has an amortized O(1) runtime with the worst case of O(n).
*
* In addition, this method is guaranteed to not alter the queue in case it throws an exception.
*
@@ -207,5 +207,4 @@ static int roundUpToPowerOfTwo(int value) {
}
return highestOneBit * 2;
}
-
}
diff --git a/src/main/java/rocks/inspectit/ocelot/eum/server/metrics/timewindow/worker/TimeWindowMetricProducer.java b/src/main/java/rocks/inspectit/ocelot/eum/server/metrics/timewindow/worker/TimeWindowMetricProducer.java
new file mode 100644
index 0000000..b687b8c
--- /dev/null
+++ b/src/main/java/rocks/inspectit/ocelot/eum/server/metrics/timewindow/worker/TimeWindowMetricProducer.java
@@ -0,0 +1,61 @@
+package rocks.inspectit.ocelot.eum.server.metrics.timewindow.worker;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.opentelemetry.sdk.metrics.data.MetricData;
+import io.opentelemetry.sdk.metrics.export.MetricProducer;
+import io.opentelemetry.sdk.resources.Resource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import rocks.inspectit.ocelot.eum.server.metrics.timewindow.TimeWindowViewManager;
+import rocks.inspectit.ocelot.eum.server.metrics.timewindow.views.TimeWindowView;
+import rocks.inspectit.ocelot.eum.server.opentelemetry.OpenTelemetryController;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collection;
+
+/**
+ * A metric producer which caches time-window metrics for a specified amount of time.
+ * The metric producer will be registered in OpenTelemetry via {@link OpenTelemetryController}. , WindowedDoubleQueue> seriesValues;
-
- /**
- * Defines the size of the sliding window in milliseconds.
- * E.g. a value of 15000 means that percentiles will be computed based on the last 15 seconds.
- */
- @Getter
- private long timeWindowMillis;
-
- /**
- * The name of the view, used as prefix for all individual metrics.
- */
- @Getter
- private String viewName;
-
- /**
- * The unit of the measure.
- */
- @Getter
- private String unit;
-
- /**
- * The description of this view.
- */
- @Getter
- private String description;
-
- /**
- * The maximum amount of measurement points to buffer.
- * If this limit is reached, new measuremetns will be rejected until there is space again.
- */
- @Getter
- private int bufferLimit;
-
- private boolean overflowWarningPrinted = false;
-
- /**
- * The current number of points stored in this view, limited by {@link #bufferLimit}.
- */
- private AtomicInteger numberOfPoints;
-
- /**
- * The timestamp when the last full cleanup happened.
- */
- private AtomicLong lastCleanupTimeMs;
-
- /**
- * Constructor.
- *
- * @param tags the tags to use for this view
- * @param timeWindowMillis the time range in milliseconds to use for computing minimum / maximum and percentile values
- * @param viewName the prefix to use for the names of all exposed metrics
- * @param unit the unit of the measure
- * @param description the description of this view
- * @param bufferLimit the maximum number of measurements to be buffered by this view
- */
- TimeWindowView(Set
, WindowedDoubleQueue> series : seriesValues.entrySet()) {
- List
<invalid> TagName is created.
- *
- * @param tagKey the tag key
- * @param value the tag value
- *
- * @return the created TagValue with 'v' or '<invalid>'
- */
- public static TagValue createTagValue(String tagKey, String value) {
- if (isTagValueValid(value)) {
- return TagValue.create(value);
- }
- printWarning(tagKey, value);
- return TagValue.create("
- *
*
+ * Note: The EUM-server cannot update metric definitions during runtime.
+ */
+@Slf4j
+@Component
+public class TimeWindowViewManager {
+
+ @Autowired
+ private EumServerConfiguration configuration;
+
+ @Autowired
+ private AttributesRegistry attributesRegistry;
+
+ /**
+ * Maps the name of measures to registered time-window views
+ */
+ private final Map, WindowedDoubleQueue> seriesValues = new ConcurrentHashMap<>();
+
+ /**
+ * The current number of points stored in this view, limited by {@link #bufferLimit}.
+ */
+ private final AtomicInteger numberOfPoints = new AtomicInteger(0);
+
+ /**
+ * The timestamp when the last full cleanup happened.
+ */
+ private final AtomicLong lastCleanupTimeMs = new AtomicLong(0);
+
+ /**
+ * Defines the attributes which are used for the view. Only these attributes can be recorded for the view!
+ * E.g. if the attribute "http_path" is used, quantiles will be computed for each http_path individually.
+ *
, WindowedDoubleQueue> series : seriesValues.entrySet()) {
+ List
+ * Computation of percentiles can be expensive.
+ * For this reason we cache computed metrics for 1 second before recomputing them.
+ * Otherwise, e.g. spamming F5 on the prometheus endpoint could lead to an increased CPU usage.
+ */
+@Component
+public class TimeWindowMetricProducer implements MetricProducer {
+
+ @Autowired
+ private TimeWindowViewManager viewManager;
+
+ /**
+ * The duration for which cached metrics are kept.
+ */
+ private final Duration cacheDuration = Duration.ofSeconds(1);
+
+ /**
+ * The timestamp when the metrics were computed the last time.
+ */
+ private Instant cacheTimestamp = Instant.now();
+
+ private Collection
+ * Note that if we do not register any view for an instrument (see {@link InstrumentManager}),
+ * OpenTelemetry would use a default view automatically to record data.
+ * However, this default view would not filter any attributes and thus would use every attribute,
+ * which was passed during recording. To prevent this, we should register at least one view for every metric by ourselves.
+ */
+@Component
+public class ViewManager {
+
+ @Autowired
+ private EumServerConfiguration configuration;
+
+ @Autowired
+ private TimeWindowViewManager viewManager;
+
+ /**
+ * Registers all configured views in the provided {@link SdkMeterProviderBuilder}.
+ *
+ * @param builder the sdk builder to register views with
+ */
+ public void registerViews(SdkMeterProviderBuilder builder) {
+ val metricViews = getAllActiveViewsForMetrics();
+
+ for (val metricEntry : metricViews.entrySet()) {
+ String metricName = metricEntry.getKey();
+ Map