ReceiveCommits: Call ExceptionHook to get status for exceptions

Not all exceptions that we catch in ReceiveCommits#processCommands
represent server errors. Call the ExceptionHooks to get the status for
the exception so that we can record the correct status in the reject
count metric.

If an exception doesn't represent a server error, log it only on INFO
severity.

Release-Notes: skip
Bug: Google b/151127672
Change-Id: Ib51147ea17e4340297541e59f0decfb424febf5b
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 713e5a2..fab7ee3 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -234,6 +234,7 @@
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.logging.Level;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
@@ -762,8 +763,11 @@
       logger.atFine().log("Processing commands done.");
     } catch (RuntimeException e) {
       String formattedCause = getFormattedCause(e).orElse(e.getClass().getSimpleName());
-      logger.atSevere().withCause(e).log("ReceiveCommits failed due to %s", formattedCause);
-      metrics.rejectCount.increment("n/a", formattedCause, SC_INTERNAL_SERVER_ERROR);
+      int statusCode =
+          getStatus(e).map(ExceptionHook.Status::statusCode).orElse(SC_INTERNAL_SERVER_ERROR);
+      logger.at(statusCode < SC_INTERNAL_SERVER_ERROR ? Level.INFO : Level.SEVERE).withCause(e).log(
+          "ReceiveCommits failed due to %s", formattedCause);
+      metrics.rejectCount.increment("n/a", formattedCause, statusCode);
       throw e;
     }
     progress.end();
@@ -778,6 +782,14 @@
         .findFirst();
   }
 
+  private Optional<ExceptionHook.Status> getStatus(Throwable err) {
+    return exceptionHooks.stream()
+        .map(h -> h.getStatus(err))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
+        .findFirst();
+  }
+
   // Process as many commands as possible, but may leave some commands in state NOT_ATTEMPTED.
   private void processCommandsUnsafe(
       Collection<ReceiveCommand> commands, MultiProgressMonitor progress) {