Send metrics when running healthcheck

Upon running healthcheck, healthcheck components metrics are propagated.

Failure count:
* plugins_healthcheck_jgit_failure_total
* plugins_healthcheck_reviewdb_failure_total
* plugins_healthcheck_projectslist_failure_total

Latency (Timer): the elapsed time for executing that specific
healthcheck
* plugins_healthcheck_jgit_latency
* plugins_healthcheck_projectslist_latency
* plugins_healthcheck_reviewdb_latency

Feature: Issue 10321
Change-Id: I1b393258dc823b19b74106fa7fce377d5c8bba92
diff --git a/README.md b/README.md
index edb538d..fe38bc8 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@
 - elapsed: elapsed time in millis to perform the check
 - reviewdb: check that Gerrit can connect and query ReviewDb
 - projectslist: check that Gerrit can list projects
-- auth: check that Gerrit can authenticate users
+- jgit: check that Gerrit can access repositories 
 
 Each check returns a JSON payload with the following information:
 
@@ -72,7 +72,7 @@
     "elapsed": 100,
     "result": "passed"
   },
-  "auth": {
+  "jgit": {
     "ts": 139402910202,
     "elapsed": 80,
     "result": "passed"
@@ -101,7 +101,7 @@
     "elapsed": 100,
     "result": "timeout"
   },
-  "auth": {
+  "jgit": {
     "ts": 139402910202,
     "elapsed": 80,
     "result": "passed"
@@ -109,3 +109,12 @@
 }
 ```
 
+## Metrics
+
+As for all other endpoints in Gerrit, some metrics are automatically emitted when the  `/config/server/healthcheck~status`
+endpoint is hit (thanks to the [Dropwizard](https://metrics.dropwizard.io/3.1.0/manual/core/) library).
+
+Some additional metrics are also produced to give extra insights on their result about results and latency of healthcheck
+sub component, such as jgit, reviewdb, etc.
+
+More information can be found in the [config.md](resources/Documentation/config.md) file.
\ No newline at end of file
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 5857bc4..a497a83 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
@@ -16,8 +16,10 @@
 
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.inject.AbstractModule;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.JGitHealthCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.MetricsHandler;
 import com.googlesource.gerrit.plugins.healthcheck.check.ProjectsListHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.ReviewDbHealthCheck;
 
@@ -25,6 +27,11 @@
 
   @Override
   protected void configure() {
+    install(
+        new FactoryModuleBuilder()
+            .implement(MetricsHandler.class, MetricsHandler.class)
+            .build(MetricsHandler.Factory.class));
+
     bindChecker(ReviewDbHealthCheck.class);
     bindChecker(JGitHealthCheck.class);
     bindChecker(ProjectsListHealthCheck.class);
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 5c8d410..a71e287 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
@@ -27,10 +27,13 @@
   public static final long CHECK_TIMEOUT = 500L;
   private final String name;
   private final ListeningExecutorService executor;
+  private final MetricsHandler metricsHandler;
 
-  protected AbstractHealthCheck(ListeningExecutorService executor, String name) {
+  protected AbstractHealthCheck(
+      ListeningExecutorService executor, String name, MetricsHandler.Factory metricsHandler) {
     this.executor = executor;
     this.name = name;
+    this.metricsHandler = metricsHandler.create(name);
   }
 
   @Override
@@ -41,6 +44,7 @@
   @Override
   public Status run() {
     final long ts = System.currentTimeMillis();
+    Status status = null;
     ListenableFuture<Status> resultFuture =
         executor.submit(
             () -> {
@@ -53,16 +57,18 @@
               }
               return new Status(healthy, ts, System.currentTimeMillis() - ts);
             });
-
     try {
-      return resultFuture.get(CHECK_TIMEOUT, TimeUnit.MILLISECONDS);
+      status = resultFuture.get(CHECK_TIMEOUT, TimeUnit.MILLISECONDS);
     } catch (TimeoutException e) {
       log.warn("Check {} timed out", name, e);
-      return new Status(Result.TIMEOUT, ts, System.currentTimeMillis() - ts);
+      status = new Status(Result.TIMEOUT, ts, System.currentTimeMillis() - ts);
     } catch (InterruptedException | ExecutionException e) {
       log.warn("Check {} failed while waiting for its future result", name, e);
-      return new Status(Result.FAILED, ts, System.currentTimeMillis() - ts);
+      status = new Status(Result.FAILED, ts, System.currentTimeMillis() - ts);
+    } finally {
+      metricsHandler.sendMetrics(status);
     }
+    return status;
   }
 
   protected abstract Result doCheck() throws Exception;
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 355b0cf..6f44ca4 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
@@ -37,6 +37,10 @@
       this.ts = ts;
       this.elapsed = elapsed;
     }
+
+    protected Boolean isFailure() {
+      return this.result != Result.PASSED;
+    }
   }
 
   Status run();
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 a23e0d1..7de042c 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
@@ -31,8 +31,9 @@
   public JGitHealthCheck(
       ListeningExecutorService executor,
       GitRepositoryManager repositoryManager,
-      AllProjectsName allProjectsName) {
-    super(executor, JGIT);
+      AllProjectsName allProjectsName,
+      MetricsHandler.Factory metricsHandlerFactory) {
+    super(executor, JGIT, metricsHandlerFactory);
     this.repositoryManager = repositoryManager;
     this.allProjectsName = allProjectsName;
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/MetricsHandler.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/MetricsHandler.java
new file mode 100644
index 0000000..c28f52a
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/MetricsHandler.java
@@ -0,0 +1,43 @@
+package com.googlesource.gerrit.plugins.healthcheck.check;
+
+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.assistedinject.Assisted;
+import java.util.concurrent.TimeUnit;
+
+public class MetricsHandler {
+  private final Timer0 latencyMetric;
+  private final Counter0 failureMetric;
+
+  public interface Factory {
+    public MetricsHandler create(String name);
+  }
+
+  @Inject
+  public MetricsHandler(@Assisted String name, MetricMaker metricMaker) {
+    this.latencyMetric =
+        metricMaker.newTimer(
+            name + "/latency",
+            new Description(String.format("%s health check latency execution (ms)", name))
+                .setCumulative()
+                .setUnit(Description.Units.MILLISECONDS));
+
+    this.failureMetric =
+        metricMaker.newCounter(
+            name + "/failure",
+            new Description(String.format("%s healthcheck failures count", name))
+                .setCumulative()
+                .setRate()
+                .setUnit("failures"));
+  }
+
+  public void sendMetrics(HealthCheck.Status status) {
+    latencyMetric.record(status.elapsed, TimeUnit.MILLISECONDS);
+    if (status.isFailure()) {
+      failureMetric.increment();
+    }
+  }
+}
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 580f3a0..c31a286 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
@@ -30,8 +30,11 @@
   private final ListProjects listProjects;
 
   @Inject
-  public ProjectsListHealthCheck(ListeningExecutorService executor, ListProjects listProjects) {
-    super(executor, PROJECTSLIST);
+  public ProjectsListHealthCheck(
+      ListeningExecutorService executor,
+      ListProjects listProjects,
+      MetricsHandler.Factory metricHandlerFactory) {
+    super(executor, PROJECTSLIST, metricHandlerFactory);
     this.listProjects = listProjects;
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ReviewDbHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ReviewDbHealthCheck.java
index 138997b..7bd60cf 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ReviewDbHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ReviewDbHealthCheck.java
@@ -26,8 +26,11 @@
   private final SchemaFactory<ReviewDb> reviewDb;
 
   @Inject
-  public ReviewDbHealthCheck(ListeningExecutorService executor, SchemaFactory<ReviewDb> reviewDb) {
-    super(executor, REVIEWDB);
+  public ReviewDbHealthCheck(
+      ListeningExecutorService executor,
+      SchemaFactory<ReviewDb> reviewDb,
+      MetricsHandler.Factory metricHandlerFactory) {
+    super(executor, REVIEWDB, metricHandlerFactory);
     this.reviewDb = reviewDb;
   }
 
diff --git a/src/resources/Documentation/config.md b/src/resources/Documentation/config.md
new file mode 100644
index 0000000..72a3ad5
--- /dev/null
+++ b/src/resources/Documentation/config.md
@@ -0,0 +1,86 @@
+## Metrics
+
+As for all other endpoints in Gerrit, some metrics are automatically emitted when the  `/config/server/healthcheck~status`
+endpoint is hit (thanks to the [Dropwizard](https://metrics.dropwizard.io/3.1.0/manual/core/) library).
+For example, some of the metrics exposed will be:
+
+```
+# HELP http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint Generated from Dropwizard metric import (metric=http/server/rest_api/response_bytes/healthcheck-com.googlesource.gerrit.plugins.healthcheck.api.HealthCheckStatusEndpoint, type=com.codahale.metrics.Histogram)
+# TYPE http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint summary
+http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.5",} 308.0
+http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.75",} 308.0
+http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.95",} 308.0
+http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.98",} 310.0
+http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.99",} 310.0
+http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.999",} 310.0
+http_server_rest_api_response_bytes_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint_count 4.0
+
+# HELP http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint Generated from Dropwizard metric import (metric=http/server/rest_api/server_latency/healthcheck-com.googlesource.gerrit.plugins.healthcheck.api.HealthCheckStatusEndpoint, type=com.codahale.metrics.Timer)
+# TYPE http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint summary
+http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.5",} 0.008638662
+http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.75",} 0.008638662
+http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.95",} 0.010553707
+http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.98",} 0.147902061
+http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.99",} 0.147902061
+http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint{quantile="0.999",} 0.147902061
+http_server_rest_api_server_latency_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint_count 4.0
+
+# HELP http_server_rest_api_count_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint_total Generated from Dropwizard metric import (metric=http/server/rest_api/count/healthcheck-com.googlesource.gerrit.plugins.healthcheck.api.HealthCheckStatusEndpoint, type=com.codahale.metrics.Meter)
+# TYPE http_server_rest_api_count_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint_total counter
+http_server_rest_api_count_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint_total 4.0
+
+# HELP http_server_rest_api_error_count_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint_500_total Generated from Dropwizard metric import (metric=http/server/rest_api/error_count/healthcheck-com.googlesource.gerrit.plugins.healthcheck.api.HealthCheckStatusEndpoint/500, type=com.codahale.metrics.Meter)
+# TYPE http_server_rest_api_error_count_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint_500_total counter
+http_server_rest_api_error_count_healthcheck_com_googlesource_gerrit_plugins_healthcheck_api_HealthCheckStatusEndpoint_500_total 2.0
+```
+
+However some extra metrics are also emitted to expose more details about the healthcheck result.
+Specifically two metrics are emitted for each component contributing to the overall healthcheck result (JGIT, PROJECTLIST, REVIEWDB).
+* plugins_healthcheck_<healthcheck_component>_latency: the cumulative latency (in ms) of performing the healthcheck for this specific component
+* plugins_healthcheck_jgit_failure_total: the cumulative number of failures for this specific component
+
+```
+# HELP plugins_healthcheck_jgit_latency Generated from Dropwizard metric import (metric=plugins/healthcheck/jgit/latency, type=com.codahale.metrics.Timer)
+# TYPE plugins_healthcheck_jgit_latency summary
+plugins_healthcheck_jgit_latency{quantile="0.5",} 0.002
+plugins_healthcheck_jgit_latency{quantile="0.75",} 0.002
+plugins_healthcheck_jgit_latency{quantile="0.95",} 0.002
+plugins_healthcheck_jgit_latency{quantile="0.98",} 0.002
+plugins_healthcheck_jgit_latency{quantile="0.99",} 0.002
+plugins_healthcheck_jgit_latency{quantile="0.999",} 0.002
+plugins_healthcheck_jgit_latency_count 1.0
+
+# HELP plugins_healthcheck_projectslist_latency Generated from Dropwizard metric import (metric=plugins/healthcheck/projectslist/latency, type=com.codahale.metrics.Timer)
+# TYPE plugins_healthcheck_projectslist_latency summary
+plugins_healthcheck_projectslist_latency{quantile="0.5",} 0.001
+plugins_healthcheck_projectslist_latency{quantile="0.75",} 0.001
+plugins_healthcheck_projectslist_latency{quantile="0.95",} 0.001
+plugins_healthcheck_projectslist_latency{quantile="0.98",} 0.001
+plugins_healthcheck_projectslist_latency{quantile="0.99",} 0.001
+plugins_healthcheck_projectslist_latency{quantile="0.999",} 0.001
+plugins_healthcheck_projectslist_latency_count 1.0
+
+# HELP plugins_healthcheck_reviewdb_latency Generated from Dropwizard metric import (metric=plugins/healthcheck/reviewdb/latency, type=com.codahale.metrics.Timer)
+# TYPE plugins_healthcheck_reviewdb_latency summary
+plugins_healthcheck_reviewdb_latency{quantile="0.5",} 0.001
+plugins_healthcheck_reviewdb_latency{quantile="0.75",} 0.001
+plugins_healthcheck_reviewdb_latency{quantile="0.95",} 0.001
+plugins_healthcheck_reviewdb_latency{quantile="0.98",} 0.001
+plugins_healthcheck_reviewdb_latency{quantile="0.99",} 0.001
+plugins_healthcheck_reviewdb_latency{quantile="0.999",} 0.001
+plugins_healthcheck_reviewdb_latency_count 1.0
+
+# HELP plugins_healthcheck_jgit_failure_total Generated from Dropwizard metric import (metric=plugins/healthcheck/jgit/failure, type=com.codahale.metrics.Meter)
+# TYPE plugins_healthcheck_jgit_failure_total counter
+plugins_healthcheck_jgit_failure_total 0.0
+
+# HELP plugins_healthcheck_projectslist_failure_total Generated from Dropwizard metric import (metric=plugins/healthcheck/projectslist/failure, type=com.codahale.metrics.Meter)
+# TYPE plugins_healthcheck_projectslist_failure_total counter
+plugins_healthcheck_projectslist_failure_total 0.0
+
+# HELP plugins_healthcheck_reviewdb_failure_total Generated from Dropwizard metric import (metric=plugins/healthcheck/reviewdb/failure, type=com.codahale.metrics.Meter)
+# TYPE plugins_healthcheck_reviewdb_failure_total counter
+plugins_healthcheck_reviewdb_failure_total 0.0
+```
+
+Metrics will be exposed to prometheus by the [metrics-reporter-prometheus](https://gerrit.googlesource.com/plugins/metrics-reporter-prometheus/) plugin.
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 84ec773..1d46933 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java
@@ -18,6 +18,8 @@
 
 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.Injector;
@@ -57,6 +59,7 @@
               protected void configure() {
                 DynamicSet.bind(binder(), HealthCheck.class)
                     .toInstance(new TestHealthCheck("checkOk", HealthCheck.Result.PASSED, 1, 2));
+                DynamicSet.bind(binder(), MetricMaker.class).toInstance(new DisabledMetricMaker());
               }
             });
 
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 990b4cb..b9beb77 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
@@ -18,6 +18,7 @@
 import static org.eclipse.jgit.lib.RefUpdate.Result.NEW;
 
 import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.metrics.DisabledMetricMaker;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -27,6 +28,7 @@
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.Result;
 import com.googlesource.gerrit.plugins.healthcheck.check.JGitHealthCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.MetricsHandler;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.SortedSet;
@@ -45,6 +47,13 @@
   private AllProjectsName allProjectsName = new AllProjectsName("All-Projects");
   private InMemoryRepositoryManager inMemoryRepositoryManager = new InMemoryRepositoryManager();
   private PersonIdent personIdent = new PersonIdent("Gerrit Rietveld", "gerrit@rietveld.nl");
+  private final MetricsHandler.Factory metricsHandlerFactory =
+      new MetricsHandler.Factory() {
+        @Override
+        public MetricsHandler create(String name) {
+          return new MetricsHandler("foo", new DisabledMetricMaker());
+        }
+      };
 
   @Inject private ListeningExecutorService executor;
 
@@ -60,14 +69,16 @@
   @Test
   public void shouldBeHealthyWhenJGitIsWorking() {
     JGitHealthCheck reviewDbCheck =
-        new JGitHealthCheck(executor, getWorkingRepositoryManager(), allProjectsName);
+        new JGitHealthCheck(
+            executor, getWorkingRepositoryManager(), allProjectsName, metricsHandlerFactory);
     assertThat(reviewDbCheck.run().result).isEqualTo(Result.PASSED);
   }
 
   @Test
   public void shouldBeUnhealthyWhenJGitIsFailing() {
     JGitHealthCheck jGitHealthCheck =
-        new JGitHealthCheck(executor, getFailingGitRepositoryManager(), allProjectsName);
+        new JGitHealthCheck(
+            executor, getFailingGitRepositoryManager(), allProjectsName, metricsHandlerFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.FAILED);
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/MetricsHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/MetricsHandlerTest.java
new file mode 100644
index 0000000..aa66b1f
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/MetricsHandlerTest.java
@@ -0,0 +1,99 @@
+package com.googlesource.gerrit.plugins.healthcheck;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.metrics.Counter0;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.metrics.Timer0;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.Result;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.Status;
+import com.googlesource.gerrit.plugins.healthcheck.check.MetricsHandler;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+
+public class MetricsHandlerTest {
+
+  private class TestMetricsMaker extends DisabledMetricMaker {
+    private Long failures = 0L;
+    private Long latency = 0L;
+
+    @Override
+    public Counter0 newCounter(String name, Description desc) {
+      return new Counter0() {
+        @Override
+        public void incrementBy(long value) {
+          failures += value;
+        }
+
+        @Override
+        public void remove() {}
+      };
+    }
+
+    @Override
+    public Timer0 newTimer(String name, Description desc) {
+      return new Timer0() {
+        @Override
+        public void remove() {}
+
+        @Override
+        public void record(long value, TimeUnit unit) {
+          latency = value;
+        }
+      };
+    }
+  }
+
+  @Test
+  public void shouldSendCounterWhenStatusFailed() {
+    TestMetricsMaker metricMaker = new TestMetricsMaker();
+    MetricsHandler handler = new MetricsHandler("test", metricMaker);
+
+    handler.sendMetrics(new Status(Result.FAILED, 1L, 1L));
+
+    assertThat(metricMaker.failures).isEqualTo(1L);
+  }
+
+  @Test
+  public void shouldSendCounterWhenStatusTimeout() {
+    TestMetricsMaker metricMaker = new TestMetricsMaker();
+    MetricsHandler handler = new MetricsHandler("test", metricMaker);
+
+    handler.sendMetrics(new Status(Result.TIMEOUT, 1L, 1L));
+
+    assertThat(metricMaker.failures).isEqualTo(1L);
+  }
+
+  @Test
+  public void shouldNOTSendCounterWhenStatusSuccess() {
+    TestMetricsMaker metricMaker = new TestMetricsMaker();
+    MetricsHandler handler = new MetricsHandler("test", metricMaker);
+
+    handler.sendMetrics(new Status(Result.PASSED, 1L, 1L));
+
+    assertThat(metricMaker.failures).isEqualTo(0L);
+  }
+
+  @Test
+  public void shouldRecordLatencyWhenSuccess() {
+    TestMetricsMaker metricMaker = new TestMetricsMaker();
+    MetricsHandler handler = new MetricsHandler("test", metricMaker);
+    Long elapsed = System.currentTimeMillis();
+
+    handler.sendMetrics(new Status(Result.PASSED, 1L, elapsed));
+
+    assertThat(metricMaker.latency).isEqualTo(elapsed);
+  }
+
+  @Test
+  public void shouldRecordLatencyWhenFailure() {
+    TestMetricsMaker metricMaker = new TestMetricsMaker();
+    MetricsHandler handler = new MetricsHandler("test", metricMaker);
+    Long elapsed = System.currentTimeMillis();
+
+    handler.sendMetrics(new Status(Result.FAILED, 1L, elapsed));
+
+    assertThat(metricMaker.latency).isEqualTo(elapsed);
+  }
+}
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 5fb2925..942af0c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ProjectsListHealthCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ProjectsListHealthCheckTest.java
@@ -19,11 +19,13 @@
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.metrics.DisabledMetricMaker;
 import com.google.gerrit.server.project.ListProjects;
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.healthcheck.check.AbstractHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.Result;
+import com.googlesource.gerrit.plugins.healthcheck.check.MetricsHandler;
 import com.googlesource.gerrit.plugins.healthcheck.check.ProjectsListHealthCheck;
 import java.util.SortedMap;
 import java.util.TreeMap;
@@ -31,6 +33,13 @@
 import org.junit.Test;
 
 public class ProjectsListHealthCheckTest {
+  private final MetricsHandler.Factory metricsHandlerFactory =
+      new MetricsHandler.Factory() {
+        @Override
+        public MetricsHandler create(String name) {
+          return new MetricsHandler("foo", new DisabledMetricMaker());
+        }
+      };
   @Inject private ListeningExecutorService executor;
 
   @Before
@@ -41,14 +50,14 @@
   @Test
   public void shouldBeHealthyWhenListProjectsWorks() {
     ProjectsListHealthCheck jGitHealthCheck =
-        new ProjectsListHealthCheck(executor, getWorkingProjectList(0));
+        new ProjectsListHealthCheck(executor, getWorkingProjectList(0), metricsHandlerFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.PASSED);
   }
 
   @Test
   public void shouldBeUnhealthyWhenListProjectsIsFailing() {
     ProjectsListHealthCheck jGitHealthCheck =
-        new ProjectsListHealthCheck(executor, getFailingProjectList());
+        new ProjectsListHealthCheck(executor, getFailingProjectList(), metricsHandlerFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.FAILED);
   }
 
@@ -56,7 +65,9 @@
   public void shouldBeUnhealthyWhenListProjectsIsTimingOut() {
     ProjectsListHealthCheck jGitHealthCheck =
         new ProjectsListHealthCheck(
-            executor, getWorkingProjectList(AbstractHealthCheck.CHECK_TIMEOUT * 2));
+            executor,
+            getWorkingProjectList(AbstractHealthCheck.CHECK_TIMEOUT * 2),
+            metricsHandlerFactory);
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.TIMEOUT);
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ReviewDbHealthCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ReviewDbHealthCheckTest.java
index 1e5d712..6c5e008 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ReviewDbHealthCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ReviewDbHealthCheckTest.java
@@ -18,38 +18,50 @@
 
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.metrics.DisabledMetricMaker;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.testutil.DisabledReviewDb;
 import com.google.gerrit.testutil.InMemoryDatabase;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Guice;
 import com.google.inject.Inject;
+import com.google.inject.Injector;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.MetricsHandler;
 import com.googlesource.gerrit.plugins.healthcheck.check.ReviewDbHealthCheck;
 import org.junit.Before;
 import org.junit.Test;
 
 public class ReviewDbHealthCheckTest {
   private SchemaFactory<ReviewDb> workingReviewDbFactory;
-
+  private final MetricsHandler.Factory metricsHandlerFactory =
+      new MetricsHandler.Factory() {
+        @Override
+        public MetricsHandler create(String name) {
+          return new MetricsHandler("foo", new DisabledMetricMaker());
+        }
+      };
   @Inject private ListeningExecutorService executor;
 
   @Before
   public void setUp() throws Exception {
-    Guice.createInjector(new HealthCheckModule()).injectMembers(this);
+    Injector testInjector = Guice.createInjector(new HealthCheckModule());
+    testInjector.injectMembers(this);
+
     workingReviewDbFactory = InMemoryDatabase.newDatabase(new LifecycleManager()).create();
   }
 
   @Test
   public void shouldBeHealthyWhenReviewDbIsWorking() {
-    ReviewDbHealthCheck reviewDbCheck = new ReviewDbHealthCheck(executor, workingReviewDbFactory);
+    ReviewDbHealthCheck reviewDbCheck =
+        new ReviewDbHealthCheck(executor, workingReviewDbFactory, metricsHandlerFactory);
     assertThat(reviewDbCheck.run().result).isEqualTo(HealthCheck.Result.PASSED);
   }
 
   @Test
   public void shouldBeUnhealthyWhenReviewDbIsFailing() {
     ReviewDbHealthCheck reviewDbCheck =
-        new ReviewDbHealthCheck(executor, getFailingReviewDbProvider());
+        new ReviewDbHealthCheck(executor, getFailingReviewDbProvider(), metricsHandlerFactory);
     assertThat(reviewDbCheck.run().result).isEqualTo(HealthCheck.Result.FAILED);
   }