Plugin reports wrong number of failures

The increment of the metric failure should happen
*when* a new failure is happening and NOT when the metric
is read from a callback.

The metrics are now published as soon as a single check is performed,
i.e.: a doCheck() for a *Check class runs.
The metric collector system will just scrape them when needed without
retriggering any calculation of the metrics.

The metrics are now injected in the AbstractHealthCheck, which
takes care of updating them whenever a check runs.

As part of this change the latency is now represented as a Timer, rather
than a Gauge as it was wrongly done before.

Bug: Issue 14385
Change-Id: I5f149f6893289c2874d125a9c04862d53cacd19b
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetrics.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetrics.java
index cea2530..1f88020 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetrics.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetrics.java
@@ -14,84 +14,29 @@
 
 package com.googlesource.gerrit.plugins.healthcheck;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Sets;
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
-import com.google.gerrit.metrics.CallbackMetric0;
 import com.google.gerrit.metrics.Counter0;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer0;
 import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.googlesource.gerrit.plugins.healthcheck.check.GlobalHealthCheck;
-import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
+import com.google.inject.assistedinject.Assisted;
 
-@Singleton
-public class HealthCheckMetrics implements LifecycleListener {
+public class HealthCheckMetrics {
 
-  private final DynamicSet<HealthCheck> healthChecks;
+  public interface Factory {
+    HealthCheckMetrics create(String name);
+  }
+
   private final MetricMaker metricMaker;
-  private final Set<RegistrationHandle> registeredMetrics;
-  private final Set<Runnable> triggers;
-  private final GlobalHealthCheck globalHealthCheck;
+  private final String name;
 
   @Inject
-  public HealthCheckMetrics(
-      DynamicSet<HealthCheck> healthChecks,
-      GlobalHealthCheck globalHealthCheck,
-      MetricMaker metricMaker) {
-    this.healthChecks = healthChecks;
+  public HealthCheckMetrics(MetricMaker metricMaker, @Assisted String name) {
     this.metricMaker = metricMaker;
-    this.registeredMetrics = Collections.synchronizedSet(new HashSet<>());
-    this.triggers = Collections.synchronizedSet(new HashSet<>());
-    this.globalHealthCheck = globalHealthCheck;
+    this.name = name;
   }
 
-  @Override
-  public void start() {
-
-    Set<HealthCheck> allChecks = Sets.newHashSet(healthChecks);
-    allChecks.add(globalHealthCheck);
-
-    for (HealthCheck healthCheck : allChecks) {
-      String name = healthCheck.name();
-
-      Counter0 failureMetric = getFailureMetric(name);
-      CallbackMetric0<Long> latencyMetric = getLatencyMetric(name);
-      Runnable metricCallBack = getCallbackMetric(healthCheck, failureMetric, latencyMetric);
-
-      registeredMetrics.add(failureMetric);
-      registeredMetrics.add(metricMaker.newTrigger(latencyMetric, metricCallBack));
-      triggers.add(metricCallBack);
-    }
-  }
-
-  private Runnable getCallbackMetric(
-      HealthCheck healthCheck, Counter0 failureMetric, CallbackMetric0<Long> latencyMetric) {
-    return () -> {
-      HealthCheck.StatusSummary status = healthCheck.getLatestStatus();
-      latencyMetric.set(healthCheck.getLatestStatus().elapsed);
-      if (status.isFailure()) {
-        failureMetric.increment();
-      }
-    };
-  }
-
-  private CallbackMetric0<Long> getLatencyMetric(String name) {
-    return metricMaker.newCallbackMetric(
-        String.format("%s/latest_latency", name),
-        Long.class,
-        new Description(String.format("%s health check latency execution (ms)", name))
-            .setGauge()
-            .setUnit(Description.Units.MILLISECONDS));
-  }
-
-  private Counter0 getFailureMetric(String name) {
+  public Counter0 getFailureCounterMetric() {
     return metricMaker.newCounter(
         String.format("%s/failure", name),
         new Description(String.format("%s healthcheck failures count", name))
@@ -100,15 +45,11 @@
             .setUnit("failures"));
   }
 
-  @Override
-  public void stop() {
-    for (RegistrationHandle handle : registeredMetrics) {
-      handle.remove();
-    }
-  }
-
-  @VisibleForTesting
-  public void triggerAll() {
-    triggers.forEach(Runnable::run);
+  public Timer0 getLatencyMetric() {
+    return metricMaker.newTimer(
+        String.format("%s/latest_latency", name),
+        new Description(String.format("%s health check latency execution (ms)", name))
+            .setCumulative()
+            .setUnit(Description.Units.MILLISECONDS));
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
index 6ff3d04..304a40c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
@@ -14,9 +14,8 @@
 
 package com.googlesource.gerrit.plugins.healthcheck;
 
-import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.inject.AbstractModule;
 import com.googlesource.gerrit.plugins.healthcheck.check.ActiveWorkersCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.AuthHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.DeadlockCheck;
@@ -25,7 +24,7 @@
 import com.googlesource.gerrit.plugins.healthcheck.check.ProjectsListHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.QueryChangesHealthCheck;
 
-public class HealthCheckSubsystemsModule extends AbstractModule {
+public class HealthCheckSubsystemsModule extends FactoryModule {
 
   @Override
   protected void configure() {
@@ -35,7 +34,8 @@
     bindChecker(AuthHealthCheck.class);
     bindChecker(ActiveWorkersCheck.class);
     bindChecker(DeadlockCheck.class);
-    bind(LifecycleListener.class).to(HealthCheckMetrics.class);
+
+    factory(HealthCheckMetrics.Factory.class);
   }
 
   private void bindChecker(Class<? extends HealthCheck> healthCheckClass) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractHealthCheck.java
index d0181ab..9e4544a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractHealthCheck.java
@@ -16,7 +16,10 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.metrics.Counter0;
+import com.google.gerrit.metrics.Timer0;
 import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics;
 import java.util.Collections;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -32,13 +35,23 @@
   protected volatile StatusSummary latestStatus;
   protected HealthCheckConfig config;
 
+  protected final Counter0 failureCounterMetric;
+  protected final Timer0 latencyMetric;
+
   protected AbstractHealthCheck(
-      ListeningExecutorService executor, HealthCheckConfig config, String name) {
+      ListeningExecutorService executor,
+      HealthCheckConfig config,
+      String name,
+      HealthCheckMetrics.Factory healthCheckMetricsFactory) {
     this.executor = executor;
     this.name = name;
     this.timeout = config.getTimeout(name);
     this.config = config;
     this.latestStatus = StatusSummary.INITIAL_STATUS;
+
+    HealthCheckMetrics healthCheckMetrics = healthCheckMetricsFactory.create(name);
+    this.failureCounterMetric = healthCheckMetrics.getFailureCounterMetric();
+    this.latencyMetric = healthCheckMetrics.getLatencyMetric();
   }
 
   @Override
@@ -61,30 +74,32 @@
                 log.warn("Check {} failed", name, e);
                 healthy = Result.FAILED;
               }
-              return new StatusSummary(
-                  healthy, ts, System.currentTimeMillis() - ts, Collections.emptyMap());
+              Long elapsed = System.currentTimeMillis() - ts;
+              StatusSummary statusSummary =
+                  new StatusSummary(healthy, ts, elapsed, Collections.emptyMap());
+              if (statusSummary.isFailure()) {
+                failureCounterMetric.increment();
+              }
+              latencyMetric.record(elapsed, TimeUnit.MILLISECONDS);
+              return statusSummary;
             });
     try {
       checkStatusSummary = resultFuture.get(timeout, TimeUnit.MILLISECONDS);
     } catch (TimeoutException e) {
       log.warn("Check {} timed out", name, e);
-      checkStatusSummary =
-          new StatusSummary(
-              Result.TIMEOUT, ts, System.currentTimeMillis() - ts, Collections.emptyMap());
+      Long elapsed = System.currentTimeMillis() - ts;
+      checkStatusSummary = new StatusSummary(Result.TIMEOUT, ts, elapsed, Collections.emptyMap());
+      failureCounterMetric.increment();
+      latencyMetric.record(elapsed, TimeUnit.MILLISECONDS);
     } catch (InterruptedException | ExecutionException e) {
       log.warn("Check {} failed while waiting for its future result", name, e);
-      checkStatusSummary =
-          new StatusSummary(
-              Result.FAILED, ts, System.currentTimeMillis() - ts, Collections.emptyMap());
+      Long elapsed = System.currentTimeMillis() - ts;
+      checkStatusSummary = new StatusSummary(Result.FAILED, ts, elapsed, Collections.emptyMap());
+      failureCounterMetric.increment();
+      latencyMetric.record(elapsed, TimeUnit.MILLISECONDS);
     }
-    latestStatus = checkStatusSummary.shallowCopy();
     return checkStatusSummary;
   }
 
   protected abstract Result doCheck() throws Exception;
-
-  @Override
-  public StatusSummary getLatestStatus() {
-    return latestStatus;
-  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ActiveWorkersCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ActiveWorkersCheck.java
index f3afc99..dcf2f0e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ActiveWorkersCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ActiveWorkersCheck.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.server.config.ThreadSettingsConfig;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics;
 import java.util.Optional;
 import org.eclipse.jgit.lib.Config;
 
@@ -33,6 +34,7 @@
   private Integer threshold;
   private Integer interactiveThreadsMaxPoolSize;
   private MetricRegistry metricRegistry;
+  private HealthCheckMetrics.Factory healthCheckMetricsFactory;
 
   @Inject
   public ActiveWorkersCheck(
@@ -40,8 +42,9 @@
       ListeningExecutorService executor,
       HealthCheckConfig healthCheckConfig,
       ThreadSettingsConfig threadSettingsConfig,
-      MetricRegistry metricRegistry) {
-    super(executor, healthCheckConfig, ACTIVEWORKERS);
+      MetricRegistry metricRegistry,
+      HealthCheckMetrics.Factory healthCheckMetricsFactory) {
+    super(executor, healthCheckConfig, ACTIVEWORKERS, healthCheckMetricsFactory);
     this.threshold = healthCheckConfig.getActiveWorkersThreshold(ACTIVEWORKERS);
     this.metricRegistry = metricRegistry;
     this.interactiveThreadsMaxPoolSize =
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AuthHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AuthHealthCheck.java
index f00b155..cd0274d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AuthHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AuthHealthCheck.java
@@ -24,6 +24,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics;
 import java.util.Optional;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,14 +36,16 @@
   private final AccountCache byIdCache;
   private final String username;
   private final String password;
+  private HealthCheckMetrics.Factory healthCheckMetricsFactory;
 
   @Inject
   public AuthHealthCheck(
       ListeningExecutorService executor,
       HealthCheckConfig config,
       Realm realm,
-      AccountCache byIdCache) {
-    super(executor, config, AUTH);
+      AccountCache byIdCache,
+      HealthCheckMetrics.Factory healthCheckMetricsFactory) {
+    super(executor, config, AUTH, healthCheckMetricsFactory);
 
     this.realm = realm;
     this.byIdCache = byIdCache;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/DeadlockCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/DeadlockCheck.java
index db5e83a..a368246 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/DeadlockCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/DeadlockCheck.java
@@ -20,6 +20,7 @@
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics;
 import java.util.Optional;
 
 public class DeadlockCheck extends AbstractHealthCheck {
@@ -28,13 +29,15 @@
       "proc/jvm/thread/num_deadlocked_threads";
 
   private final MetricRegistry metricRegistry;
+  private HealthCheckMetrics.Factory healthCheckMetricsFactory;
 
   @Inject
   public DeadlockCheck(
       ListeningExecutorService executor,
       HealthCheckConfig healthCheckConfig,
-      MetricRegistry metricRegistry) {
-    super(executor, healthCheckConfig, DEADLOCK);
+      MetricRegistry metricRegistry,
+      HealthCheckMetrics.Factory healthCheckMetricsFactory) {
+    super(executor, healthCheckConfig, DEADLOCK, healthCheckMetricsFactory);
     this.metricRegistry = metricRegistry;
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/GlobalHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/GlobalHealthCheck.java
index c464a9e..b004eaa 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/GlobalHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/GlobalHealthCheck.java
@@ -14,22 +14,32 @@
 
 package com.googlesource.gerrit.plugins.healthcheck.check;
 
+import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.GLOBAL;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics;
 import java.util.Arrays;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 
 @Singleton
-public class GlobalHealthCheck implements HealthCheck {
+public class GlobalHealthCheck extends AbstractHealthCheck {
 
   private final DynamicSet<HealthCheck> healthChecks;
-  private volatile StatusSummary latestStatus = StatusSummary.INITIAL_STATUS;
 
   @Inject
-  public GlobalHealthCheck(DynamicSet<HealthCheck> healthChecks) {
+  public GlobalHealthCheck(
+      DynamicSet<HealthCheck> healthChecks,
+      ListeningExecutorService executor,
+      HealthCheckConfig healthCheckConfig,
+      HealthCheckMetrics.Factory healthCheckMetricsFactory) {
+    super(executor, healthCheckConfig, GLOBAL, healthCheckMetricsFactory);
     this.healthChecks = healthChecks;
   }
 
@@ -48,10 +58,18 @@
             ts,
             elapsed,
             checkToResults);
-    latestStatus = globalStatus.shallowCopy();
+    if (globalStatus.isFailure()) {
+      failureCounterMetric.increment();
+    }
+    latencyMetric.record(elapsed, TimeUnit.MILLISECONDS);
     return globalStatus;
   }
 
+  @Override
+  protected Result doCheck() {
+    return run().result;
+  }
+
   public static boolean hasAnyFailureOnResults(Map<String, Object> results) {
     return results.values().stream()
         .filter(
@@ -61,14 +79,4 @@
         .findAny()
         .isPresent();
   }
-
-  @Override
-  public String name() {
-    return "global";
-  }
-
-  @Override
-  public StatusSummary getLatestStatus() {
-    return latestStatus;
-  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheck.java
index 46acdde..7c4f8a1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheck.java
@@ -55,15 +55,9 @@
     public Boolean isFailure() {
       return failingResults.contains(this.result);
     }
-
-    public StatusSummary shallowCopy() {
-      return new StatusSummary(result, ts, elapsed, Collections.emptyMap());
-    }
   }
 
   StatusSummary run();
 
   String name();
-
-  StatusSummary getLatestStatus();
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java
index ac4ba51..97e06d0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java
@@ -21,4 +21,5 @@
   String AUTH = "auth";
   String ACTIVEWORKERS = "activeworkers";
   String DEADLOCK = "deadlock";
+  String GLOBAL = "global";
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/JGitHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/JGitHealthCheck.java
index 9c6930f..d107588 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/JGitHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/JGitHealthCheck.java
@@ -22,6 +22,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics;
 import java.util.Set;
 import org.eclipse.jgit.lib.Repository;
 
@@ -30,13 +31,15 @@
   private final GitRepositoryManager repositoryManager;
   private final Set<Project.NameKey> repositoryNameKeys;
 
+  private HealthCheckMetrics.Factory healthCheckMetricsFactory;
+
   @Inject
   public JGitHealthCheck(
       ListeningExecutorService executor,
       HealthCheckConfig config,
-      GitRepositoryManager repositoryManager) {
-    super(executor, config, JGIT);
-
+      GitRepositoryManager repositoryManager,
+      HealthCheckMetrics.Factory healthCheckMetricsFactory) {
+    super(executor, config, JGIT, healthCheckMetricsFactory);
     this.repositoryManager = repositoryManager;
     this.repositoryNameKeys = config.getJGITRepositories(JGIT);
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ProjectsListHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ProjectsListHealthCheck.java
index 4da142d..9850a10 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ProjectsListHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ProjectsListHealthCheck.java
@@ -22,6 +22,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics;
 import java.util.SortedMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,10 +33,15 @@
   private static final int PROJECTS_LIST_LIMIT = 100;
   private final ListProjects listProjects;
 
+  private HealthCheckMetrics.Factory healthCheckMetricsFactory;
+
   @Inject
   public ProjectsListHealthCheck(
-      ListeningExecutorService executor, HealthCheckConfig config, ListProjects listProjects) {
-    super(executor, config, PROJECTSLIST);
+      ListeningExecutorService executor,
+      HealthCheckConfig config,
+      ListProjects listProjects,
+      HealthCheckMetrics.Factory healthCheckMetricsFactory) {
+    super(executor, config, PROJECTSLIST, healthCheckMetricsFactory);
 
     this.listProjects = listProjects;
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/QueryChangesHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/QueryChangesHealthCheck.java
index 5ccaf9b..9b6825f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/QueryChangesHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/QueryChangesHealthCheck.java
@@ -24,6 +24,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics;
 import java.util.List;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,13 +37,16 @@
   private final int limit;
   private final OneOffRequestContext oneOffCtx;
 
+  private HealthCheckMetrics.Factory healthCheckMetricsFactory;
+
   @Inject
   public QueryChangesHealthCheck(
       ListeningExecutorService executor,
       HealthCheckConfig config,
       Provider<QueryChanges> queryChangesProvider,
-      OneOffRequestContext oneOffCtx) {
-    super(executor, config, QUERYCHANGES);
+      OneOffRequestContext oneOffCtx,
+      HealthCheckMetrics.Factory healthCheckMetricsFactory) {
+    super(executor, config, QUERYCHANGES, healthCheckMetricsFactory);
     this.queryChangesProvider = queryChangesProvider;
     this.config = config;
     this.limit = config.getLimit(QUERYCHANGES);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckTest.java
new file mode 100644
index 0000000..f67802e
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckTest.java
@@ -0,0 +1,195 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.healthcheck;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.gerrit.metrics.Counter0;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer0;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.healthcheck.check.AbstractHealthCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AbstractHealthCheckTest {
+
+  private ListeningExecutorService executor;
+  MetricMaker testMetricMaker;
+  DummyHealthCheckMetricsFactory healthCheckMetricsFactory = new DummyHealthCheckMetricsFactory();
+
+  @Before
+  public void setUp() throws Exception {
+    testMetricMaker = new TestMetricMaker();
+  }
+
+  @Test
+  public void shouldPublishAllMetricsWhenFailing() {
+    TestCheck testCheck = createFailingTestCheck();
+
+    testCheck.run();
+
+    TestMetricMaker testMetricMaker = healthCheckMetricsFactory.getMetricMaker();
+    assertThat(testMetricMaker.getFailureCount()).isEqualTo(TestMetricMaker.expectedFailureCount);
+    assertThat(testMetricMaker.getLatency()).isEqualTo(TestMetricMaker.expectedLatency);
+  }
+
+  @Test
+  public void shouldPublishAllMetricsWhenTimingOut() {
+    TestCheck testCheck = createTimingOutTestCheck();
+
+    testCheck.run();
+
+    TestMetricMaker testMetricMaker = healthCheckMetricsFactory.getMetricMaker();
+    assertThat(testMetricMaker.getFailureCount()).isEqualTo(TestMetricMaker.expectedFailureCount);
+    assertThat(testMetricMaker.getLatency()).isEqualTo(TestMetricMaker.expectedLatency);
+  }
+
+  @Test
+  public void shouldPublishOnlyLatencyMetricsWhenPassing() {
+    TestCheck testCheck = createPassingTestCheck();
+
+    testCheck.run();
+
+    TestMetricMaker testMetricMaker = healthCheckMetricsFactory.getMetricMaker();
+    assertThat(testMetricMaker.getFailureCount()).isEqualTo(0L);
+    assertThat(testMetricMaker.getLatency()).isEqualTo(TestMetricMaker.expectedLatency);
+  }
+
+  @Test
+  public void shouldPublishOnlyLatencyMetricsWhenDisabled() {
+    TestCheck testCheck = createDisabledTestCheck();
+
+    testCheck.run();
+
+    TestMetricMaker testMetricMaker = healthCheckMetricsFactory.getMetricMaker();
+    assertThat(testMetricMaker.getFailureCount()).isEqualTo(0L);
+    assertThat(testMetricMaker.getLatency()).isEqualTo(TestMetricMaker.expectedLatency);
+  }
+
+  public TestCheck createPassingTestCheck() {
+    return createTestCheckWithStatus(HealthCheck.Result.PASSED);
+  }
+
+  public TestCheck createFailingTestCheck() {
+    return createTestCheckWithStatus(HealthCheck.Result.FAILED);
+  }
+
+  public TestCheck createDisabledTestCheck() {
+    return createTestCheckWithStatus(HealthCheck.Result.DISABLED);
+  }
+
+  public TestCheck createTimingOutTestCheck() {
+    return createTestCheckWithStatus(HealthCheck.Result.TIMEOUT);
+  }
+
+  private TestCheck createTestCheckWithStatus(HealthCheck.Result result) {
+    return new TestCheck(
+        executor, HealthCheckConfig.DEFAULT_CONFIG, "testCheck", healthCheckMetricsFactory, result);
+  }
+
+  private static class TestCheck extends AbstractHealthCheck {
+    @Inject private ListeningExecutorService executor;
+    private final Result finalResult;
+    private DummyHealthCheckMetricsFactory healthCheckMetricsFactory =
+        new DummyHealthCheckMetricsFactory();
+
+    public TestCheck(
+        ListeningExecutorService executor,
+        HealthCheckConfig config,
+        String name,
+        HealthCheckMetrics.Factory healthCheckMetricsFactory,
+        Result finalResult) {
+      super(
+          MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)),
+          config,
+          name,
+          healthCheckMetricsFactory);
+      this.finalResult = finalResult;
+    }
+
+    @Override
+    protected Result doCheck() throws Exception {
+      return finalResult;
+    }
+  }
+
+  private static class TestMetricMaker extends DisabledMetricMaker {
+    private Long failureCount = 0L;
+    private Long latency = 0L;
+    public static Long expectedFailureCount = 1L;
+    public static Long expectedLatency = 100L;
+
+    @Override
+    public Counter0 newCounter(String name, Description desc) {
+      return new Counter0() {
+
+        @Override
+        public void incrementBy(long value) {
+          failureCount += value;
+        }
+
+        @Override
+        public void increment() {
+          failureCount = expectedFailureCount;
+        }
+
+        @Override
+        public void remove() {}
+      };
+    }
+
+    @Override
+    public Timer0 newTimer(String name, Description desc) {
+      return new Timer0(name) {
+        @Override
+        protected void doRecord(long value, TimeUnit unit) {
+          latency = expectedLatency;
+        }
+
+        @Override
+        public void remove() {}
+      };
+    }
+
+    public Long getLatency() {
+      return latency;
+    }
+
+    public Long getFailureCount() {
+      return failureCount;
+    }
+  }
+
+  private static class DummyHealthCheckMetricsFactory implements HealthCheckMetrics.Factory {
+    private final TestMetricMaker metricMaker = new TestMetricMaker();
+
+    @Override
+    public HealthCheckMetrics create(String name) {
+      return new HealthCheckMetrics(metricMaker, name);
+    }
+
+    public TestMetricMaker getMetricMaker() {
+      return metricMaker;
+    }
+  }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ActiveWorkersCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ActiveWorkersCheckTest.java
index 71bef43..df88c48 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ActiveWorkersCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ActiveWorkersCheckTest.java
@@ -32,15 +32,7 @@
 import org.junit.Test;
 
 public class ActiveWorkersCheckTest {
-
-  @Test
-  public void shouldPassCheckWhenNoMetric() {
-
-    Injector injector = testInjector(new TestModule(new Config(), new MetricRegistry()));
-
-    ActiveWorkersCheck check = createCheck(injector);
-    assertThat(check.run().result).isEqualTo(Result.PASSED);
-  }
+  HealthCheckMetrics.Factory healthCheckMetricsFactory = new DummyHealthCheckMetricsFactory();
 
   @Test
   public void shouldPassCheckWhenNoActiveWorkers() {
@@ -139,7 +131,8 @@
         injector.getInstance(ListeningExecutorService.class),
         healtchCheckConfig,
         injector.getInstance(ThreadSettingsConfig.class),
-        injector.getInstance(MetricRegistry.class));
+        injector.getInstance(MetricRegistry.class),
+        healthCheckMetricsFactory);
   }
 
   private class TestModule extends AbstractModule {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/DeadlockCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/DeadlockCheckTest.java
index d53a1d8..79a4bc4 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/DeadlockCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/DeadlockCheckTest.java
@@ -32,6 +32,8 @@
 
 public class DeadlockCheckTest {
 
+  HealthCheckMetrics.Factory healthCheckMetricsFactory = new DummyHealthCheckMetricsFactory();
+
   @Test
   public void shouldPassCheckWhenNoMetric() {
 
@@ -91,7 +93,8 @@
     return new DeadlockCheck(
         injector.getInstance(ListeningExecutorService.class),
         DEFAULT_CONFIG,
-        injector.getInstance(MetricRegistry.class));
+        injector.getInstance(MetricRegistry.class),
+        healthCheckMetricsFactory);
   }
 
   private class TestModule extends AbstractModule {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/DummyHealthCheckMetricsFactory.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/DummyHealthCheckMetricsFactory.java
new file mode 100644
index 0000000..7523e95
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/DummyHealthCheckMetricsFactory.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.healthcheck;
+
+import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.metrics.MetricMaker;
+import org.junit.Ignore;
+
+@Ignore
+public class DummyHealthCheckMetricsFactory implements HealthCheckMetrics.Factory {
+  @Override
+  public HealthCheckMetrics create(String name) {
+    MetricMaker metricMaker = new DisabledMetricMaker();
+    return new HealthCheckMetrics(metricMaker, name);
+  }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetricsTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetricsTest.java
index 1c01950..b186658 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetricsTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetricsTest.java
@@ -16,115 +16,58 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.metrics.CallbackMetric0;
 import com.google.gerrit.metrics.Counter0;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.DisabledMetricMaker;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Singleton;
-import com.googlesource.gerrit.plugins.healthcheck.check.AbstractHealthCheck;
-import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
-import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.Result;
-import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.StatusSummary;
-import java.util.Collections;
-import org.junit.Before;
+import com.google.gerrit.metrics.Timer0;
+import java.util.concurrent.TimeUnit;
 import org.junit.Test;
 
 public class HealthCheckMetricsTest {
 
-  @Inject private ListeningExecutorService executor;
-  private TestMetrics testMetrics = new TestMetrics();
-
-  @Before
-  public void setUp() throws Exception {
-    testMetrics.reset();
-  }
-
-  private void setWithStatusSummary(StatusSummary StatusSummary) {
-    TestHealthCheck testHealthCheck = new TestHealthCheck(executor, "test", StatusSummary);
-
-    Injector injector =
-        testInjector(
-            new AbstractModule() {
-              @Override
-              protected void configure() {
-                DynamicSet.bind(binder(), HealthCheck.class).toInstance(testHealthCheck);
-                bind(MetricMaker.class).toInstance(testMetrics);
-                bind(LifecycleListener.class).to(HealthCheckMetrics.class);
-              }
-            });
-    HealthCheckMetrics healthCheckMetrics = injector.getInstance(HealthCheckMetrics.class);
-
-    healthCheckMetrics.start();
-    testHealthCheck.run();
-    healthCheckMetrics.triggerAll();
+  @Test
+  public void shouldReturnACounterWithCorrectValues() {
+    String testMetricName = "testMetric";
+    TestMetrics testMetrics = new TestMetrics();
+    HealthCheckMetrics healthCheckMetrics = new HealthCheckMetrics(testMetrics, testMetricName);
+    healthCheckMetrics.getFailureCounterMetric();
+    assertThat(testMetrics.metricName).isEqualTo(testMetricName + "/failure");
+    assertThat(testMetrics.metricDescription.toString())
+        .isEqualTo(
+            new Description(String.format("%s healthcheck failures count", testMetricName))
+                .setCumulative()
+                .setRate()
+                .setUnit("failures")
+                .toString());
   }
 
   @Test
-  public void shouldSendCounterWhenStatusSummaryFailed() {
-    Long elapsed = 100L;
-    setWithStatusSummary(new StatusSummary(Result.FAILED, 1L, elapsed, Collections.emptyMap()));
-
-    assertThat(testMetrics.getFailures()).isEqualTo(1);
-    assertThat(testMetrics.getLatency()).isEqualTo(elapsed);
+  public void shouldReturnATimerWithCorrectValues() {
+    String testMetricName = "testMetric";
+    TestMetrics testMetrics = new TestMetrics();
+    HealthCheckMetrics healthCheckMetrics = new HealthCheckMetrics(testMetrics, testMetricName);
+    healthCheckMetrics.getLatencyMetric();
+    assertThat(testMetrics.metricName).isEqualTo(testMetricName + "/latest_latency");
+    assertThat(testMetrics.metricDescription.toString())
+        .isEqualTo(
+            new Description(String.format("%s health check latency execution (ms)", testMetricName))
+                .setCumulative()
+                .setUnit(Description.Units.MILLISECONDS)
+                .toString());
   }
 
-  @Test
-  public void shouldSendCounterWhenStatusSummaryTimeout() {
-    Long elapsed = 100L;
-    setWithStatusSummary(new StatusSummary(Result.TIMEOUT, 1L, elapsed, Collections.emptyMap()));
-
-    assertThat(testMetrics.getFailures()).isEqualTo(1);
-    assertThat(testMetrics.getLatency()).isEqualTo(elapsed);
-  }
-
-  @Test
-  public void shouldNOTSendCounterWhenStatusSummarySuccess() {
-    Long elapsed = 100L;
-    setWithStatusSummary(new StatusSummary(Result.PASSED, 1L, elapsed, Collections.emptyMap()));
-
-    assertThat(testMetrics.failures).isEqualTo(0L);
-    assertThat(testMetrics.getLatency()).isEqualTo(elapsed);
-  }
-
-  private Injector testInjector(AbstractModule testModule) {
-    return Guice.createInjector(new HealthCheckModule(), testModule);
-  }
-
-  @Singleton
   private static class TestMetrics extends DisabledMetricMaker {
-    private Long failures = 0L;
-    private Long latency = 0L;
-
-    public Long getFailures() {
-      return failures;
-    }
-
-    public Long getLatency() {
-      return latency;
-    }
-
-    public void reset() {
-      failures = 0L;
-      latency = 0L;
-    }
+    String metricName;
+    Description metricDescription;
 
     @Override
     public Counter0 newCounter(String name, Description desc) {
+      metricName = name;
+      metricDescription = desc;
       return new Counter0() {
+
         @Override
-        public void incrementBy(long value) {
-          if (!name.startsWith("global/")) {
-            failures += value;
-          }
-        }
+        public void incrementBy(long value) {}
 
         @Override
         public void remove() {}
@@ -132,38 +75,16 @@
     }
 
     @Override
-    public <V> CallbackMetric0<V> newCallbackMetric(
-        String name, Class<V> valueClass, Description desc) {
-      return new CallbackMetric0<V>() {
+    public Timer0 newTimer(String name, Description desc) {
+      metricName = name;
+      metricDescription = desc;
+      return new Timer0(name) {
         @Override
-        public void set(V value) {
-          if (!name.startsWith("global/")) {
-            latency = (Long) value;
-          }
-        }
+        protected void doRecord(long value, TimeUnit unit) {}
 
         @Override
         public void remove() {}
       };
     }
   }
-
-  private static class TestHealthCheck extends AbstractHealthCheck {
-
-    protected TestHealthCheck(
-        ListeningExecutorService executor, String name, StatusSummary returnStatusSummary) {
-      super(executor, HealthCheckConfig.DEFAULT_CONFIG, name);
-      this.latestStatus = returnStatusSummary;
-    }
-
-    @Override
-    protected Result doCheck() {
-      return latestStatus.result;
-    }
-
-    @Override
-    public StatusSummary run() {
-      return latestStatus;
-    }
-  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java
index 78b89e4..b2fb862 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java
@@ -17,16 +17,17 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig.DEFAULT_CONFIG;
 
+import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.metrics.DisabledMetricMaker;
-import com.google.gerrit.metrics.MetricMaker;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
+import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.googlesource.gerrit.plugins.healthcheck.api.HealthCheckStatusEndpoint;
 import com.googlesource.gerrit.plugins.healthcheck.check.AbstractHealthCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.GlobalHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
 import java.util.concurrent.Executors;
 import javax.servlet.http.HttpServletResponse;
@@ -34,13 +35,24 @@
 
 public class HealthCheckStatusEndpointTest {
 
+  @Inject private ListeningExecutorService executor;
+  HealthCheckMetrics.Factory healthCheckMetricsFactory = new DummyHealthCheckMetricsFactory();
+
   public static class TestHealthCheck extends AbstractHealthCheck {
     private final HealthCheck.Result checkResult;
     private final long sleep;
 
     public TestHealthCheck(
-        HealthCheckConfig config, String checkName, HealthCheck.Result result, long sleep) {
-      super(MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)), config, checkName);
+        HealthCheckConfig config,
+        String checkName,
+        HealthCheck.Result result,
+        long sleep,
+        HealthCheckMetrics.Factory healthCheckMetricsFactory) {
+      super(
+          MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)),
+          config,
+          checkName,
+          healthCheckMetricsFactory);
       this.checkResult = result;
       this.sleep = sleep;
     }
@@ -53,11 +65,6 @@
       }
       return checkResult;
     }
-
-    @Override
-    public StatusSummary getLatestStatus() {
-      return this.latestStatus;
-    }
   }
 
   @Test
@@ -67,12 +74,22 @@
             new AbstractModule() {
               @Override
               protected void configure() {
-                DynamicSet.bind(binder(), HealthCheck.class)
-                    .toInstance(
-                        new TestHealthCheck(
-                            DEFAULT_CONFIG, "checkOk", HealthCheck.Result.PASSED, 0));
-                bind(HealthCheckConfig.class).toInstance(DEFAULT_CONFIG);
-                DynamicSet.bind(binder(), MetricMaker.class).toInstance(new DisabledMetricMaker());
+                HealthCheckConfig config =
+                    new HealthCheckConfig("[healthcheck]\n" + "timeout = 20");
+                TestHealthCheck passingHealthCheck =
+                    new TestHealthCheck(
+                        DEFAULT_CONFIG,
+                        "checkOk",
+                        HealthCheck.Result.PASSED,
+                        0,
+                        healthCheckMetricsFactory);
+                DynamicSet<HealthCheck> healthChecks = new DynamicSet<>();
+                healthChecks.add("passingHealthCheck", passingHealthCheck);
+                GlobalHealthCheck globalHealthCheck =
+                    new GlobalHealthCheck(
+                        healthChecks, executor, config, healthCheckMetricsFactory);
+                bind(GlobalHealthCheck.class).toInstance(globalHealthCheck);
+                bind(HealthCheckConfig.class).toInstance(config);
               }
             });
 
@@ -92,9 +109,19 @@
               protected void configure() {
                 HealthCheckConfig config =
                     new HealthCheckConfig("[healthcheck]\n" + "timeout = 20");
-                DynamicSet.bind(binder(), HealthCheck.class)
-                    .toInstance(
-                        new TestHealthCheck(config, "checkOk", HealthCheck.Result.PASSED, 30));
+                TestHealthCheck testHealthCheck =
+                    new TestHealthCheck(
+                        config,
+                        "checkOk",
+                        HealthCheck.Result.PASSED,
+                        30,
+                        healthCheckMetricsFactory);
+                DynamicSet<HealthCheck> healthChecks = new DynamicSet<>();
+                healthChecks.add("testHealthCheck", testHealthCheck);
+                GlobalHealthCheck globalHealthCheck =
+                    new GlobalHealthCheck(
+                        healthChecks, executor, config, healthCheckMetricsFactory);
+                bind(GlobalHealthCheck.class).toInstance(globalHealthCheck);
                 bind(HealthCheckConfig.class).toInstance(config);
               }
             });
@@ -113,14 +140,32 @@
             new AbstractModule() {
               @Override
               protected void configure() {
-                DynamicSet.bind(binder(), HealthCheck.class)
-                    .toInstance(
-                        new TestHealthCheck(
-                            DEFAULT_CONFIG, "checkOk", HealthCheck.Result.PASSED, 0));
-                DynamicSet.bind(binder(), HealthCheck.class)
-                    .toInstance(
-                        new TestHealthCheck(
-                            DEFAULT_CONFIG, "checkKo", HealthCheck.Result.FAILED, 0));
+                HealthCheckConfig config =
+                    new HealthCheckConfig("[healthcheck]\n" + "timeout = 20");
+                TestHealthCheck passingHealthCheck =
+                    new TestHealthCheck(
+                        DEFAULT_CONFIG,
+                        "checkOk",
+                        HealthCheck.Result.PASSED,
+                        0,
+                        healthCheckMetricsFactory);
+
+                TestHealthCheck failingHealthCheck =
+                    new TestHealthCheck(
+                        DEFAULT_CONFIG,
+                        "checkKo",
+                        HealthCheck.Result.FAILED,
+                        0,
+                        healthCheckMetricsFactory);
+
+                DynamicSet<HealthCheck> healthChecks = new DynamicSet<>();
+                healthChecks.add("passingHealthCheck", passingHealthCheck);
+                healthChecks.add("failingHealthCheck", failingHealthCheck);
+                GlobalHealthCheck globalHealthCheck =
+                    new GlobalHealthCheck(
+                        healthChecks, executor, config, healthCheckMetricsFactory);
+                bind(GlobalHealthCheck.class).toInstance(globalHealthCheck);
+                bind(HealthCheckConfig.class).toInstance(config);
               }
             });
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
index 74956c6..3c0adae 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
@@ -52,6 +52,8 @@
 
   @Inject private ListeningExecutorService executor;
 
+  HealthCheckMetrics.Factory healthCheckMetricsFactory = new DummyHealthCheckMetricsFactory();
+
   @Before
   public void setupAllProjects() throws Exception {
     Guice.createInjector(new HealthCheckModule()).injectMembers(this);
@@ -68,14 +70,16 @@
   @Test
   public void shouldBeHealthyWhenJGitIsWorking() {
     JGitHealthCheck check =
-        new JGitHealthCheck(executor, DEFAULT_CONFIG, getWorkingRepositoryManager());
+        new JGitHealthCheck(
+            executor, DEFAULT_CONFIG, getWorkingRepositoryManager(), healthCheckMetricsFactory);
     assertThat(check.run().result).isEqualTo(Result.PASSED);
   }
 
   @Test
   public void shouldBeUnhealthyWhenJGitIsFailingForAllRepos() {
     JGitHealthCheck jGitHealthCheck =
-        new JGitHealthCheck(executor, DEFAULT_CONFIG, getFailingGitRepositoryManager());
+        new JGitHealthCheck(
+            executor, DEFAULT_CONFIG, getFailingGitRepositoryManager(), healthCheckMetricsFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.FAILED);
   }
 
@@ -89,7 +93,8 @@
                 + "  project = All-Users\n"
                 + "  project = Not-Existing-Repo");
     JGitHealthCheck jGitHealthCheck =
-        new JGitHealthCheck(executor, config, getWorkingRepositoryManager());
+        new JGitHealthCheck(
+            executor, config, getWorkingRepositoryManager(), healthCheckMetricsFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.FAILED);
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ProjectsListHealthCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ProjectsListHealthCheckTest.java
index 2ab0d83..c8d25b6 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ProjectsListHealthCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ProjectsListHealthCheckTest.java
@@ -34,6 +34,8 @@
 public class ProjectsListHealthCheckTest {
   @Inject private ListeningExecutorService executor;
 
+  HealthCheckMetrics.Factory healthCheckMetricsFactory = new DummyHealthCheckMetricsFactory();
+
   private Config gerritConfig = new Config();
 
   @Before
@@ -44,14 +46,16 @@
   @Test
   public void shouldBeHealthyWhenListProjectsWorks() {
     ProjectsListHealthCheck jGitHealthCheck =
-        new ProjectsListHealthCheck(executor, DEFAULT_CONFIG, getWorkingProjectList(0));
+        new ProjectsListHealthCheck(
+            executor, DEFAULT_CONFIG, getWorkingProjectList(0), healthCheckMetricsFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.PASSED);
   }
 
   @Test
   public void shouldBeUnhealthyWhenListProjectsIsFailing() {
     ProjectsListHealthCheck jGitHealthCheck =
-        new ProjectsListHealthCheck(executor, DEFAULT_CONFIG, getFailingProjectList());
+        new ProjectsListHealthCheck(
+            executor, DEFAULT_CONFIG, getFailingProjectList(), healthCheckMetricsFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.FAILED);
   }
 
@@ -59,7 +63,10 @@
   public void shouldBeUnhealthyWhenListProjectsIsTimingOut() {
     ProjectsListHealthCheck jGitHealthCheck =
         new ProjectsListHealthCheck(
-            executor, DEFAULT_CONFIG, getWorkingProjectList(DEFAULT_CONFIG.getTimeout() * 2));
+            executor,
+            DEFAULT_CONFIG,
+            getWorkingProjectList(DEFAULT_CONFIG.getTimeout() * 2),
+            healthCheckMetricsFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.TIMEOUT);
   }