Merge "Upgrade JGit to 5.0.3.201809091024-r"
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index 12f88d5..dc293cd 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -39,7 +39,8 @@
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.IndexUtils;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import java.io.IOException;
@@ -55,6 +56,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -100,7 +102,7 @@
private final ReferenceManager<IndexSearcher> searcherManager;
private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread;
private final Set<NrtFuture> notDoneNrtFutures;
- private ScheduledThreadPoolExecutor autoCommitExecutor;
+ private ScheduledExecutorService autoCommitExecutor;
AbstractLuceneIndex(
Schema<V> schema,
@@ -129,13 +131,13 @@
delegateWriter = autoCommitWriter;
autoCommitExecutor =
- new ScheduledThreadPoolExecutor(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat(index + " Commit-%d")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ new ScheduledThreadPoolExecutor(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat(index + " Commit-%d")
+ .setDaemon(true)
+ .build()));
@SuppressWarnings("unused") // Error handling within Runnable.
Future<?> possiblyIgnoredError =
autoCommitExecutor.scheduleAtFixedRate(
@@ -170,13 +172,13 @@
writerThread =
MoreExecutors.listeningDecorator(
- Executors.newFixedThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat(index + " Write-%d")
- .setDaemon(true)
- .build()));
+ new LoggingContextAwareExecutorService(
+ Executors.newFixedThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat(index + " Write-%d")
+ .setDaemon(true)
+ .build())));
reopenThread =
new ControlledRealTimeReopenThread<>(
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
index a7824ea..af1228d 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -28,7 +28,8 @@
import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -75,20 +76,17 @@
if (cacheDir != null) {
executor =
- Executors.newFixedThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("DiskCache-Store-%d")
- .build());
+ new LoggingContextAwareExecutorService(
+ Executors.newFixedThreadPool(
+ 1, new ThreadFactoryBuilder().setNameFormat("DiskCache-Store-%d").build()));
cleanup =
- Executors.newScheduledThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("DiskCache-Prune-%d")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ Executors.newScheduledThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("DiskCache-Prune-%d")
+ .setDaemon(true)
+ .build()));
} else {
executor = null;
cleanup = null;
diff --git a/java/com/google/gerrit/server/config/SysExecutorModule.java b/java/com/google/gerrit/server/config/SysExecutorModule.java
index 2e97a58..f552434 100644
--- a/java/com/google/gerrit/server/config/SysExecutorModule.java
+++ b/java/com/google/gerrit/server/config/SysExecutorModule.java
@@ -19,7 +19,7 @@
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
@@ -83,18 +83,18 @@
return MoreExecutors.newDirectExecutorService();
}
return MoreExecutors.listeningDecorator(
- MoreExecutors.getExitingExecutorService(
- new ThreadPoolExecutor(
- 1,
- poolSize,
- 10,
- TimeUnit.MINUTES,
- new ArrayBlockingQueue<Runnable>(poolSize),
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ChangeUpdate-%d")
- .setDaemon(true)
- .build(),
- new ThreadPoolExecutor.CallerRunsPolicy())));
+ new LoggingContextAwareExecutorService(
+ MoreExecutors.getExitingExecutorService(
+ new ThreadPoolExecutor(
+ 1,
+ poolSize,
+ 10,
+ TimeUnit.MINUTES,
+ new ArrayBlockingQueue<Runnable>(poolSize),
+ new ThreadFactoryBuilder()
+ .setNameFormat("ChangeUpdate-%d")
+ .setDaemon(true)
+ .build(),
+ new ThreadPoolExecutor.CallerRunsPolicy()))));
}
}
diff --git a/java/com/google/gerrit/server/git/WorkQueue.java b/java/com/google/gerrit/server/git/WorkQueue.java
index a2c12df..a7336f0 100644
--- a/java/com/google/gerrit/server/git/WorkQueue.java
+++ b/java/com/google/gerrit/server/git/WorkQueue.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.git;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.base.CaseFormat;
import com.google.common.base.Supplier;
import com.google.common.flogger.FluentLogger;
@@ -24,7 +26,8 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.ScheduleConfig.Schedule;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.LoggingContextAwareRunnable;
import com.google.gerrit.server.util.IdGenerator;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -43,6 +46,7 @@
import java.util.concurrent.Future;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
@@ -166,12 +170,11 @@
if (threadPriority != Thread.NORM_PRIORITY) {
ThreadFactory parent = executor.getThreadFactory();
executor.setThreadFactory(
- new LoggingContextAwareThreadFactory(
- task -> {
- Thread t = parent.newThread(task);
- t.setPriority(threadPriority);
- return t;
- }));
+ task -> {
+ Thread t = parent.newThread(task);
+ t.setPriority(threadPriority);
+ return t;
+ });
}
return executor;
@@ -253,19 +256,18 @@
Executor(int corePoolSize, final String queueName) {
super(
corePoolSize,
- new LoggingContextAwareThreadFactory(
- new ThreadFactory() {
- private final ThreadFactory parent = Executors.defaultThreadFactory();
- private final AtomicInteger tid = new AtomicInteger(1);
+ new ThreadFactory() {
+ private final ThreadFactory parent = Executors.defaultThreadFactory();
+ private final AtomicInteger tid = new AtomicInteger(1);
- @Override
- public Thread newThread(Runnable task) {
- final Thread t = parent.newThread(task);
- t.setName(queueName + "-" + tid.getAndIncrement());
- t.setUncaughtExceptionHandler(LOG_UNCAUGHT_EXCEPTION);
- return t;
- }
- }));
+ @Override
+ public Thread newThread(Runnable task) {
+ final Thread t = parent.newThread(task);
+ t.setName(queueName + "-" + tid.getAndIncrement());
+ t.setUncaughtExceptionHandler(LOG_UNCAUGHT_EXCEPTION);
+ return t;
+ }
+ });
all =
new ConcurrentHashMap<>( //
@@ -277,6 +279,75 @@
}
@Override
+ public void execute(Runnable command) {
+ super.execute(LoggingContext.copy(command));
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return super.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return super.submit(LoggingContext.copy(task), result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return super.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return super.invokeAll(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(
+ Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return super.invokeAll(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return super.invokeAny(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return super.invokeAny(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ return super.schedule(LoggingContext.copy(command), delay, unit);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return super.schedule(LoggingContext.copy(callable), delay, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(
+ Runnable command, long initialDelay, long period, TimeUnit unit) {
+ return super.scheduleAtFixedRate(LoggingContext.copy(command), initialDelay, period, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(
+ Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ return super.scheduleWithFixedDelay(LoggingContext.copy(command), initialDelay, delay, unit);
+ }
+
+ @Override
protected void terminated() {
super.terminated();
queues.remove(this);
@@ -370,6 +441,10 @@
Task<V> task;
+ if (runnable instanceof LoggingContextAwareRunnable) {
+ runnable = ((LoggingContextAwareRunnable) runnable).unwrap();
+ }
+
if (runnable instanceof ProjectRunnable) {
task = new ProjectTask<>((ProjectRunnable) runnable, r, this, id);
} else {
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
index b8ececb..27f6caa 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
@@ -23,6 +23,8 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -85,9 +87,17 @@
for (Index<Account.Id, AccountState> i : getWriteIndexes()) {
// Evict the cache to get an up-to-date value for sure.
if (accountState.isPresent()) {
- i.replace(accountState.get());
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Replacing account %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ i.replace(accountState.get());
+ }
} else {
- i.delete(id);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Deleteing account %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ i.delete(id);
+ }
}
}
fireAccountIndexedEvent(id.get());
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index 8573862..113d340 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -33,6 +33,8 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.IndexExecutor;
import com.google.gerrit.server.index.IndexUtils;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -214,7 +216,12 @@
private void indexImpl(ChangeData cd) throws IOException {
logger.atInfo().log("Replace change %d in index.", cd.getId().get());
for (Index<?, ChangeData> i : getWriteIndexes()) {
- i.replace(cd);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Replacing change %d in index version %d",
+ cd.getId().get(), i.getSchema().getVersion())) {
+ i.replace(cd);
+ }
}
fireChangeIndexedEvent(cd.project().get(), cd.getId().get());
}
@@ -417,7 +424,11 @@
// Implementations should not need to access the DB in order to delete a
// change ID.
for (ChangeIndex i : getWriteIndexes()) {
- i.delete(id);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Deleteing change %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ i.delete(id);
+ }
}
fireChangeDeletedFromIndexEvent(id.get());
return null;
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
index 3f4c7be..28e441b 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
@@ -23,6 +23,8 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -85,9 +87,17 @@
for (Index<AccountGroup.UUID, InternalGroup> i : getWriteIndexes()) {
if (internalGroup.isPresent()) {
- i.replace(internalGroup.get());
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Replacing group %s in index version %d", uuid.get(), i.getSchema().getVersion())) {
+ i.replace(internalGroup.get());
+ }
} else {
- i.delete(uuid);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Deleting group %s in index version %d", uuid.get(), i.getSchema().getVersion())) {
+ i.delete(uuid);
+ }
}
}
fireGroupIndexedEvent(uuid.get());
diff --git a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
index f5bf8b5..1ec5d55 100644
--- a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
@@ -24,6 +24,8 @@
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.assistedinject.Assisted;
@@ -75,13 +77,23 @@
logger.atInfo().log("Replace project %s in index", nameKey.get());
ProjectData projectData = projectState.toProjectData();
for (ProjectIndex i : getWriteIndexes()) {
- i.replace(projectData);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Replacing project %s in index version %d",
+ nameKey.get(), i.getSchema().getVersion())) {
+ i.replace(projectData);
+ }
}
fireProjectIndexedEvent(nameKey.get());
} else {
logger.atInfo().log("Delete project %s from index", nameKey.get());
for (ProjectIndex i : getWriteIndexes()) {
- i.delete(nameKey);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Deleting project %s in index version %d",
+ nameKey.get(), i.getSchema().getVersion())) {
+ i.delete(nameKey);
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/logging/LoggingContext.java b/java/com/google/gerrit/server/logging/LoggingContext.java
index 2ce4c93..1e81c29 100644
--- a/java/com/google/gerrit/server/logging/LoggingContext.java
+++ b/java/com/google/gerrit/server/logging/LoggingContext.java
@@ -16,6 +16,7 @@
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.backend.Tags;
+import java.util.concurrent.Callable;
import java.util.logging.Level;
/**
@@ -42,6 +43,20 @@
return INSTANCE;
}
+ public static Runnable copy(Runnable runnable) {
+ if (runnable instanceof LoggingContextAwareRunnable) {
+ return runnable;
+ }
+ return new LoggingContextAwareRunnable(runnable);
+ }
+
+ public static <T> Callable<T> copy(Callable<T> callable) {
+ if (callable instanceof LoggingContextAwareCallable) {
+ return callable;
+ }
+ return new LoggingContextAwareCallable<>(callable);
+ }
+
@Override
public boolean shouldForceLogging(String loggerName, Level level, boolean isEnabled) {
return isLoggingForced();
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java b/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
new file mode 100644
index 0000000..6aff5c4
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2018 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.google.gerrit.server.logging;
+
+import com.google.common.collect.ImmutableSetMultimap;
+import java.util.concurrent.Callable;
+
+/**
+ * Wrapper for a {@link Callable} that copies the {@link LoggingContext} from the current thread to
+ * the thread that executes the callable.
+ *
+ * <p>The state of the logging context that is copied to the thread that executes the callable is
+ * fixed at the creation time of this wrapper. If the callable is submitted to an executor and is
+ * executed later this means that changes that are done to the logging context in between creating
+ * and executing the callable do not apply.
+ *
+ * <p>See {@link LoggingContextAwareRunnable} for an example.
+ *
+ * @see LoggingContextAwareRunnable
+ */
+class LoggingContextAwareCallable<T> implements Callable<T> {
+ private final Callable<T> callable;
+ private final Thread callingThread;
+ private final ImmutableSetMultimap<String, String> tags;
+ private final boolean forceLogging;
+
+ LoggingContextAwareCallable(Callable<T> callable) {
+ this.callable = callable;
+ this.callingThread = Thread.currentThread();
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
+ this.forceLogging = LoggingContext.getInstance().isLoggingForced();
+ }
+
+ @Override
+ public T call() throws Exception {
+ if (callingThread.equals(Thread.currentThread())) {
+ // propagation of logging context is not needed
+ return callable.call();
+ }
+
+ // propagate logging context
+ LoggingContext loggingCtx = LoggingContext.getInstance();
+ ImmutableSetMultimap<String, String> oldTags = loggingCtx.getTagsAsMap();
+ boolean oldForceLogging = loggingCtx.isLoggingForced();
+ loggingCtx.setTags(tags);
+ loggingCtx.forceLogging(forceLogging);
+ try {
+ return callable.call();
+ } finally {
+ loggingCtx.setTags(oldTags);
+ loggingCtx.forceLogging(oldForceLogging);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java b/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java
new file mode 100644
index 0000000..17e152e3
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java
@@ -0,0 +1,110 @@
+// Copyright (C) 2018 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.google.gerrit.server.logging;
+
+import static java.util.stream.Collectors.toList;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * An {@link ExecutorService} that copies the {@link LoggingContext} on executing a {@link Runnable}
+ * to the executing thread.
+ */
+public class LoggingContextAwareExecutorService implements ExecutorService {
+ private final ExecutorService executorService;
+
+ public LoggingContextAwareExecutorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ executorService.execute(LoggingContext.copy(command));
+ }
+
+ @Override
+ public void shutdown() {
+ executorService.shutdown();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ return executorService.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return executorService.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return executorService.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return executorService.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return executorService.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return executorService.submit(LoggingContext.copy(task), result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return executorService.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return executorService.invokeAll(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(
+ Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return executorService.invokeAll(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return executorService.invokeAny(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return executorService.invokeAny(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java b/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
new file mode 100644
index 0000000..0bd7d00
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
@@ -0,0 +1,89 @@
+// Copyright (C) 2018 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.google.gerrit.server.logging;
+
+import com.google.common.collect.ImmutableSetMultimap;
+
+/**
+ * Wrapper for a {@link Runnable} that copies the {@link LoggingContext} from the current thread to
+ * the thread that executes the runnable.
+ *
+ * <p>The state of the logging context that is copied to the thread that executes the runnable is
+ * fixed at the creation time of this wrapper. If the runnable is submitted to an executor and is
+ * executed later this means that changes that are done to the logging context in between creating
+ * and executing the runnable do not apply.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * try (TraceContext traceContext = TraceContext.newTrace(true, ...)) {
+ * executor
+ * .submit(new LoggingContextAwareRunnable(
+ * () -> {
+ * // Tracing is enabled since the runnable is created within the TraceContext.
+ * // Tracing is even enabled if the executor runs the runnable only after the
+ * // TraceContext was closed.
+ *
+ * // The tag "foo=bar" is not set, since it was added to the logging context only
+ * // after this runnable was created.
+ *
+ * // do stuff
+ * }))
+ * .get();
+ * traceContext.addTag("foo", "bar");
+ * }
+ * </pre>
+ *
+ * @see LoggingContextAwareCallable
+ */
+public class LoggingContextAwareRunnable implements Runnable {
+ private final Runnable runnable;
+ private final Thread callingThread;
+ private final ImmutableSetMultimap<String, String> tags;
+ private final boolean forceLogging;
+
+ LoggingContextAwareRunnable(Runnable runnable) {
+ this.runnable = runnable;
+ this.callingThread = Thread.currentThread();
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
+ this.forceLogging = LoggingContext.getInstance().isLoggingForced();
+ }
+
+ public Runnable unwrap() {
+ return runnable;
+ }
+
+ @Override
+ public void run() {
+ if (callingThread.equals(Thread.currentThread())) {
+ // propagation of logging context is not needed
+ runnable.run();
+ return;
+ }
+
+ // propagate logging context
+ LoggingContext loggingCtx = LoggingContext.getInstance();
+ ImmutableSetMultimap<String, String> oldTags = loggingCtx.getTagsAsMap();
+ boolean oldForceLogging = loggingCtx.isLoggingForced();
+ loggingCtx.setTags(tags);
+ loggingCtx.forceLogging(forceLogging);
+ try {
+ runnable.run();
+ } finally {
+ loggingCtx.setTags(oldTags);
+ loggingCtx.forceLogging(oldForceLogging);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java b/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java
new file mode 100644
index 0000000..e17a91e
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2018 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.google.gerrit.server.logging;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ScheduledExecutorService} that copies the {@link LoggingContext} on executing a {@link
+ * Runnable} to the executing thread.
+ */
+public class LoggingContextAwareScheduledExecutorService extends LoggingContextAwareExecutorService
+ implements ScheduledExecutorService {
+ private final ScheduledExecutorService scheduledExecutorService;
+
+ public LoggingContextAwareScheduledExecutorService(
+ ScheduledExecutorService scheduledExecutorService) {
+ super(scheduledExecutorService);
+ this.scheduledExecutorService = scheduledExecutorService;
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ return scheduledExecutorService.schedule(LoggingContext.copy(command), delay, unit);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return scheduledExecutorService.schedule(LoggingContext.copy(callable), delay, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(
+ Runnable command, long initialDelay, long period, TimeUnit unit) {
+ return scheduledExecutorService.scheduleAtFixedRate(
+ LoggingContext.copy(command), initialDelay, period, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(
+ Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ return scheduledExecutorService.scheduleWithFixedDelay(
+ LoggingContext.copy(command), initialDelay, delay, unit);
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java b/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java
deleted file mode 100644
index 05ff0d3..0000000
--- a/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2018 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.google.gerrit.server.logging;
-
-import com.google.common.collect.ImmutableSetMultimap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-
-/**
- * ThreadFactory that copies the logging context of the current thread to any new thread that is
- * created by this ThreadFactory.
- */
-public class LoggingContextAwareThreadFactory implements ThreadFactory {
- private final ThreadFactory parentThreadFactory;
-
- public LoggingContextAwareThreadFactory() {
- this.parentThreadFactory = Executors.defaultThreadFactory();
- }
-
- public LoggingContextAwareThreadFactory(ThreadFactory parentThreadFactory) {
- this.parentThreadFactory = parentThreadFactory;
- }
-
- @Override
- public Thread newThread(Runnable r) {
- Thread callingThread = Thread.currentThread();
- ImmutableSetMultimap<String, String> tags = LoggingContext.getInstance().getTagsAsMap();
- boolean forceLogging = LoggingContext.getInstance().isLoggingForced();
- return parentThreadFactory.newThread(
- () -> {
- if (callingThread.equals(Thread.currentThread())) {
- // propagation of logging context is not needed
- r.run();
- return;
- }
-
- // propagate logging context
- LoggingContext loggingCtx = LoggingContext.getInstance();
- loggingCtx.setTags(tags);
- loggingCtx.forceLogging(forceLogging);
- try {
- r.run();
- } finally {
- loggingCtx.clearTags();
- loggingCtx.forceLogging(false);
- }
- });
- }
-}
diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java
index 5397077..977baa5 100644
--- a/java/com/google/gerrit/server/logging/TraceContext.java
+++ b/java/com/google/gerrit/server/logging/TraceContext.java
@@ -16,11 +16,15 @@
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* TraceContext that allows to set logging tags and enforce logging.
@@ -43,8 +47,9 @@
* </pre>
*
* <p>The logging tags and the force logging flag are stored in the {@link LoggingContext}. {@link
- * LoggingContextAwareThreadFactory} ensures that the logging context is automatically copied to
- * background threads.
+ * LoggingContextAwareExecutorService}, {@link LoggingContextAwareScheduledExecutorService} and the
+ * executor in {@link com.google.gerrit.server.git.WorkQueue} ensure that the logging context is
+ * automatically copied to background threads.
*
* <p>On close of the trace context newly set tags are unset. Force logging is disabled on close if
* it got enabled while the trace context was open.
@@ -148,6 +153,75 @@
void accept(String tagName, String traceId);
}
+ /**
+ * Opens a new timer that logs the time for an operation if request tracing is enabled.
+ *
+ * <p>If request tracing is not enabled this is a no-op.
+ *
+ * @param message the message
+ * @return the trace timer
+ */
+ public static TraceTimer newTimer(String message) {
+ return new TraceTimer(message);
+ }
+
+ /**
+ * Opens a new timer that logs the time for an operation if request tracing is enabled.
+ *
+ * <p>If request tracing is not enabled this is a no-op.
+ *
+ * @param format the message format string
+ * @param arg argument for the message
+ * @return the trace timer
+ */
+ public static TraceTimer newTimer(String format, Object arg) {
+ return new TraceTimer(format, arg);
+ }
+
+ /**
+ * Opens a new timer that logs the time for an operation if request tracing is enabled.
+ *
+ * <p>If request tracing is not enabled this is a no-op.
+ *
+ * @param format the message format string
+ * @param arg1 first argument for the message
+ * @param arg2 second argument for the message
+ * @return the trace timer
+ */
+ public static TraceTimer newTimer(String format, Object arg1, Object arg2) {
+ return new TraceTimer(format, arg1, arg2);
+ }
+
+ public static class TraceTimer implements AutoCloseable {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final Consumer<Long> logFn;
+ private final Stopwatch stopwatch;
+
+ private TraceTimer(String message) {
+ this(elapsedMs -> logger.atFine().log(message + " (%d ms)", elapsedMs));
+ }
+
+ private TraceTimer(String format, @Nullable Object arg) {
+ this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg, elapsedMs));
+ }
+
+ private TraceTimer(String format, @Nullable Object arg1, @Nullable Object arg2) {
+ this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, elapsedMs));
+ }
+
+ private TraceTimer(Consumer<Long> logFn) {
+ this.logFn = logFn;
+ this.stopwatch = Stopwatch.createStarted();
+ }
+
+ @Override
+ public void close() {
+ stopwatch.stop();
+ logFn.accept(stopwatch.elapsed(TimeUnit.MILLISECONDS));
+ }
+ }
+
// Table<TAG_NAME, TAG_VALUE, REMOVE_ON_CLOSE>
private final Table<String, String, Boolean> tags = HashBasedTable.create();
diff --git a/java/com/google/gerrit/server/patch/DiffExecutorModule.java b/java/com/google/gerrit/server/patch/DiffExecutorModule.java
index f3776e0..eb6a280 100644
--- a/java/com/google/gerrit/server/patch/DiffExecutorModule.java
+++ b/java/com/google/gerrit/server/patch/DiffExecutorModule.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.patch;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
@@ -32,11 +32,8 @@
@Singleton
@DiffExecutor
public ExecutorService createDiffExecutor() {
- return Executors.newCachedThreadPool(
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("Diff-%d")
- .setDaemon(true)
- .build());
+ return new LoggingContextAwareExecutorService(
+ Executors.newCachedThreadPool(
+ new ThreadFactoryBuilder().setNameFormat("Diff-%d").setDaemon(true).build()));
}
}
diff --git a/java/com/google/gerrit/server/project/ProjectCacheClock.java b/java/com/google/gerrit/server/project/ProjectCacheClock.java
index 188ee08..eb451fd 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheClock.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheClock.java
@@ -18,7 +18,7 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.concurrent.Executors;
@@ -54,14 +54,14 @@
// Start with generation 1 (to avoid magic 0 below).
generation.set(1);
executor =
- Executors.newScheduledThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ProjectCacheClock-%d")
- .setDaemon(true)
- .setPriority(Thread.MIN_PRIORITY)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ Executors.newScheduledThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("ProjectCacheClock-%d")
+ .setDaemon(true)
+ .setPriority(Thread.MIN_PRIORITY)
+ .build()));
@SuppressWarnings("unused") // Runnable already handles errors
Future<?> possiblyIgnoredError =
executor.scheduleAtFixedRate(
diff --git a/java/com/google/gerrit/server/project/ProjectCacheWarmer.java b/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
index adfaf62..10cf2de 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
@@ -19,11 +19,11 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
@@ -44,13 +44,11 @@
public void start() {
int cpus = Runtime.getRuntime().availableProcessors();
if (config.getBoolean("cache", "projects", "loadOnStartup", false)) {
- ThreadPoolExecutor pool =
- new ScheduledThreadPoolExecutor(
- config.getInt("cache", "projects", "loadThreads", cpus),
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ProjectCacheLoader-%d")
- .build());
+ ExecutorService pool =
+ new LoggingContextAwareExecutorService(
+ new ScheduledThreadPoolExecutor(
+ config.getInt("cache", "projects", "loadThreads", cpus),
+ new ThreadFactoryBuilder().setNameFormat("ProjectCacheLoader-%d").build()));
Thread scheduler =
new Thread(
() -> {
diff --git a/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
index 68ea7bb..1fdf7d8 100644
--- a/java/com/google/gerrit/sshd/CommandFactoryProvider.java
+++ b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -22,7 +22,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.gerrit.sshd.SshScope.Context;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -78,12 +78,12 @@
int threads = cfg.getInt("sshd", "commandStartThreads", 2);
startExecutor = workQueue.createQueue(threads, "SshCommandStart", true);
destroyExecutor =
- Executors.newSingleThreadExecutor(
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("SshCommandDestroy-%s")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareExecutorService(
+ Executors.newSingleThreadExecutor(
+ new ThreadFactoryBuilder()
+ .setNameFormat("SshCommandDestroy-%s")
+ .setDaemon(true)
+ .build()));
}
@Override
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index 4de54e3..137dc21 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -20,6 +20,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.truth.Expect;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
@@ -28,23 +29,31 @@
import com.google.gerrit.httpd.restapi.ParameterParser;
import com.google.gerrit.httpd.restapi.RestApiServlet;
import com.google.gerrit.server.events.CommitReceivedEvent;
+import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.project.CreateProjectArgs;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
import org.apache.http.message.BasicHeader;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
public class TraceIT extends AbstractDaemonTest {
+ @Rule public final Expect expect = Expect.create();
+
@Inject private DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
@Inject private DynamicSet<CommitValidationListener> commitValidationListeners;
+ @Inject private WorkQueue workQueue;
private TraceValidatingProjectCreationValidationListener projectCreationListener;
private RegistrationHandle projectCreationListenerRegistrationHandle;
@@ -218,6 +227,47 @@
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
+ @Test
+ public void workQueueCopyLoggingContext() throws Exception {
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
+ SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+
+ workQueue
+ .createQueue(1, "test-queue")
+ .submit(
+ () -> {
+ // Verify that the tags and force logging flag have been propagated to the new
+ // thread.
+ SortedMap<String, SortedSet<Object>> threadTagMap =
+ LoggingContext.getInstance().getTags().asMap();
+ expect.that(threadTagMap.keySet()).containsExactly("foo");
+ expect.that(threadTagMap.get("foo")).containsExactly("bar");
+ expect
+ .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isTrue();
+ })
+ .get();
+
+ // Verify that tags and force logging flag in the outer thread are still set.
+ tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+ }
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ }
+
+ private void assertForceLogging(boolean expected) {
+ assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isEqualTo(expected);
+ }
+
private static class TraceValidatingProjectCreationValidationListener
implements ProjectCreationValidationListener {
String traceId;
diff --git a/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
new file mode 100644
index 0000000..5117c01
--- /dev/null
+++ b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2018 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.google.gerrit.server.logging;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.truth.Expect;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class LoggingContextAwareExecutorServiceTest {
+ @Rule public final Expect expect = Expect.create();
+
+ @Test
+ public void loggingContextPropagationToBackgroundThread() throws Exception {
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
+ SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+
+ ExecutorService executor =
+ new LoggingContextAwareExecutorService(Executors.newFixedThreadPool(1));
+ executor
+ .submit(
+ () -> {
+ // Verify that the tags and force logging flag have been propagated to the new
+ // thread.
+ SortedMap<String, SortedSet<Object>> threadTagMap =
+ LoggingContext.getInstance().getTags().asMap();
+ expect.that(threadTagMap.keySet()).containsExactly("foo");
+ expect.that(threadTagMap.get("foo")).containsExactly("bar");
+ expect
+ .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isTrue();
+ })
+ .get();
+
+ // Verify that tags and force logging flag in the outer thread are still set.
+ tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+ }
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ }
+
+ private void assertForceLogging(boolean expected) {
+ assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isEqualTo(expected);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java b/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java
deleted file mode 100644
index 1164e27..0000000
--- a/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (C) 2018 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.google.gerrit.server.logging;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.common.truth.Expect;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class LoggingContextAwareThreadFactoryTest {
- @Rule public final Expect expect = Expect.create();
-
- @Test
- public void loggingContextPropagationToNewThread() throws Exception {
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
- SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
-
- Thread thread =
- new LoggingContextAwareThreadFactory(r -> new Thread(r, "test-thread"))
- .newThread(
- () -> {
- // Verify that the tags and force logging flag have been propagated to the new
- // thread.
- SortedMap<String, SortedSet<Object>> threadTagMap =
- LoggingContext.getInstance().getTags().asMap();
- expect.that(threadTagMap.keySet()).containsExactly("foo");
- expect.that(threadTagMap.get("foo")).containsExactly("bar");
- expect
- .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isTrue();
- });
-
- // Execute in background.
- thread.start();
- thread.join();
-
- // Verify that tags and force logging flag in the outer thread are still set.
- tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
- }
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- }
-
- @Test
- public void loggingContextPropagationToSameThread() throws Exception {
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
- SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
-
- Thread thread =
- new LoggingContextAwareThreadFactory()
- .newThread(
- () -> {
- // Verify that the tags and force logging flag have been propagated to the new
- // thread.
- SortedMap<String, SortedSet<Object>> threadTagMap =
- LoggingContext.getInstance().getTags().asMap();
- expect.that(threadTagMap.keySet()).containsExactly("foo");
- expect.that(threadTagMap.get("foo")).containsExactly("bar");
- expect
- .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isTrue();
- });
-
- // Execute in the same thread.
- thread.run();
-
- // Verify that tags and force logging flag in the outer thread are still set.
- tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
- }
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- }
-
- private void assertForceLogging(boolean expected) {
- assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isEqualTo(expected);
- }
-}
diff --git a/plugins/hooks b/plugins/hooks
index ca64db3..cc74144 160000
--- a/plugins/hooks
+++ b/plugins/hooks
@@ -1 +1 @@
-Subproject commit ca64db31265e985ab3cec635d6f063b0414c45e1
+Subproject commit cc74144db755a18c5a63764a336b93ab3d1be1fe