This is an automated email from the ASF dual-hosted git repository.

ghenzler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git

commit 8ecf2064bf9f51cfea6bbabadbc0b769b1432978
Author: georg.henzler <[email protected]>
AuthorDate: Tue Dec 14 21:28:08 2021 +0100

    FELIX-6480 Make logging of HC monitor more flexible
---
 healthcheck/README.md                              |   4 +-
 .../hc/core/impl/monitor/HealthCheckMonitor.java   |  92 ++++-----
 .../felix/hc/core/impl/monitor/HealthState.java    |  20 +-
 .../impl/servlet/ResultTxtVerboseSerializer.java   |  28 ++-
 .../core/impl/monitor/HealthCheckMonitorTest.java  | 207 ++++++++++++++++++++-
 5 files changed, 292 insertions(+), 59 deletions(-)

diff --git a/healthcheck/README.md b/healthcheck/README.md
index 2007d5f..b40505b 100644
--- a/healthcheck/README.md
+++ b/healthcheck/README.md
@@ -274,8 +274,8 @@ Property    | Type     | Default | Description
 `registerHealthyMarkerService` | boolean | true | For the case a given 
tag/name is healthy, will register a service 
`org.apache.felix.hc.api.condition.Healthy` with property tag=<tagname> (or 
name=<hc.name>) that other services can depend on. For the special case of the 
tag `systemready`, the marker service 
`org.apache.felix.hc.api.condition.SystemReady` is registered
 `registerUnhealthyMarkerService` | boolean | false | For the case a given 
tag/name is **un**healthy, will register a service 
`org.apache.felix.hc.api.condition.Unhealthy` with property tag=<tagname> (or 
name=<hc.name>) that other services can depend on
 `treatWarnAsHealthy` | boolean | true | `WARN` usually means [the system is 
usable](#semantic-meaning-of-health-check-results), hence WARN is treated as 
healthy by default. When set to false `WARN` is treated as `Unhealthy`
-`sendEvents` | enum `NONE`, `STATUS_CHANGES` or `ALL` | `STATUS_CHANGES` | 
Whether to send events for health check status changes. See 
[below](#osgi-events-for-health-check-status-changes) for details.
-`logResults` | enum `NONE`, `STATUS_CHANGES` or `ALL` | `NONE ` | Whether to 
log the result of the monitor to the regular log file
+`sendEvents` | enum `NONE`, `STATUS_CHANGES`, `STATUS_CHANGES_OR_NOT_OK` or 
`ALL` | `STATUS_CHANGES` | Whether to send events for health check status 
changes. See [below](#osgi-events-for-health-check-status-changes) for details.
+`logResults` | enum `NONE`, `STATUS_CHANGES`, `STATUS_CHANGES_OR_NOT_OK` or 
`ALL` | `NONE ` | Whether to log the result of the monitor to the regular log 
file
 `isDynamic` | boolean | false | In dynamic mode all checks for names/tags are 
monitored individually (this means events are sent/services registered for name 
only, never for given tags). This mode allows to use `*` in tags to query for 
all health checks in system. It is also possible to query for all except 
certain tags by using `-`, e.g. by configuring the values `*`, `-tag1` and 
`-tag2` for `tags`.
 
 ### Marker Service to depend on a health status in SCR Components
diff --git 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
index 6da64ed..17bd1d8 100644
--- 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
+++ 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
@@ -26,7 +26,6 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result.Status;
 import org.apache.felix.hc.api.condition.Healthy;
 import org.apache.felix.hc.api.condition.SystemReady;
 import org.apache.felix.hc.api.condition.Unhealthy;
@@ -78,22 +77,22 @@ public class HealthCheckMonitor implements Runnable {
     private static final Logger LOG = 
LoggerFactory.getLogger(HealthCheckMonitor.class);
 
     public enum ChangeType {
-        NONE, STATUS_CHANGES, ALL
+        NONE, STATUS_CHANGES, STATUS_CHANGES_OR_NOT_OK, ALL
     }
     
     @ObjectClassDefinition(name = "Health Check Monitor", description = 
"Regularly executes health checks according to given interval/cron expression")
     public @interface Config {
 
-        @AttributeDefinition(name = "Tags", description = "List of tags to 
query regularly")
+        @AttributeDefinition(name = "Tags", description = "List of tags to 
monitor")
         String[] tags() default {};
 
-        @AttributeDefinition(name = "Names", description = "List of health 
check names to query regularly")
+        @AttributeDefinition(name = "Names", description = "List of health 
check names to monitor")
         String[] names() default {};
 
-        @AttributeDefinition(name = "Interval (Sec)", description = "Will 
execute the checks for give tags every n seconds (either use intervalInSec or 
cronExpression )")
+        @AttributeDefinition(name = "Interval (Sec)", description = "Will 
execute the checks for given tags/names every n seconds (either use 
intervalInSec or cronExpression )")
         long intervalInSec() default 0;
 
-        @AttributeDefinition(name = "Interval (Cron Expresson)", description = 
"Will execute the checks for give tags according to cron expression")
+        @AttributeDefinition(name = "Interval (Cron Expresson)", description = 
"Will execute the checks for given tags/names according to cron expression")
         String cronExpression() default "";
 
         @AttributeDefinition(name = "Register Healthy Marker Service", 
description = "For the case a given tag/name is healthy, will register a 
service Healthy with property tag=<tagname> (or name=<hc.name>) that other 
services can depend on")
@@ -102,16 +101,16 @@ public class HealthCheckMonitor implements Runnable {
         @AttributeDefinition(name = "Register Unhealthy Marker Service", 
description = "For the case a given tag/name is unhealthy, will register a 
service Unhealthy with property tag=<tagname> (or name=<hc.name>) that other 
services can depend on")
         boolean registerUnhealthyMarkerService() default false;
 
-        @AttributeDefinition(name = "Treat WARN as Healthy", description = 
"Whether to treat status WARN as healthy (it normally should because WARN 
indicates a working system that only possibly might become unavailable if no 
action is taken")
+        @AttributeDefinition(name = "Treat WARN as Healthy", description = 
"Whether to treat status WARN as healthy (defaults to true because WARN 
indicates a working system that only possibly might become unavailable if no 
action is taken)")
         boolean treatWarnAsHealthy() default true;
 
-        @AttributeDefinition(name = "Send Events", description = "Send OSGi 
events for the case a status has changed or for all executions or for none.")
+        @AttributeDefinition(name = "Send Events", description = "What updates 
should be sent as OSGi events (none, status changes, status changes and not ok 
results, all updates)")
         ChangeType sendEvents() default ChangeType.STATUS_CHANGES;
 
-        @AttributeDefinition(name = "Log results", description = "Log the 
result to the regular log file.")
+        @AttributeDefinition(name = "Log results", description = "What updates 
should be logged to regular log file (none, status changes, status changes and 
not ok results, all updates)")
         ChangeType logResults() default ChangeType.NONE;
         
-        @AttributeDefinition(name = "Dynamic Mode", description = "In dynamic 
mode all checks for names/tags are monitored individually (this means events 
are sent/services registered for name only, never for given tags). This mode 
allows to use '*' in tags to query for all health checks in system. It is also 
possible to query for all except certain tags by using '-', e.g. by configuring 
the values '*', '-tag1' and '-tag2' for tags.")
+        @AttributeDefinition(name = "Resolve Tags (dynamic)", description = 
"In dynamic mode tags are resolved to a list of health checks that are 
monitored individually (this means events are sent/services are registered for 
name only, never for given tags). This mode allows to use '*' in tags to query 
for all health checks in system. It is also possible to query for all except 
certain tags by using '-', e.g. by configuring the values '*', '-tag1' and 
'-tag2' for tags.")
         boolean isDynamic() default false;
         
         @AttributeDefinition
@@ -166,7 +165,7 @@ public class HealthCheckMonitor implements Runnable {
         this.names = 
Arrays.stream(config.names()).filter(StringUtils::isNotBlank).collect(toList());
         this.isDynamic = config.isDynamic();
         initHealthStates();
-        
+
         this.registerHealthyMarkerService = 
config.registerHealthyMarkerService();
         this.registerUnhealthyMarkerService = 
config.registerUnhealthyMarkerService();
 
@@ -263,12 +262,10 @@ public class HealthCheckMonitor implements Runnable {
                 healthStates.values().parallelStream().forEach(healthState -> 
                     runWithThreadNameContext(healthState::update)
                 );
-                
 
                 if(logResults != ChangeType.NONE) {
                     logResults();
                 }
-                
 
                 LOG.debug("Updated {} health states for tags {} and names {}", 
healthStates.size(), this.tags, this.names);
             } catch (Exception e) {
@@ -278,42 +275,51 @@ public class HealthCheckMonitor implements Runnable {
     }
 
     private void logResults() {
-
-        List<HealthCheckExecutionResult> executionResults = 
healthStates.values().stream()
-            .filter(healthState -> { return healthState.hasChanged() || 
logResults == ChangeType.ALL; })
-            .flatMap( healthState -> {
-                HealthCheckExecutionResult executionResult = 
healthState.getExecutionResult();
-                List<HealthCheckExecutionResult> execResults;
-                if (executionResult instanceof CombinedExecutionResult) {
-                    execResults = ((CombinedExecutionResult) 
executionResult).getExecutionResults();
-                } else {
-                    execResults = Arrays.asList(executionResult);
-                }
-                return execResults.stream();
-            })
-            .sorted()
-            .collect(toList());
-        
-        if(executionResults.isEmpty()) {
-            return;
-        }
         
-        CombinedExecutionResult combinedResultForLogging = new 
CombinedExecutionResult(executionResults);
-        Status hcStatus = 
combinedResultForLogging.getHealthCheckResult().getStatus();
-        if(!LOG.isInfoEnabled() && hcStatus == Status.OK) {
-            return;
+        for(HealthState healthState: healthStates.values()) {
+
+            HealthCheckExecutionResult executionResult = 
healthState.getExecutionResult();
+
+            boolean isOk = executionResult.getHealthCheckResult().isOk();
+            if(!LOG.isInfoEnabled() && isOk) {
+                return; // with INFO disabled even ChangeType.ALL would not 
log it
+            }
+            boolean changeToBeLogged = healthState.hasChanged() && (logResults 
== ChangeType.STATUS_CHANGES || logResults == 
ChangeType.STATUS_CHANGES_OR_NOT_OK);
+            boolean notOkToBeLogged = !isOk && logResults == 
ChangeType.STATUS_CHANGES_OR_NOT_OK;
+            if(!changeToBeLogged && !notOkToBeLogged && logResults != 
ChangeType.ALL) {
+                continue;
+            }
+
+            List<HealthCheckExecutionResult> execResults;
+            boolean isCombinedResult = executionResult instanceof 
CombinedExecutionResult;
+            if (isCombinedResult) {
+                execResults = ((CombinedExecutionResult) 
executionResult).getExecutionResults();
+            } else {
+                execResults = Arrays.asList(executionResult);
+            }
+
+            String label = 
+                    isCombinedResult ?  String.format("Health State for %s 
'%s': healthy:%b isOk:%b hasChanged:%b count HCs:%d", (healthState.isTag() ? 
"tag" : "name"), healthState.getTagOrName(), healthState.isHealthy(), isOk, 
healthState.hasChanged(), execResults.size())
+                            : String.format("Health State for '%s': healthy:%b 
hasChanged:%b", executionResult.getHealthCheckMetadata().getTitle(), 
healthState.isHealthy(), healthState.hasChanged());
+            if(!healthState.hasChanged() && notOkToBeLogged) {
+                // filter the ok items to not clutter the log file
+                execResults = execResults.stream().filter(r -> 
!r.getHealthCheckResult().isOk()).collect(toList());
+            }
+
+            String logMsg = resultTxtVerboseSerializer.serialize(label, 
execResults, false);
+            logResultItem(isOk, logMsg);
         }
 
-        String logMsg = 
resultTxtVerboseSerializer.serialize(combinedResultForLogging.getHealthCheckResult(),
 combinedResultForLogging.getExecutionResults(), false);
-        String firstLineMsg = (logResults == ChangeType.STATUS_CHANGES) ? 
"Status Changes:" : "";
-        if(hcStatus == Status.OK) {
-            LOG.info(firstLineMsg+"\n"+logMsg);
+    }
+
+    void logResultItem(boolean isOk, String msg) {
+        if(isOk) {
+            LOG.info(msg);
         } else {
-            LOG.warn(firstLineMsg+"\n"+logMsg);
+            LOG.warn(msg);
         }
-
     }
-    
+
     private void runWithThreadNameContext(Runnable r) {
         String threadNameToRestore = Thread.currentThread().getName();
         try {
diff --git 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthState.java
 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthState.java
index ed5d33f..3f2c345 100644
--- 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthState.java
+++ 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthState.java
@@ -25,11 +25,9 @@ import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Function;
 
 import org.apache.felix.hc.api.HealthCheck;
 import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.Result.Status;
 import org.apache.felix.hc.api.condition.Healthy;
 import org.apache.felix.hc.api.condition.SystemReady;
 import org.apache.felix.hc.api.condition.Unhealthy;
@@ -109,6 +107,18 @@ class HealthState {
         return statusChanged;
     }
 
+    public boolean isHealthy() {
+        return isHealthy;
+    }
+
+    String getTagOrName() {
+        return tagOrName;
+    }
+
+    boolean isTag() {
+        return isTag;
+    }
+
     HealthCheckExecutionResult getExecutionResult() {
         return executionResult;
     }
@@ -214,8 +224,10 @@ class HealthState {
 
     private void sendEvents(HealthCheckExecutionResult executionResult, 
Result.Status previousStatus) {
         ChangeType sendEventsConfig = monitor.getSendEvents();
-        if ((sendEventsConfig == ChangeType.STATUS_CHANGES && statusChanged) 
|| sendEventsConfig == ChangeType.ALL) {
-            
+        if (sendEventsConfig == ChangeType.ALL 
+                || (statusChanged && (sendEventsConfig == 
ChangeType.STATUS_CHANGES || sendEventsConfig == 
ChangeType.STATUS_CHANGES_OR_NOT_OK))
+                || (!executionResult.getHealthCheckResult().isOk() && 
sendEventsConfig == ChangeType.STATUS_CHANGES_OR_NOT_OK)) {
+
             String eventSuffix = statusChanged ? 
EVENT_TOPIC_SUFFIX_STATUS_CHANGED : EVENT_TOPIC_SUFFIX_STATUS_UPDATED;
             String logMsg = "Posted event for topic '{}': " + (statusChanged ? 
"Status change from {} to {}" : "Result updated (status {})");
 
diff --git 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializer.java
 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializer.java
index 1148344..f5bbc8f 100644
--- 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializer.java
+++ 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializer.java
@@ -62,27 +62,47 @@ public class ResultTxtVerboseSerializer {
         colWidthLog = totalWidth - colWidthWithoutLog;
     }
 
-    public String serialize(final Result overallResult, final 
List<HealthCheckExecutionResult> executionResults, boolean includeDebug) {
+    // without overall result, only the execution results are listed without 
header
+    public String serialize(String label, final 
List<HealthCheckExecutionResult> executionResults, boolean includeDebug) {
+        StringBuilder resultStr = new StringBuilder();
+
+        resultStr.append(label + "\n");
+        resultStr.append(serializeResults(executionResults, includeDebug));
 
-        LOG.debug("Sending verbose txt response... ");
+        return resultStr.toString();
+    }
+
+    public String serialize(final Result overallResult, final 
List<HealthCheckExecutionResult> executionResults, boolean includeDebug) {
 
         StringBuilder resultStr = new StringBuilder();
 
         resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
-        resultStr.append(center("Overall Health Result: " + 
overallResult.getStatus().toString(), totalWidth) + NEWLINE);
+        resultStr.append(
+                center("Overall Health Result: " + 
overallResult.getStatus().toString(), totalWidth) + NEWLINE);
         resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
+
         resultStr.append(rightPad("Name", colWidthName));
         resultStr.append(rightPad("Result", colWidthResult));
         resultStr.append(rightPad("Timing", colWidthTiming));
         resultStr.append("Logs" + NEWLINE);
         resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
 
+        resultStr.append(serializeResults(executionResults, includeDebug));
+        resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
+
+        return resultStr.toString();
+
+    }
+    
+    private String serializeResults(final List<HealthCheckExecutionResult> 
executionResults, boolean includeDebug) {
+
+        StringBuilder resultStr = new StringBuilder();
+
         final DateFormat dfShort = new SimpleDateFormat("HH:mm:ss.SSS");
 
         for (HealthCheckExecutionResult healthCheckResult : executionResults) {
             appendVerboseTxtForResult(resultStr, healthCheckResult, 
includeDebug, dfShort);
         }
-        resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
 
         return resultStr.toString();
 
diff --git 
a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
 
b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
index 2d9502d..47f69b5 100644
--- 
a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
+++ 
b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
@@ -20,7 +20,12 @@ package org.apache.felix.hc.core.impl.monitor;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.matches;
+import static org.mockito.Mockito.lenient;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
@@ -44,13 +49,18 @@ import 
org.apache.felix.hc.core.impl.executor.ExecutionResult;
 import org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor;
 import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
 import org.apache.felix.hc.core.impl.scheduling.AsyncIntervalJob;
+import org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializer;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
@@ -60,12 +70,15 @@ import org.osgi.service.component.ComponentContext;
 import org.osgi.service.event.Event;
 import org.osgi.service.event.EventAdmin;
 
+@RunWith(MockitoJUnitRunner.class)
 public class HealthCheckMonitorTest {
 
     private static final String TEST_TAG = "test-tag";
+    private static final String HC_RESULT_SERIALIZED = "HC result serialized";
 
+    @Spy
     @InjectMocks
-    private HealthCheckMonitor healthCheckMonitor = new HealthCheckMonitor();
+    private HealthCheckMonitor healthCheckMonitor;
 
     @Mock
     private BundleContext bundleContext;
@@ -103,9 +116,11 @@ public class HealthCheckMonitorTest {
     @Mock
     private ServiceRegistration<Unhealthy> unhealthyRegistration;
     
+    @Mock
+    private ResultTxtVerboseSerializer resultTxtVerboseSerializer;
+    
     @Before
     public void before() throws ReflectiveOperationException {
-        MockitoAnnotations.initMocks(this);
 
         for (Method m : HealthCheckMonitor.Config.class.getDeclaredMethods()) {
             when(m.invoke(config)).thenReturn(m.getDefaultValue());
@@ -115,10 +130,12 @@ public class HealthCheckMonitorTest {
         when(config.tags()).thenReturn(new String[] { TEST_TAG });
         
         
when(healthCheckMetadata.getServiceReference()).thenReturn(healthCheckServiceRef);
-        
+        when(healthCheckMetadata.getTitle()).thenReturn("Test Check");
+
         Dictionary<String,Object> componentProps = new Hashtable<>();
         componentProps.put(ComponentConstants.COMPONENT_ID, 7L);
         when(componentContext.getProperties()).thenReturn(componentProps);
+
     }
 
     @Test
@@ -188,9 +205,9 @@ public class HealthCheckMonitorTest {
     private void resetMarkerServicesContext() {
         reset(bundleContext, healthyRegistration, unhealthyRegistration);
         when(bundleContext.registerService(eq(Healthy.class), 
eq(HealthState.MARKER_SERVICE_HEALTHY), 
any())).thenReturn((ServiceRegistration<Healthy>) healthyRegistration);
-        when(bundleContext.registerService(eq(Unhealthy.class), 
eq(HealthState.MARKER_SERVICE_UNHEALTHY), 
any())).thenReturn(unhealthyRegistration);
+        lenient().when(bundleContext.registerService(eq(Unhealthy.class), 
eq(HealthState.MARKER_SERVICE_UNHEALTHY), 
any())).thenReturn(unhealthyRegistration);
     }
-    
+
     @Test
     public void testRunSendEventsStatusChanges() throws InvalidSyntaxException 
{
 
@@ -271,7 +288,185 @@ public class HealthCheckMonitorTest {
         assertEquals(Result.Status.OK, 
postedEvents.get(0).getProperty(HealthState.EVENT_PROP_PREVIOUS_STATUS));
         
assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/UPDATED",
 postedEvents.get(1).getTopic());
     }
+
+
+
+    @Test
+    public void testRunLogAll() throws InvalidSyntaxException {
+
+        prepareLoggingTest(HealthCheckMonitor.ChangeType.ALL);
+
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(true), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(true), 
matches(".*healthy:true hasChanged:false .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.WARN);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.WARN);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:true hasChanged:false .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.CRITICAL);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:false hasChanged:true .*" + HC_RESULT_SERIALIZED));
+        
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.CRITICAL);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:false hasChanged:false .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(true), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(true), 
matches(".*healthy:true hasChanged:false .*" + HC_RESULT_SERIALIZED));
+
+    }
+
+
+
+    @Test
+    public void testRunLogStatusChanges() throws InvalidSyntaxException {
+
+        prepareLoggingTest(HealthCheckMonitor.ChangeType.STATUS_CHANGES);
+
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(true), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), 
anyString());
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.WARN);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.WARN);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), 
anyString());
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.CRITICAL);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:false hasChanged:true .*" + HC_RESULT_SERIALIZED));
+        
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.CRITICAL);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), 
anyString());
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(true), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), 
anyString());
+
+    }
+
+
+    @Test
+    public void testRunLogStatusChangesOrNotOk() throws InvalidSyntaxException 
{
+
+        
prepareLoggingTest(HealthCheckMonitor.ChangeType.STATUS_CHANGES_OR_NOT_OK);
+
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(true), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), 
anyString());
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.WARN);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.WARN);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:true hasChanged:false .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.CRITICAL);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:false hasChanged:true .*" + HC_RESULT_SERIALIZED));
+        
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.CRITICAL);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(false), 
matches(".*healthy:false hasChanged:false .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor).logResultItem(eq(true), 
matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
+
+        reset(healthCheckMonitor);
+        setHcResult(Result.Status.OK);
+        healthCheckMonitor.run();
+        verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), 
anyString());
+
+    }
+
+
+    @Test
+    public void testRunLogStatusNone() throws InvalidSyntaxException {
+
+        prepareLoggingTest(HealthCheckMonitor.ChangeType.NONE);
+
+        for (Result.Status status : Arrays.asList(
+                Result.Status.OK, Result.Status.OK,
+                Result.Status.WARN, Result.Status.WARN,
+                Result.Status.CRITICAL, Result.Status.CRITICAL,
+                Result.Status.OK, Result.Status.OK)) {
+
+            setHcResult(status);
+            healthCheckMonitor.run();
+        }
+
+        // ensure logging is never called for whatever state remains the same 
or changes
+        verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), 
anyString());
+
+    }
     
+    private void prepareLoggingTest(HealthCheckMonitor.ChangeType 
loggingChangeType) throws InvalidSyntaxException {
+        
when(config.sendEvents()).thenReturn(HealthCheckMonitor.ChangeType.NONE);
+        when(config.logResults()).thenReturn(loggingChangeType);
+        healthCheckMonitor.activate(bundleContext, config, componentContext);
+        healthCheckMonitor.healthStates.put(TEST_TAG, new 
HealthState(healthCheckMonitor, TEST_TAG, true));
+
+        when(resultTxtVerboseSerializer.serialize(any(String.class), 
anyList(), eq(false))).thenAnswer(new Answer<String>() {
+            @Override
+            public String answer(InvocationOnMock invocation) throws Throwable 
{
+              Object[] args = invocation.getArguments();
+              return (String) args[0] + " " + HC_RESULT_SERIALIZED;
+            }
+          });
+    }
     
     private void setHcResult(Result.Status status) {
         when(healthCheckExecutor.execute(HealthCheckSelector.tags(TEST_TAG)))

Reply via email to