Record RestModifyView name in NoteDb reflog

Instead of using a generic string "Update NoteDb refs", record the
class name of the RestModifyView that caused this operation to
execute.  This makes inspection of the history of a change much
easier, as its possible to correlate events to specific sections of
the Gerrit server.

The implementation favors going from the RestApiServlet down the
stack, so that plugin handlers are caught first and logged as the
plugin's RestModifyView implementation, even if the plugin is using
an internal API call to a standard view like PutTopic.

Change-Id: Ifd6a9843b6bd5f3e7f1ff7e145fa433f237b147a
This commit is contained in:
Shawn Pearce
2017-08-10 05:53:23 -07:00
parent 7bd77e4856
commit 6f42e5d2d5
3 changed files with 111 additions and 1 deletions

View File

@@ -24,10 +24,12 @@ import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import com.google.auto.value.AutoValue;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Table;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.metrics.Timer1;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
@@ -41,6 +43,7 @@ import com.google.gerrit.server.git.InsertedObject;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.update.ChainedReceiveCommands;
import com.google.gerrit.server.update.RefUpdateUtil;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gwtorm.server.OrmConcurrencyException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -77,6 +80,12 @@ import org.eclipse.jgit.transport.ReceiveCommand;
public class NoteDbUpdateManager implements AutoCloseable {
public static final String CHANGES_READ_ONLY = "NoteDb changes are read-only";
private static final ImmutableList<String> PACKAGE_PREFIXES =
ImmutableList.of("com.google.gerrit.server.", "com.google.gerrit.");
private static final ImmutableSet<String> SERVLET_NAMES =
ImmutableSet.of(
"com.google.gerrit.httpd.restapi.RestApiServlet", RetryingRestModifyView.class.getName());
public interface Factory {
NoteDbUpdateManager create(Project.NameKey projectName);
}
@@ -539,7 +548,11 @@ public class NoteDbUpdateManager implements AutoCloseable {
BatchRefUpdate bru = or.repo.getRefDatabase().newBatchUpdate();
bru.setPushCertificate(pushCert);
bru.setRefLogMessage(firstNonNull(refLogMessage, "Update NoteDb refs"), false);
if (refLogMessage != null) {
bru.setRefLogMessage(refLogMessage, false);
} else {
bru.setRefLogMessage(firstNonNull(guessRestApiHandler(), "Update NoteDb refs"), false);
}
bru.setRefLogIdent(refLogIdent != null ? refLogIdent : serverIdent.get());
or.cmds.addTo(bru);
bru.setAllowNonFastForwards(true);
@@ -550,6 +563,45 @@ public class NoteDbUpdateManager implements AutoCloseable {
return bru;
}
private static String guessRestApiHandler() {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
int i = findRestApiServlet(trace);
if (i < 0) {
return null;
}
try {
for (i--; i >= 0; i--) {
String cn = trace[i].getClassName();
Class<?> cls = Class.forName(cn);
if (RestModifyView.class.isAssignableFrom(cls) && cls != RetryingRestModifyView.class) {
return viewName(cn);
}
}
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
private static String viewName(String cn) {
String impl = cn.replace('$', '.');
for (String p : PACKAGE_PREFIXES) {
if (impl.startsWith(p)) {
return impl.substring(p.length());
}
}
return impl;
}
private static int findRestApiServlet(StackTraceElement[] trace) {
for (int i = 0; i < trace.length; i++) {
if (SERVLET_NAMES.contains(trace[i].getClassName())) {
return i;
}
}
return -1;
}
private void addCommands() throws OrmException, IOException {
if (isEmpty()) {
return;