diff --git a/components/inspectit-ocelot-configurationserver-ui/src/components/layout/Menubar.js b/components/inspectit-ocelot-configurationserver-ui/src/components/layout/Menubar.js index c3f88045f6..1be253ed25 100644 --- a/components/inspectit-ocelot-configurationserver-ui/src/components/layout/Menubar.js +++ b/components/inspectit-ocelot-configurationserver-ui/src/components/layout/Menubar.js @@ -72,9 +72,6 @@ class Menubar extends React.Component { } right={
- diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/commons/models/health/AgentHealthIncident.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/commons/models/health/AgentHealthIncident.java index 610476f4ad..2a55d81948 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/commons/models/health/AgentHealthIncident.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/commons/models/health/AgentHealthIncident.java @@ -9,7 +9,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class AgentHealthIncident implements Comparable{ +public class AgentHealthIncident implements Comparable { private String time; private AgentHealth health; @@ -17,10 +17,6 @@ public class AgentHealthIncident implements Comparable{ private String message; private boolean changedHealth; - public static AgentHealthIncident getNoneIncident() { - return new AgentHealthIncident(LocalDateTime.now().toString(), AgentHealth.OK, "", "", false); - } - @Override public int compareTo(Object o) { if(!(o instanceof AgentHealthIncident)) { diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/MetricsSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/MetricsSettings.java index dc5bc11856..c9aa327d6d 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/MetricsSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/MetricsSettings.java @@ -38,6 +38,9 @@ public class MetricsSettings { */ private Duration frequency; + /** + * Settings for controlling the amount of unique tag values + */ private TagGuardSettings tagGuard; @NotNull diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/TagGuardSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/TagGuardSettings.java index 783a6af541..d382368815 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/TagGuardSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/TagGuardSettings.java @@ -15,21 +15,23 @@ @AdditionalValidations public class TagGuardSettings { + private boolean enabled; + /** - * The schedule delay of the {@code TagValueGuard} + * The schedule delay for the blocking task of the {@code MeasureTagValueGuard} */ private Duration scheduleDelay; /** - * + * File, which contains metrics with their particular recorded tags and their tag values */ private String databaseFile; - + /** + * String, which should be used as tag value, if the defined limit of tag values is exceeded + */ private String overflowReplacement; - private boolean enabled; - /** * Default max values per tag for all measures that are not specified in {@link #maxValuesPerTagByMeasure} or {@link rocks.inspectit.ocelot.config.model.metrics.definition.MetricDefinitionSettings#maxValuesPerTag}. */ diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/AgentHealthSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/AgentHealthSettings.java index 3751782af1..4449bff9ca 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/AgentHealthSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/AgentHealthSettings.java @@ -22,18 +22,29 @@ public class AgentHealthSettings { @NonNull private Duration validityPeriod; + /** + * The amount of AgentHealthIncidents, which should be buffered. + */ + @NonNull + private Integer incidentBufferSize; + /** * The minimum delay how often the AgentHealthManager checks for invalid agent health events to clear health status. */ @NonNull private Duration minHealthCheckDelay; - @AssertTrue(message = "minHealthCheckDelay must be at least 60 seconds") + @AssertTrue(message = "validityPeriod must be greater than minHealthCheckDelay!") + public boolean validityPeriodIsGreaterThanMinDelay() { + return validityPeriod.compareTo(minHealthCheckDelay) > 0; + } + + @AssertTrue(message = "minHealthCheckDelay must be at least 60 seconds!") public boolean isMin60SecondsDelay() { return minHealthCheckDelay.toMinutes() >= 1; } - @AssertFalse(message = "The specified period should not be negative!") + @AssertFalse(message = "The specified period must not be negative!") public boolean isNegativeDuration() { return validityPeriod != null && validityPeriod.isNegative(); } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml index 05a914cfb9..a4d448bc8e 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml @@ -15,6 +15,9 @@ inspectit: # health changes due to instrumentation errors are valid until the next re-instrumentation validity-period: 1h + # The amount of agent health incidents, which should be buffered + incident-buffer-size: 10 + # The minimum delay how often the AgentHealthManager checks for invalid agent health events to clear health status # By default the delay is calculated based on the last agent health event # Minimum value is 1m diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/propertysources/http/HttpConfigurationPoller.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/propertysources/http/HttpConfigurationPoller.java index 59491fc19b..ceffb66652 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/propertysources/http/HttpConfigurationPoller.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/propertysources/http/HttpConfigurationPoller.java @@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit; /** - * Service for continuously triggering the updated of a agent configuration via HTTP. + * Service for continuously triggering the updating of an agent configuration via HTTP. */ @Service @Slf4j diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/metrics/MeasureTagValueGuard.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/metrics/MeasureTagValueGuard.java index b6d3e1721a..3c717b2d0c 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/metrics/MeasureTagValueGuard.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/metrics/MeasureTagValueGuard.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import io.opencensus.tags.TagContext; import io.opencensus.tags.TagContextBuilder; import io.opencensus.tags.TagKey; @@ -46,6 +47,8 @@ public class MeasureTagValueGuard { private static final String tagOverFlowMessageTemplate = "Overflow for tag %s"; + private static final String tagOverFlowResolvedMessageTemplate = "Overflow resolved for tag %s"; + @Autowired private InspectitEnvironment env; @@ -65,7 +68,10 @@ public class MeasureTagValueGuard { private volatile boolean isShuttingDown = false; - private final Map> blockedTagKeysByMeasure = Maps.newHashMap(); + /** + * Map of measure names and their related set of tag keys, which should be blocked. + */ + private final Map> blockedTagKeysByMeasure = Maps.newHashMap(); private Set latestTags = Collections.synchronizedSet(new HashSet<>()); @@ -100,6 +106,11 @@ protected void stop() { blockTagValuesFuture.cancel(true); } + /** + * Task, which reads the persisted tag values to determine, which tags should be blocked, because of exceeding + * the specific tag value limit. + * If new tags values have been created, they will be persisted. + */ Runnable blockTagValuesTask = () -> { if (!env.getCurrentConfig().getMetrics().getTagGuard().isEnabled()) { @@ -111,20 +122,24 @@ protected void stop() { Map>> availableTagsByMeasure = fileReaderWriter.read(); - copy.forEach(t -> { - String measureName = t.getMeasureName(); - Map newTags = t.getTags(); + copy.forEach(tagsHolder -> { + String measureName = tagsHolder.getMeasureName(); + Map newTags = tagsHolder.getTags(); int maxValuesPerTag = getMaxValuesPerTag(measureName, env.getCurrentConfig()); Map> tagValuesByTagKey = availableTagsByMeasure.computeIfAbsent(measureName, k -> Maps.newHashMap()); newTags.forEach((tagKey, tagValue) -> { Set tagValues = tagValuesByTagKey.computeIfAbsent(tagKey, (x) -> new HashSet<>()); - if (tagValues.size() > maxValuesPerTag) { - blockedTagKeysByMeasure.computeIfAbsent(tagKey, blocked -> Maps.newHashMap()) - .putIfAbsent(tagKey, true); + // if tag value is new AND max values per tag is already reached + if (!tagValues.contains(tagValue) && tagValues.size() >= maxValuesPerTag) { + blockedTagKeysByMeasure.computeIfAbsent(measureName, blocked -> Sets.newHashSet()) + .add(tagKey); agentHealthManager.handleInvalidatableHealth(AgentHealth.ERROR, this.getClass(), String.format(tagOverFlowMessageTemplate, tagKey)); } else { + Set blockedTagKeys = blockedTagKeysByMeasure.computeIfAbsent(measureName, blocked -> Sets.newHashSet()); + if(blockedTagKeys.remove(tagKey)) + agentHealthManager.invalidateIncident(this.getClass(), String.format(tagOverFlowResolvedMessageTemplate, tagKey)); tagValues.add(tagValue); } }); @@ -162,11 +177,17 @@ int getMaxValuesPerTag(String measureName, InspectitConfig config) { .getMaxValuesPerTag()); } + /** + * + * @param context + * @param metricAccessor + * @return + */ public TagContext getTagContext(IHookAction.ExecutionContext context, MetricAccessor metricAccessor) { Map tags = Maps.newHashMap(); String measureName = metricAccessor.getName(); InspectitContextImpl inspectitContext = context.getInspectitContext(); - Map blockedTagKeys = blockedTagKeysByMeasure.getOrDefault(measureName, Maps.newHashMap()); + Set blockedTagKeys = blockedTagKeysByMeasure.getOrDefault(measureName, Sets.newHashSet()); TagGuardSettings tagGuardSettings = env.getCurrentConfig().getMetrics().getTagGuard(); // first common tags to allow to overwrite by constant or data tags @@ -177,7 +198,7 @@ public TagContext getTagContext(IHookAction.ExecutionContext context, MetricAcce // then constant tags to allow to overwrite by data metricAccessor.getConstantTags().forEach((key, value) -> { - if (tagGuardSettings.isEnabled() && blockedTagKeys.containsKey(key)) { + if (tagGuardSettings.isEnabled() && blockedTagKeys.contains(key)) { String overflowReplacement = env.getCurrentConfig().getMetrics().getTagGuard().getOverflowReplacement(); tags.put(key, TagUtils.createTagValueAsString(key, overflowReplacement)); } else { @@ -187,7 +208,7 @@ public TagContext getTagContext(IHookAction.ExecutionContext context, MetricAcce // go over data tags and match the value to the key from the contextTags (if available) metricAccessor.getDataTagAccessors().forEach((key, accessor) -> { - if (tagGuardSettings.isEnabled() && blockedTagKeys.containsKey(key)) { + if (tagGuardSettings.isEnabled() && blockedTagKeys.contains(key)) { String overflowReplacement = env.getCurrentConfig().getMetrics().getTagGuard().getOverflowReplacement(); tags.put(key, TagUtils.createTagValueAsString(key, overflowReplacement)); } else { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/AgentHealthManager.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/AgentHealthManager.java index ae5461fe68..4b527f7d0c 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/AgentHealthManager.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/AgentHealthManager.java @@ -1,5 +1,6 @@ package rocks.inspectit.ocelot.core.selfmonitoring; +import com.google.common.annotations.VisibleForTesting; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; @@ -8,14 +9,17 @@ import rocks.inspectit.ocelot.commons.models.health.AgentHealthIncident; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.selfmonitoring.event.models.AgentHealthChangedEvent; -import rocks.inspectit.ocelot.core.utils.RingBuffer; +import rocks.inspectit.ocelot.core.utils.AgentHealthIncidentBuffer; +import javax.annotation.PostConstruct; import java.time.Duration; import java.time.LocalDateTime; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * Manages the {@link AgentHealth} and publishes {@link AgentHealthChangedEvent}s when it changes. @@ -27,9 +31,11 @@ public class AgentHealthManager { private final ApplicationContext ctx; + private final ScheduledExecutorService executor; + /** * Map of {@code eventClass -> agentHealth}, whereas the {@code agentHealth} is reset whenever an event of type - * {@code eventClass} occurs (see {@link #onInvalidationEvent(Object)}. + * {@code eventClass} occurs (see {@link #onInvalidationEvent(Object)}). * The resulting agent health is the most severe value in the map. */ private final Map, AgentHealth> invalidatableHealth = new ConcurrentHashMap<>(); @@ -44,24 +50,28 @@ public class AgentHealthManager { private final InspectitEnvironment env; - private final RingBuffer healthIncidentBuffer = new RingBuffer<>(10); //TODO make this configurable + private final AgentHealthIncidentBuffer healthIncidentBuffer; - public List getHistory() { - return this.healthIncidentBuffer.asList(); + @PostConstruct + @VisibleForTesting + void startHealthCheckScheduler() { + checkHealthAndSchedule(); } - public void notifyAgentHealth(AgentHealth eventHealth, Class invalidator, String loggerName, String message) { //TODO test to check if the healthstate changes. + public List getHistory() { + return healthIncidentBuffer.asList(); + } - if (invalidator == null) { + public void notifyAgentHealth(AgentHealth eventHealth, Class invalidator, String loggerName, String message) { + if (invalidator == null) handleTimeoutHealth(eventHealth, loggerName, message); - } else { + else handleInvalidatableHealth(eventHealth, invalidator, message); - } } public void handleInvalidatableHealth(AgentHealth eventHealth, Class invalidator, String eventMessage) { invalidatableHealth.merge(invalidator, eventHealth, AgentHealth::mostSevere); - triggerEvent(invalidator.getTypeName(), eventMessage); + triggerAgentHealthChangedEvent(invalidator.getTypeName(), eventMessage); } private void handleTimeoutHealth(AgentHealth eventHealth, String loggerName, String eventMassage) { @@ -70,28 +80,60 @@ private void handleTimeoutHealth(AgentHealth eventHealth, String loggerName, Str if (eventHealth.isMoreSevereOrEqualTo(AgentHealth.WARNING)) { generalHealthTimeouts.put(eventHealth, LocalDateTime.now().plus(validityPeriod)); } - triggerEvent(loggerName, eventMassage + " This status is valid for " + validityPeriod); + triggerAgentHealthChangedEvent(loggerName, eventMassage + ". This status is valid for " + validityPeriod); } - public void invalidateIncident(Class eventClass, String eventMessage) { + public void invalidateIncident(Class eventClass, String eventMessage) { invalidatableHealth.remove(eventClass); - triggerEvent(eventClass.getTypeName(), eventMessage); + triggerAgentHealthChangedEvent(eventClass.getTypeName(), eventMessage); + } + + /** + * Checks whether the current health has changed since last check and schedules another check. + * The next check will run dependent on the earliest status timeout in the future: + *
    + *
  • does not exist -> run again after validity period
  • + *
  • exists -> run until that timeout is over
  • + *
+ */ + private void checkHealthAndSchedule() { + triggerAgentHealthChangedEvent(AgentHealthManager.class.getCanonicalName(), "Checking timed out agent healths"); + + Duration validityPeriod = env.getCurrentConfig().getSelfMonitoring().getAgentHealth().getValidityPeriod(); + Duration minDelay = env.getCurrentConfig().getSelfMonitoring().getAgentHealth().getMinHealthCheckDelay(); + Duration delay = generalHealthTimeouts.values() + .stream() + .filter(dateTime -> dateTime.isAfter(LocalDateTime.now())) + .max(Comparator.naturalOrder()) + .map(dateTime -> { + Duration dif = Duration.between(dateTime, LocalDateTime.now()); + if (minDelay.compareTo(dif) > 0) return minDelay; + else return dif; + }) + .orElse(validityPeriod); + + executor.schedule(this::checkHealthAndSchedule, delay.toMillis(), TimeUnit.MILLISECONDS); } - private void triggerEvent( String incidentSource, String message) { + /** + * Creates a new AgentHealthIncident and also triggers an AgentHealthChangedEvent, if the agent health has changed + * @param incidentSource class, which caused the incident + * @param message message, describing the incident + */ + private void triggerAgentHealthChangedEvent(String incidentSource, String message) { synchronized (this) { - boolean changedHealth = false; - AgentHealth currHealth = getCurrentHealth(); - AgentHealth lastHealth = lastNotifiedHealth; - if(healthChanged()) { - changedHealth = true; - lastNotifiedHealth = currHealth; - AgentHealthChangedEvent event = new AgentHealthChangedEvent(this, lastHealth, currHealth, message); - ctx.publishEvent(event); - } + boolean changedHealth = healthHasChanged(); + AgentHealth currentHealth = getCurrentHealth(); - AgentHealthIncident incident = new AgentHealthIncident(LocalDateTime.now().toString(), currHealth, incidentSource, message, changedHealth); + AgentHealthIncident incident = new AgentHealthIncident(LocalDateTime.now().toString(), currentHealth, incidentSource, message, changedHealth); healthIncidentBuffer.put(incident); + + if(changedHealth) { + AgentHealth lastHealth = lastNotifiedHealth; + lastNotifiedHealth = currentHealth; + AgentHealthChangedEvent event = new AgentHealthChangedEvent(this, lastHealth, currentHealth, message); + ctx.publishEvent(event); + } } } @@ -100,7 +142,7 @@ private void triggerEvent( String incidentSource, String message) { * * @return true if the health state changed */ - private boolean healthChanged() { + private boolean healthHasChanged() { return getCurrentHealth() != lastNotifiedHealth; } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/listener/HealthEventListener.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/listener/HealthEventListener.java index af4f01ca5e..20832ab049 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/listener/HealthEventListener.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/listener/HealthEventListener.java @@ -2,7 +2,6 @@ import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; -import rocks.inspectit.ocelot.core.instrumentation.config.event.InstrumentationConfigurationChangedEvent; import rocks.inspectit.ocelot.core.selfmonitoring.event.models.AgentHealthChangedEvent; public interface HealthEventListener { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/listener/PollerWritingHealthEventListener.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/listener/PollerWritingHealthEventListener.java index c3aa5558e0..69d8ba659b 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/listener/PollerWritingHealthEventListener.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/listener/PollerWritingHealthEventListener.java @@ -22,11 +22,7 @@ public class PollerWritingHealthEventListener implements HealthEventListener { @Override public void onAgentHealthEvent(AgentHealthChangedEvent event) { List incidentHistory = agentHealthManager.getHistory(); - AgentHealthIncident latestIncident = AgentHealthIncident.getNoneIncident(); - if (!incidentHistory.isEmpty()) { - latestIncident = incidentHistory.get(incidentHistory.size() - 1); - } - AgentHealthState state = new AgentHealthState(latestIncident.getHealth(), latestIncident.getSource(), latestIncident.getMessage(), incidentHistory); + AgentHealthState state = new AgentHealthState(event.getNewHealth(), event.getSource().toString(), event.getMessage(), incidentHistory); httpConfigurationPoller.updateAgentHealthState(state); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/models/AgentHealthChangedEvent.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/models/AgentHealthChangedEvent.java index e667e80634..401ed4bef3 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/models/AgentHealthChangedEvent.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/event/models/AgentHealthChangedEvent.java @@ -29,11 +29,6 @@ public class AgentHealthChangedEvent extends ApplicationEvent { @Getter private String message; - /** - * Indicates if this event caused a health change. - */ - private boolean changedState; - public AgentHealthChangedEvent(Object source, @NonNull AgentHealth oldHealth, @NonNull AgentHealth newHealth, String message) { super(source); this.oldHealth = oldHealth; diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/logs/LogHealthMonitor.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/logs/LogHealthMonitor.java index 03809cb45c..27af2daace 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/logs/LogHealthMonitor.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/logs/LogHealthMonitor.java @@ -28,7 +28,7 @@ public void onLoggingEvent(ILoggingEvent event, Class invalidator) { return; } AgentHealth eventHealth = AgentHealth.fromLogLevel(event.getLevel()); - agentHealthManager.notifyAgentHealth(eventHealth, invalidator, event.getLoggerName(), event.getMessage()); + agentHealthManager.notifyAgentHealth(eventHealth, invalidator, event.getLoggerName(), event.getFormattedMessage()); } @Override diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/AgentHealthIncidentBuffer.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/AgentHealthIncidentBuffer.java new file mode 100644 index 0000000000..ad095d297a --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/AgentHealthIncidentBuffer.java @@ -0,0 +1,49 @@ +package rocks.inspectit.ocelot.core.utils; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import rocks.inspectit.ocelot.commons.models.health.AgentHealthIncident; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; + +import java.util.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * Buffer with queued AgentHealthIncidents. + * New incidents will be inserted at the beginning of the queue. + * As soon as incidents are put into a full queue, old incidents will be removed to create space + */ +@Component +@RequiredArgsConstructor +public class AgentHealthIncidentBuffer { + private final ConcurrentLinkedQueue buffer = new ConcurrentLinkedQueue<>(); + + private final InspectitEnvironment env; + + /** + * Add new incident to the buffer. + * If the buffer is full, remove the latest incident at first. + * The buffer size will be read from the current inspectIT configuration. + * @param incident new incident + */ + public void put(AgentHealthIncident incident) { + int bufferSize = env.getCurrentConfig().getSelfMonitoring().getAgentHealth().getIncidentBufferSize(); + while(buffer.size() >= bufferSize) buffer.poll(); + buffer.offer(incident); + } + + /** + * Creates a list from the internal queue. + * The list will be reversed, since the queue inserts new elements at the tail + * @return List of agent health incidents + */ + public List asList() { + List incidentList = new LinkedList<>(buffer); + Collections.reverse(incidentList); + return incidentList; + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/RingBuffer.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/RingBuffer.java deleted file mode 100644 index 490c6250e0..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/RingBuffer.java +++ /dev/null @@ -1,45 +0,0 @@ -package rocks.inspectit.ocelot.core.utils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -public class RingBuffer { - - private final AtomicInteger currentIndex = new AtomicInteger(0); - - private final AtomicInteger bufferSize; - private final List buffer; - - public RingBuffer (int bufferSize) { - this.bufferSize = new AtomicInteger(bufferSize); - buffer = new ArrayList<>(bufferSize); - } - - public void put(T element) { - int index = currentIndex.getAndIncrement(); - if (index >= bufferSize.get()) { - currentIndex.set(0); - index = 0; - } - buffer.add(index, element); - } - - public T get(int index) { - return buffer.get(index % buffer.size()); - } - - public List asList() { - if (buffer.isEmpty()) { - return Collections.emptyList(); - } - int oldestIndex = currentIndex.get() - 1; - List elementList = new ArrayList<>(); - for (int i = oldestIndex; i < oldestIndex + this.buffer.size(); i++) { - elementList.add(get(i)); - } - return elementList; - } - -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/logs/LogHealthMonitorTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/logs/LogHealthMonitorTest.java index ff07e1f592..0a0fe5a60b 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/logs/LogHealthMonitorTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/logs/LogHealthMonitorTest.java @@ -55,7 +55,7 @@ void triggerAgentHealthManagerNotifyAgentHealthEvent() { healthMonitor.onLoggingEvent(loggingEvent, invalidatorMock); - verify(healthManager).notifyAgentHealth(eventHealth, invalidatorMock, loggerClass.getCanonicalName(), loggingEvent.getMessage()); + verify(healthManager).notifyAgentHealth(eventHealth, invalidatorMock, loggerClass.getCanonicalName(), loggingEvent.getFormattedMessage()); } } }