Merge branch 'stable-2.16'

* stable-2.16:
  Model healthcheck latency as gauge rather than timer
  Introduce the changes query healthcheck
  Config: allow per-check timeout configuration
  Send metrics when running healthcheck
  Fix ListProjects constructor in IT

Change-Id: I5efad5b0dda5af529b3c55af33128959222cde3b
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/HealthCheckConfig.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckConfig.java
new file mode 100644
index 0000000..6c93e91
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckConfig.java
@@ -0,0 +1,75 @@
+// Copyright (C) 2019 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.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+
+@Singleton
+public class HealthCheckConfig {
+  public static final String HEALTHCHECK = "healthcheck";
+  public static final HealthCheckConfig DEFAULT_CONFIG = new HealthCheckConfig(null);
+  private static final long HEALTHCHECK_TIMEOUT_DEFAULT = 500L;
+  private static final String QUERY_DEFAULT = "status:open";
+  private static final int LIMIT_DEFAULT = 10;
+
+  private final Config config;
+
+  @Inject
+  public HealthCheckConfig(PluginConfigFactory configFactory, @PluginName String pluginName) {
+    config = configFactory.getGlobalPluginConfig(pluginName);
+  }
+
+  @VisibleForTesting
+  public HealthCheckConfig(String configText) {
+    config = new Config();
+    if (!Strings.isNullOrEmpty(configText)) {
+      try {
+        config.fromText(configText);
+      } catch (ConfigInvalidException e) {
+        throw new IllegalArgumentException("Invalid configuration " + configText, e);
+      }
+    }
+  }
+
+  public long getTimeout() {
+    return getTimeout(null);
+  }
+
+  public long getTimeout(String healthCheckName) {
+    long defaultTimeout = healthCheckName == null ? HEALTHCHECK_TIMEOUT_DEFAULT : getTimeout(null);
+    return config.getTimeUnit(
+        HEALTHCHECK, healthCheckName, "timeout", defaultTimeout, TimeUnit.MILLISECONDS);
+  }
+
+  public String getQuery(String healthCheckName) {
+    String defaultQuery = healthCheckName == null ? QUERY_DEFAULT : getQuery(null);
+    return MoreObjects.firstNonNull(
+        config.getString(HEALTHCHECK, healthCheckName, "query"), defaultQuery);
+  }
+
+  public int getLimit(String healthCheckName) {
+    int defaultLimit = healthCheckName == null ? LIMIT_DEFAULT : getLimit(null);
+    return config.getInt(HEALTHCHECK, healthCheckName, "limit", defaultLimit);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetrics.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetrics.java
new file mode 100644
index 0000000..edc3223
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetrics.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2019 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.common.annotations.VisibleForTesting;
+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.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@Singleton
+public class HealthCheckMetrics implements LifecycleListener {
+
+  private final DynamicSet<HealthCheck> healthChecks;
+  private final MetricMaker metricMaker;
+  private final Set<RegistrationHandle> registeredMetrics;
+  private final Set<Runnable> triggers;
+
+  @Inject
+  public HealthCheckMetrics(DynamicSet<HealthCheck> healthChecks, MetricMaker metricMaker) {
+    this.healthChecks = healthChecks;
+    this.metricMaker = metricMaker;
+    this.registeredMetrics = Collections.synchronizedSet(new HashSet<>());
+    this.triggers = Collections.synchronizedSet(new HashSet<>());
+  }
+
+  @Override
+  public void start() {
+
+    for (HealthCheck healthCheck : healthChecks) {
+      String name = healthCheck.name();
+
+      Counter0 failureMetric =
+          metricMaker.newCounter(
+              String.format("%s/failure", name),
+              new Description(String.format("%s healthcheck failures count", name))
+                  .setCumulative()
+                  .setRate()
+                  .setUnit("failures"));
+
+      CallbackMetric0<Long> latencyMetric =
+          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));
+
+      Runnable metricCallBack =
+          () -> {
+            HealthCheck.StatusSummary status = healthCheck.getLatestStatus();
+            latencyMetric.set(healthCheck.getLatestStatus().elapsed);
+            if (status.isFailure()) {
+              failureMetric.increment();
+            }
+          };
+
+      registeredMetrics.add(failureMetric);
+      registeredMetrics.add(metricMaker.newTrigger(latencyMetric, metricCallBack));
+      triggers.add(metricCallBack);
+    }
+  }
+
+  @Override
+  public void stop() {
+    for (RegistrationHandle handle : registeredMetrics) {
+      handle.remove();
+    }
+  }
+
+  @VisibleForTesting
+  public void triggerAll() {
+    triggers.forEach(Runnable::run);
+  }
+}
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 d57a1f9..277c11f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
@@ -14,11 +14,13 @@
 
 package com.googlesource.gerrit.plugins.healthcheck;
 
+import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.inject.AbstractModule;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.JGitHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.ProjectsListHealthCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.QueryChangesHealthCheck;
 
 public class HealthCheckSubsystemsModule extends AbstractModule {
 
@@ -26,6 +28,8 @@
   protected void configure() {
     bindChecker(JGitHealthCheck.class);
     bindChecker(ProjectsListHealthCheck.class);
+    bindChecker(QueryChangesHealthCheck.class);
+    bind(LifecycleListener.class).to(HealthCheckMetrics.class);
   }
 
   private void bindChecker(Class<? extends HealthCheck> healthCheckClass) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/api/HealthCheckStatusEndpoint.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/api/HealthCheckStatusEndpoint.java
index 87a1fc8..26a2042 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/api/HealthCheckStatusEndpoint.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/api/HealthCheckStatusEndpoint.java
@@ -50,8 +50,8 @@
         .stream()
         .filter(
             res ->
-                res instanceof HealthCheck.Status
-                    && ((HealthCheck.Status) res).result != HealthCheck.Result.PASSED)
+                res instanceof HealthCheck.StatusSummary
+                    && ((HealthCheck.StatusSummary) res).result != HealthCheck.Result.PASSED)
         .findFirst()
         .isPresent()) {
       return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
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..26ef1ee 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,6 +16,7 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -24,13 +25,17 @@
 
 public abstract class AbstractHealthCheck implements HealthCheck {
   private static final Logger log = LoggerFactory.getLogger(AbstractHealthCheck.class);
-  public static final long CHECK_TIMEOUT = 500L;
+  private final long timeout;
   private final String name;
   private final ListeningExecutorService executor;
+  protected StatusSummary latestStatus;
 
-  protected AbstractHealthCheck(ListeningExecutorService executor, String name) {
+  protected AbstractHealthCheck(
+      ListeningExecutorService executor, HealthCheckConfig config, String name) {
     this.executor = executor;
     this.name = name;
+    this.timeout = config.getTimeout(name);
+    this.latestStatus = StatusSummary.INITIAL_STATUS;
   }
 
   @Override
@@ -39,9 +44,9 @@
   }
 
   @Override
-  public Status run() {
+  public StatusSummary run() {
     final long ts = System.currentTimeMillis();
-    ListenableFuture<Status> resultFuture =
+    ListenableFuture<StatusSummary> resultFuture =
         executor.submit(
             () -> {
               Result healthy;
@@ -51,19 +56,24 @@
                 log.warn("Check {} failed", name, e);
                 healthy = Result.FAILED;
               }
-              return new Status(healthy, ts, System.currentTimeMillis() - ts);
+              return new StatusSummary(healthy, ts, System.currentTimeMillis() - ts);
             });
-
     try {
-      return resultFuture.get(CHECK_TIMEOUT, TimeUnit.MILLISECONDS);
+      latestStatus = resultFuture.get(timeout, TimeUnit.MILLISECONDS);
     } catch (TimeoutException e) {
       log.warn("Check {} timed out", name, e);
-      return new Status(Result.TIMEOUT, ts, System.currentTimeMillis() - ts);
+      latestStatus = new StatusSummary(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);
+      latestStatus = new StatusSummary(Result.FAILED, ts, System.currentTimeMillis() - ts);
     }
+    return latestStatus;
   }
 
   protected abstract Result doCheck() throws Exception;
+
+  @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 355b0cf..4f6a532 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
@@ -27,19 +27,27 @@
     TIMEOUT;
   }
 
-  public class Status {
+  public class StatusSummary {
+    public static final StatusSummary INITIAL_STATUS =
+        new StatusSummary(Result.PASSED, System.currentTimeMillis(), 0L);
     public final Result result;
     public final long ts;
     public final long elapsed;
 
-    public Status(Result result, long ts, long elapsed) {
+    public StatusSummary(Result result, long ts, long elapsed) {
       this.result = result;
       this.ts = ts;
       this.elapsed = elapsed;
     }
+
+    public Boolean isFailure() {
+      return this.result != Result.PASSED;
+    }
   }
 
-  Status run();
+  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 9a87490..670e1b4 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
@@ -17,4 +17,5 @@
 public interface HealthCheckNames {
   String JGIT = "jgit";
   String PROJECTSLIST = "projectslist";
+  String QUERYCHANGES = "querychanges";
 }
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..73fa88d 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
@@ -20,9 +20,12 @@
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
 
+@Singleton
 public class JGitHealthCheck extends AbstractHealthCheck {
   private final GitRepositoryManager repositoryManager;
   private final AllProjectsName allProjectsName;
@@ -30,9 +33,11 @@
   @Inject
   public JGitHealthCheck(
       ListeningExecutorService executor,
+      HealthCheckConfig config,
       GitRepositoryManager repositoryManager,
       AllProjectsName allProjectsName) {
-    super(executor, JGIT);
+    super(executor, config, JGIT);
+
     this.repositoryManager = repositoryManager;
     this.allProjectsName = allProjectsName;
   }
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 a7552f9..4da142d 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
@@ -20,18 +20,23 @@
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.server.restapi.project.ListProjects;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
 import java.util.SortedMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class ProjectsListHealthCheck extends AbstractHealthCheck {
   private static final Logger log = LoggerFactory.getLogger(ProjectsListHealthCheck.class);
   private static final int PROJECTS_LIST_LIMIT = 100;
   private final ListProjects listProjects;
 
   @Inject
-  public ProjectsListHealthCheck(ListeningExecutorService executor, ListProjects listProjects) {
-    super(executor, PROJECTSLIST);
+  public ProjectsListHealthCheck(
+      ListeningExecutorService executor, HealthCheckConfig config, ListProjects listProjects) {
+    super(executor, config, PROJECTSLIST);
+
     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
new file mode 100644
index 0000000..2f5d115
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/QueryChangesHealthCheck.java
@@ -0,0 +1,72 @@
+// Copyright (C) 2019 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.check;
+
+import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.QUERYCHANGES;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.server.restapi.change.QueryChanges;
+import com.google.gerrit.server.util.ManualRequestContext;
+import com.google.gerrit.server.util.OneOffRequestContext;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class QueryChangesHealthCheck extends AbstractHealthCheck {
+  private static final Logger log = LoggerFactory.getLogger(QueryChangesHealthCheck.class);
+  private final QueryChanges queryChanges;
+  private final int limit;
+  private final OneOffRequestContext oneOffCtx;
+
+  @Inject
+  public QueryChangesHealthCheck(
+      ListeningExecutorService executor,
+      HealthCheckConfig config,
+      QueryChanges queryChanges,
+      OneOffRequestContext oneOffCtx) {
+    super(executor, config, QUERYCHANGES);
+    this.queryChanges = queryChanges;
+    this.limit = config.getLimit(QUERYCHANGES);
+    queryChanges.setLimit(limit);
+    queryChanges.addQuery(config.getQuery(QUERYCHANGES));
+    queryChanges.setStart(0);
+    this.oneOffCtx = oneOffCtx;
+  }
+
+  @Override
+  protected Result doCheck() throws Exception {
+    try (ManualRequestContext ctx = oneOffCtx.open()) {
+      List<?> changes = queryChanges.apply(null);
+      if (changes == null) {
+        log.warn("Cannot query changes: received a null list of results");
+        return Result.FAILED;
+      }
+
+      if (changes.size() < limit) {
+        log.warn(
+            "Query changes did not return enough items: expected {} items but got only {}",
+            limit,
+            changes.size());
+        return Result.FAILED;
+      }
+
+      return Result.PASSED;
+    }
+  }
+}
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
new file mode 100644
index 0000000..b2f798d
--- /dev/null
+++ b/src/main/resources/Documentation/about.md
@@ -0,0 +1,3 @@
+This plugin provides support for monitoring and alerting purposes.
+A common use-case is the automatic detection and recovery of issues
+for Gerrit Servers that need to be available on a 24x7 basis.
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
new file mode 100644
index 0000000..74ad62c
--- /dev/null
+++ b/src/main/resources/Documentation/config.md
@@ -0,0 +1,66 @@
+@PLUGIN@ configuration
+======================
+
+The @PLUGIN@
+------------
+
+The plugin does not require any configuration at all and exposes an HTTP
+endpoint for querying the service status.
+
+```
+GET /config/server/healthcheck~status
+
+)]}'
+{
+  "ts": 139402910202,
+  "elapsed": 100,
+  "reviewdb": {
+    "ts": 139402910202,
+    "elapsed": 50,
+    "result": "passed"
+  },
+  "projectslist": {
+    "ts": 139402910202,
+    "elapsed": 100,
+    "result": "passed"
+  },
+  "auth": {
+    "ts": 139402910202,
+    "elapsed": 80,
+    "result": "passed"
+  }
+}
+```
+
+Settings
+--------
+
+The plugin allows to customize its behaviour through a specific
+`healthcheck.config` file in the `$GERRIT_SITE/etc` directory.
+
+Each section of the form `[healthcheck "<checkName>"]` can tailor the
+behaviour of an individual `<checkName>`. The section `[healthcheck]`
+defines the global defaults valid for all checks.
+
+The following check names are available:
+
+- `reviewdb` : check connectivity and ability to query ReviewDb
+- `jgit` : check connectivity to the filesystem and ability to open a JGit ref and object
+- `projectslist` : check the ability to list projects with their descriptions
+
+The follwing parameters are available:
+
+- `healthcheck.<checkName>.timeout` : Specific timeout (msec) for the
+  healthcheck to complete. Zero means that there is no timeout.
+
+  Default: 500
+
+- `healthcheck.<checkName>.query` : Query to be executed for extracting
+   elements from the check.
+
+  Default: status:open
+
+- `healthcheck.<checkName>.limit` : Maximum number of elements to retrieve from
+  the the check results.
+
+  Default: 10
\ No newline at end of file
diff --git a/src/resources/Documentation/config.md b/src/resources/Documentation/config.md
new file mode 100644
index 0000000..ea51c10
--- /dev/null
+++ b/src/resources/Documentation/config.md
@@ -0,0 +1,68 @@
+## 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>_latest_measured_latency: the latency of this component in the latest executed healthcheck run
+* plugins_healthcheck_jgit_failure_total: the cumulative number of failures for this specific component
+
+```
+# HELP plugins_healthcheck_jgit_latest_measured_latency Generated from Dropwizard metric import (metric=plugins/healthcheck/jgit/latency, type=com.google.gerrit.metrics.dropwizard.CallbackMetricImpl0$1)
+# TYPE plugins_healthcheck_jgit_latest_measured_latency gauge
+plugins_healthcheck_jgit_latest_measured_latency 4.0
+
+# HELP plugins_healthcheck_projectslist_latest_measured_latency Generated from Dropwizard metric import (metric=plugins/healthcheck/projectslist/latency, type=com.google.gerrit.metrics.dropwizard.CallbackMetricImpl0$1)
+# TYPE plugins_healthcheck_projectslist_latest_measured_latency gauge
+plugins_healthcheck_projectslist_latest_measured_latency 5.0
+
+# HELP plugins_healthcheck_reviewdb_latest_measured_latency Generated from Dropwizard metric import (metric=plugins/healthcheck/reviewdb/latency, type=com.google.gerrit.metrics.dropwizard.CallbackMetricImpl0$1)
+# TYPE plugins_healthcheck_reviewdb_latest_measured_latency gauge
+plugins_healthcheck_reviewdb_latest_measured_latency 3.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 3.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/HealthCheckConfigTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckConfigTest.java
new file mode 100644
index 0000000..fc215ff
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckConfigTest.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2019 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 static com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig.DEFAULT_CONFIG;
+
+import org.junit.Test;
+
+public class HealthCheckConfigTest {
+
+  @Test
+  public void shouldHaveDefaultTimeout() {
+    long defaultTimeout = DEFAULT_CONFIG.getTimeout(null);
+    assertThat(defaultTimeout).isGreaterThan(0L);
+    assertThat(DEFAULT_CONFIG.getTimeout("fooCheck")).isEqualTo(defaultTimeout);
+  }
+
+  @Test
+  public void shouldHaveGlobalTimeout() {
+    HealthCheckConfig config = new HealthCheckConfig("[healthcheck]\n" + "timeout=1000");
+
+    assertThat(config.getTimeout(null)).isEqualTo(1000);
+    assertThat(config.getTimeout("barCheck")).isEqualTo(1000);
+  }
+
+  @Test
+  public void shouldHaveCheckOverriddenTimeout() {
+    HealthCheckConfig config =
+        new HealthCheckConfig(
+            "[healthcheck]\n" + "timeout=2000\n" + "[healthcheck \"fooCheck\"]\n" + "timeout=1000");
+
+    assertThat(config.getTimeout("fooCheck")).isEqualTo(1000);
+    assertThat(config.getTimeout("barCheck")).isEqualTo(2000);
+  }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
index ec338b6..3f70903 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
@@ -17,20 +17,48 @@
 import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
 import static com.google.common.truth.Truth.assertThat;
 import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.JGIT;
+import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.QUERYCHANGES;
 
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.Sandboxed;
 import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gson.Gson;
 import com.google.gson.JsonObject;
+import com.google.inject.AbstractModule;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames;
 import java.io.IOException;
+import org.junit.Before;
 import org.junit.Test;
 
 @TestPlugin(name = "healthcheck", sysModule = "com.googlesource.gerrit.plugins.healthcheck.Module")
 @Sandboxed
 public class HealthCheckIT extends LightweightPluginDaemonTest {
   Gson gson = new Gson();
+  HealthCheckConfig config;
+
+  @Override
+  @Before
+  public void setUpTestPlugin() throws Exception {
+    super.setUpTestPlugin();
+
+    config =
+        server
+            .getTestInjector()
+            .createChildInjector(
+                new AbstractModule() {
+                  @Override
+                  protected void configure() {
+                    bind(String.class).annotatedWith(PluginName.class).toInstance("healthcheck");
+                  }
+                })
+            .getInstance(HealthCheckConfig.class);
+    int numChanges = config.getLimit(HealthCheckNames.QUERYCHANGES);
+    for (int i = 0; i < numChanges; i++) {
+      createChange("refs/for/master");
+    }
+  }
 
   @Test
   public void shouldReturnOkWhenHealthy() throws Exception {
@@ -52,6 +80,17 @@
     assertCheckResult(respPayload, JGIT, "passed");
   }
 
+  @Test
+  public void shouldReturnQueryChangesCheck() throws Exception {
+    createChange("refs/for/master");
+    RestResponse resp = getHealthCheckStatus();
+    resp.assertOK();
+
+    JsonObject respPayload = gson.fromJson(resp.getReader(), JsonObject.class);
+
+    assertCheckResult(respPayload, QUERYCHANGES, "passed");
+  }
+
   private RestResponse getHealthCheckStatus() throws IOException {
     return adminRestSession.get("/config/server/healthcheck~status");
   }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetricsTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetricsTest.java
new file mode 100644
index 0000000..9955a33
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckMetricsTest.java
@@ -0,0 +1,164 @@
+// Copyright (C) 2019 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.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 org.junit.Before;
+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 shouldSendCounterWhenStatusSummaryFailed() {
+    Long elapsed = 100L;
+    setWithStatusSummary(new StatusSummary(Result.FAILED, 1L, elapsed));
+
+    assertThat(testMetrics.getFailures()).isEqualTo(1);
+    assertThat(testMetrics.getLatency()).isEqualTo(elapsed);
+  }
+
+  @Test
+  public void shouldSendCounterWhenStatusSummaryTimeout() {
+    Long elapsed = 100L;
+    setWithStatusSummary(new StatusSummary(Result.TIMEOUT, 1L, elapsed));
+
+    assertThat(testMetrics.getFailures()).isEqualTo(1);
+    assertThat(testMetrics.getLatency()).isEqualTo(elapsed);
+  }
+
+  @Test
+  public void shouldNOTSendCounterWhenStatusSummarySuccess() {
+    Long elapsed = 100L;
+    setWithStatusSummary(new StatusSummary(Result.PASSED, 1L, elapsed));
+
+    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;
+    }
+
+    @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 <V> CallbackMetric0<V> newCallbackMetric(
+        String name, Class<V> valueClass, Description desc) {
+      return new CallbackMetric0<V>() {
+        @Override
+        public void set(V value) {
+          latency = (Long) value;
+        }
+
+        @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 84ec773..78b89e4 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java
@@ -15,36 +15,48 @@
 package com.googlesource.gerrit.plugins.healthcheck;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig.DEFAULT_CONFIG;
 
+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.Injector;
 import com.googlesource.gerrit.plugins.healthcheck.api.HealthCheckStatusEndpoint;
+import com.googlesource.gerrit.plugins.healthcheck.check.AbstractHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
+import java.util.concurrent.Executors;
 import javax.servlet.http.HttpServletResponse;
 import org.junit.Test;
 
 public class HealthCheckStatusEndpointTest {
 
-  public static class TestHealthCheck implements HealthCheck {
-    private final HealthCheck.Status checkResult;
-    private final String checkName;
+  public static class TestHealthCheck extends AbstractHealthCheck {
+    private final HealthCheck.Result checkResult;
+    private final long sleep;
 
-    public TestHealthCheck(String checkName, HealthCheck.Result result, long ts, long elapsed) {
-      this.checkName = checkName;
-      this.checkResult = new Status(result, ts, elapsed);
+    public TestHealthCheck(
+        HealthCheckConfig config, String checkName, HealthCheck.Result result, long sleep) {
+      super(MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)), config, checkName);
+      this.checkResult = result;
+      this.sleep = sleep;
     }
 
     @Override
-    public Status run() {
+    public Result doCheck() {
+      try {
+        Thread.sleep(sleep);
+      } catch (InterruptedException e) {
+      }
       return checkResult;
     }
 
     @Override
-    public String name() {
-      return checkName;
+    public StatusSummary getLatestStatus() {
+      return this.latestStatus;
     }
   }
 
@@ -56,7 +68,11 @@
               @Override
               protected void configure() {
                 DynamicSet.bind(binder(), HealthCheck.class)
-                    .toInstance(new TestHealthCheck("checkOk", HealthCheck.Result.PASSED, 1, 2));
+                    .toInstance(
+                        new TestHealthCheck(
+                            DEFAULT_CONFIG, "checkOk", HealthCheck.Result.PASSED, 0));
+                bind(HealthCheckConfig.class).toInstance(DEFAULT_CONFIG);
+                DynamicSet.bind(binder(), MetricMaker.class).toInstance(new DisabledMetricMaker());
               }
             });
 
@@ -68,6 +84,29 @@
   }
 
   @Test
+  public void shouldReturnServerErrorWhenOneChecksTimesOut() throws Exception {
+    Injector injector =
+        testInjector(
+            new AbstractModule() {
+              @Override
+              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));
+                bind(HealthCheckConfig.class).toInstance(config);
+              }
+            });
+
+    HealthCheckStatusEndpoint healthCheckApi =
+        injector.getInstance(HealthCheckStatusEndpoint.class);
+    Response<?> resp = (Response<?>) healthCheckApi.apply(null);
+
+    assertThat(resp.statusCode()).isEqualTo(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+  }
+
+  @Test
   public void shouldReturnServerErrorWhenAtLeastOneCheckIsFailing() throws Exception {
     Injector injector =
         testInjector(
@@ -75,9 +114,13 @@
               @Override
               protected void configure() {
                 DynamicSet.bind(binder(), HealthCheck.class)
-                    .toInstance(new TestHealthCheck("checkOk", HealthCheck.Result.PASSED, 1, 2));
+                    .toInstance(
+                        new TestHealthCheck(
+                            DEFAULT_CONFIG, "checkOk", HealthCheck.Result.PASSED, 0));
                 DynamicSet.bind(binder(), HealthCheck.class)
-                    .toInstance(new TestHealthCheck("checkKo", HealthCheck.Result.FAILED, 1, 2));
+                    .toInstance(
+                        new TestHealthCheck(
+                            DEFAULT_CONFIG, "checkKo", HealthCheck.Result.FAILED, 0));
               }
             });
 
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 1991871..50a748f 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.healthcheck;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig.DEFAULT_CONFIG;
 import static org.eclipse.jgit.lib.RefUpdate.Result.NEW;
 
 import com.google.common.util.concurrent.ListeningExecutorService;
@@ -60,14 +61,16 @@
   @Test
   public void shouldBeHealthyWhenJGitIsWorking() {
     JGitHealthCheck reviewDbCheck =
-        new JGitHealthCheck(executor, getWorkingRepositoryManager(), allProjectsName);
+        new JGitHealthCheck(
+            executor, DEFAULT_CONFIG, getWorkingRepositoryManager(), allProjectsName);
     assertThat(reviewDbCheck.run().result).isEqualTo(Result.PASSED);
   }
 
   @Test
   public void shouldBeUnhealthyWhenJGitIsFailing() {
     JGitHealthCheck jGitHealthCheck =
-        new JGitHealthCheck(executor, getFailingGitRepositoryManager(), allProjectsName);
+        new JGitHealthCheck(
+            executor, DEFAULT_CONFIG, getFailingGitRepositoryManager(), allProjectsName);
     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 93d1350..eefce0a 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ProjectsListHealthCheckTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ProjectsListHealthCheckTest.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.healthcheck;
 
 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.gerrit.extensions.common.ProjectInfo;
@@ -22,7 +23,6 @@
 import com.google.gerrit.server.restapi.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.ProjectsListHealthCheck;
 import java.util.SortedMap;
@@ -34,21 +34,21 @@
   @Inject private ListeningExecutorService executor;
 
   @Before
-  public void setUp() {
+  public void setUp() throws Exception {
     Guice.createInjector(new HealthCheckModule()).injectMembers(this);
   }
 
   @Test
   public void shouldBeHealthyWhenListProjectsWorks() {
     ProjectsListHealthCheck jGitHealthCheck =
-        new ProjectsListHealthCheck(executor, getWorkingProjectList(0));
+        new ProjectsListHealthCheck(executor, DEFAULT_CONFIG, getWorkingProjectList(0));
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.PASSED);
   }
 
   @Test
   public void shouldBeUnhealthyWhenListProjectsIsFailing() {
     ProjectsListHealthCheck jGitHealthCheck =
-        new ProjectsListHealthCheck(executor, getFailingProjectList());
+        new ProjectsListHealthCheck(executor, DEFAULT_CONFIG, getFailingProjectList());
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.FAILED);
   }
 
@@ -56,7 +56,7 @@
   public void shouldBeUnhealthyWhenListProjectsIsTimingOut() {
     ProjectsListHealthCheck jGitHealthCheck =
         new ProjectsListHealthCheck(
-            executor, getWorkingProjectList(AbstractHealthCheck.CHECK_TIMEOUT * 2));
+            executor, DEFAULT_CONFIG, getWorkingProjectList(DEFAULT_CONFIG.getTimeout() * 2));
     assertThat(jGitHealthCheck.run().result).isEqualTo(Result.TIMEOUT);
   }