Support copying logging context to background threads
The logging context (contains logging tags) is stored in ThreadLocal variables. When we execute work in background threads we must propagate the logging context from the current thread to the background threads. Add a LoggingContextAwareThreadFactory that does this and use it in all places where we create new threads. Change-Id: I349f0a8b667266df1684ae5f5d8fb0bcae9aceaa Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
@@ -39,6 +39,7 @@ import com.google.gerrit.index.query.DataSource;
|
|||||||
import com.google.gerrit.index.query.FieldBundle;
|
import com.google.gerrit.index.query.FieldBundle;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
import com.google.gerrit.server.index.IndexUtils;
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
|
import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.ResultSet;
|
import com.google.gwtorm.server.ResultSet;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -131,6 +132,7 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> {
|
|||||||
new ScheduledThreadPoolExecutor(
|
new ScheduledThreadPoolExecutor(
|
||||||
1,
|
1,
|
||||||
new ThreadFactoryBuilder()
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
.setNameFormat(index + " Commit-%d")
|
.setNameFormat(index + " Commit-%d")
|
||||||
.setDaemon(true)
|
.setDaemon(true)
|
||||||
.build());
|
.build());
|
||||||
@@ -171,6 +173,7 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> {
|
|||||||
Executors.newFixedThreadPool(
|
Executors.newFixedThreadPool(
|
||||||
1,
|
1,
|
||||||
new ThreadFactoryBuilder()
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
.setNameFormat(index + " Write-%d")
|
.setNameFormat(index + " Write-%d")
|
||||||
.setDaemon(true)
|
.setDaemon(true)
|
||||||
.build()));
|
.build()));
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.google.gerrit.server.cache.h2.H2CacheImpl.SqlStore;
|
|||||||
import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
|
import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
|
import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
@@ -75,11 +76,16 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
|
|||||||
if (cacheDir != null) {
|
if (cacheDir != null) {
|
||||||
executor =
|
executor =
|
||||||
Executors.newFixedThreadPool(
|
Executors.newFixedThreadPool(
|
||||||
1, new ThreadFactoryBuilder().setNameFormat("DiskCache-Store-%d").build());
|
1,
|
||||||
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
|
.setNameFormat("DiskCache-Store-%d")
|
||||||
|
.build());
|
||||||
cleanup =
|
cleanup =
|
||||||
Executors.newScheduledThreadPool(
|
Executors.newScheduledThreadPool(
|
||||||
1,
|
1,
|
||||||
new ThreadFactoryBuilder()
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
.setNameFormat("DiskCache-Prune-%d")
|
.setNameFormat("DiskCache-Prune-%d")
|
||||||
.setDaemon(true)
|
.setDaemon(true)
|
||||||
.build());
|
.build());
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import com.google.common.util.concurrent.MoreExecutors;
|
|||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import com.google.gerrit.server.FanOutExecutor;
|
import com.google.gerrit.server.FanOutExecutor;
|
||||||
import com.google.gerrit.server.git.WorkQueue;
|
import com.google.gerrit.server.git.WorkQueue;
|
||||||
|
import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
@@ -89,7 +90,11 @@ public class SysExecutorModule extends AbstractModule {
|
|||||||
10,
|
10,
|
||||||
TimeUnit.MINUTES,
|
TimeUnit.MINUTES,
|
||||||
new ArrayBlockingQueue<Runnable>(poolSize),
|
new ArrayBlockingQueue<Runnable>(poolSize),
|
||||||
new ThreadFactoryBuilder().setNameFormat("ChangeUpdate-%d").setDaemon(true).build(),
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
|
.setNameFormat("ChangeUpdate-%d")
|
||||||
|
.setDaemon(true)
|
||||||
|
.build(),
|
||||||
new ThreadPoolExecutor.CallerRunsPolicy())));
|
new ThreadPoolExecutor.CallerRunsPolicy())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.google.gerrit.metrics.MetricMaker;
|
|||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.config.ScheduleConfig.Schedule;
|
import com.google.gerrit.server.config.ScheduleConfig.Schedule;
|
||||||
|
import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
|
||||||
import com.google.gerrit.server.util.IdGenerator;
|
import com.google.gerrit.server.util.IdGenerator;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
@@ -165,11 +166,12 @@ public class WorkQueue {
|
|||||||
if (threadPriority != Thread.NORM_PRIORITY) {
|
if (threadPriority != Thread.NORM_PRIORITY) {
|
||||||
ThreadFactory parent = executor.getThreadFactory();
|
ThreadFactory parent = executor.getThreadFactory();
|
||||||
executor.setThreadFactory(
|
executor.setThreadFactory(
|
||||||
task -> {
|
new LoggingContextAwareThreadFactory(
|
||||||
Thread t = parent.newThread(task);
|
task -> {
|
||||||
t.setPriority(threadPriority);
|
Thread t = parent.newThread(task);
|
||||||
return t;
|
t.setPriority(threadPriority);
|
||||||
});
|
return t;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executor;
|
return executor;
|
||||||
@@ -251,18 +253,19 @@ public class WorkQueue {
|
|||||||
Executor(int corePoolSize, final String queueName) {
|
Executor(int corePoolSize, final String queueName) {
|
||||||
super(
|
super(
|
||||||
corePoolSize,
|
corePoolSize,
|
||||||
new ThreadFactory() {
|
new LoggingContextAwareThreadFactory(
|
||||||
private final ThreadFactory parent = Executors.defaultThreadFactory();
|
new ThreadFactory() {
|
||||||
private final AtomicInteger tid = new AtomicInteger(1);
|
private final ThreadFactory parent = Executors.defaultThreadFactory();
|
||||||
|
private final AtomicInteger tid = new AtomicInteger(1);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Thread newThread(Runnable task) {
|
public Thread newThread(Runnable task) {
|
||||||
final Thread t = parent.newThread(task);
|
final Thread t = parent.newThread(task);
|
||||||
t.setName(queueName + "-" + tid.getAndIncrement());
|
t.setName(queueName + "-" + tid.getAndIncrement());
|
||||||
t.setUncaughtExceptionHandler(LOG_UNCAUGHT_EXCEPTION);
|
t.setUncaughtExceptionHandler(LOG_UNCAUGHT_EXCEPTION);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
all =
|
all =
|
||||||
new ConcurrentHashMap<>( //
|
new ConcurrentHashMap<>( //
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.logging;
|
package com.google.gerrit.server.logging;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSetMultimap;
|
||||||
import com.google.common.flogger.backend.Tags;
|
import com.google.common.flogger.backend.Tags;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
@@ -51,6 +52,11 @@ public class LoggingContext extends com.google.common.flogger.backend.system.Log
|
|||||||
return mutableTags != null ? mutableTags.getTags() : Tags.empty();
|
return mutableTags != null ? mutableTags.getTags() : Tags.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImmutableSetMultimap<String, String> getTagsAsMap() {
|
||||||
|
MutableTags mutableTags = tags.get();
|
||||||
|
return mutableTags != null ? mutableTags.asMap() : ImmutableSetMultimap.of();
|
||||||
|
}
|
||||||
|
|
||||||
boolean addTag(String name, String value) {
|
boolean addTag(String name, String value) {
|
||||||
return getMutableTags().add(name, value);
|
return getMutableTags().add(name, value);
|
||||||
}
|
}
|
||||||
@@ -63,6 +69,14 @@ public class LoggingContext extends com.google.common.flogger.backend.system.Log
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setTags(ImmutableSetMultimap<String, String> newTags) {
|
||||||
|
if (newTags.isEmpty()) {
|
||||||
|
tags.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getMutableTags().set(newTags);
|
||||||
|
}
|
||||||
|
|
||||||
void clearTags() {
|
void clearTags() {
|
||||||
tags.remove();
|
tags.remove();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// 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();
|
||||||
|
return parentThreadFactory.newThread(
|
||||||
|
() -> {
|
||||||
|
if (callingThread.equals(Thread.currentThread())) {
|
||||||
|
// propagation of logging context is not needed
|
||||||
|
r.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// propagate logging context
|
||||||
|
LoggingContext.getInstance().setTags(tags);
|
||||||
|
try {
|
||||||
|
r.run();
|
||||||
|
} finally {
|
||||||
|
LoggingContext.getInstance().clearTags();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ package com.google.gerrit.server.logging;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSetMultimap;
|
||||||
import com.google.common.collect.MultimapBuilder;
|
import com.google.common.collect.MultimapBuilder;
|
||||||
import com.google.common.collect.SetMultimap;
|
import com.google.common.collect.SetMultimap;
|
||||||
import com.google.common.flogger.backend.Tags;
|
import com.google.common.flogger.backend.Tags;
|
||||||
@@ -76,6 +77,26 @@ public class MutableTags {
|
|||||||
tags = Tags.empty();
|
tags = Tags.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tags as Multimap.
|
||||||
|
*
|
||||||
|
* @return the tags as Multimap
|
||||||
|
*/
|
||||||
|
public ImmutableSetMultimap<String, String> asMap() {
|
||||||
|
return ImmutableSetMultimap.copyOf(tagMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the existing tags with the provided tags.
|
||||||
|
*
|
||||||
|
* @param tags the tags that should be set.
|
||||||
|
*/
|
||||||
|
void set(ImmutableSetMultimap<String, String> tags) {
|
||||||
|
tagMap.clear();
|
||||||
|
tags.forEach(tagMap::put);
|
||||||
|
buildTags();
|
||||||
|
}
|
||||||
|
|
||||||
private void buildTags() {
|
private void buildTags() {
|
||||||
if (tagMap.isEmpty()) {
|
if (tagMap.isEmpty()) {
|
||||||
if (tags.isEmpty()) {
|
if (tags.isEmpty()) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.server.patch;
|
package com.google.gerrit.server.patch;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
@@ -32,6 +33,10 @@ public class DiffExecutorModule extends AbstractModule {
|
|||||||
@DiffExecutor
|
@DiffExecutor
|
||||||
public ExecutorService createDiffExecutor() {
|
public ExecutorService createDiffExecutor() {
|
||||||
return Executors.newCachedThreadPool(
|
return Executors.newCachedThreadPool(
|
||||||
new ThreadFactoryBuilder().setNameFormat("Diff-%d").setDaemon(true).build());
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
|
.setNameFormat("Diff-%d")
|
||||||
|
.setDaemon(true)
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|||||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||||
import com.google.gerrit.server.config.ConfigUtil;
|
import com.google.gerrit.server.config.ConfigUtil;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -56,6 +57,7 @@ public class ProjectCacheClock implements LifecycleListener {
|
|||||||
Executors.newScheduledThreadPool(
|
Executors.newScheduledThreadPool(
|
||||||
1,
|
1,
|
||||||
new ThreadFactoryBuilder()
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
.setNameFormat("ProjectCacheClock-%d")
|
.setNameFormat("ProjectCacheClock-%d")
|
||||||
.setDaemon(true)
|
.setDaemon(true)
|
||||||
.setPriority(Thread.MIN_PRIORITY)
|
.setPriority(Thread.MIN_PRIORITY)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|||||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
@@ -46,7 +47,10 @@ public class ProjectCacheWarmer implements LifecycleListener {
|
|||||||
ThreadPoolExecutor pool =
|
ThreadPoolExecutor pool =
|
||||||
new ScheduledThreadPoolExecutor(
|
new ScheduledThreadPoolExecutor(
|
||||||
config.getInt("cache", "projects", "loadThreads", cpus),
|
config.getInt("cache", "projects", "loadThreads", cpus),
|
||||||
new ThreadFactoryBuilder().setNameFormat("ProjectCacheLoader-%d").build());
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
|
.setNameFormat("ProjectCacheLoader-%d")
|
||||||
|
.build());
|
||||||
Thread scheduler =
|
Thread scheduler =
|
||||||
new Thread(
|
new Thread(
|
||||||
() -> {
|
() -> {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.google.gerrit.extensions.registration.DynamicItem;
|
|||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.git.WorkQueue;
|
import com.google.gerrit.server.git.WorkQueue;
|
||||||
|
import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
|
||||||
import com.google.gerrit.sshd.SshScope.Context;
|
import com.google.gerrit.sshd.SshScope.Context;
|
||||||
import com.google.gwtorm.server.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -79,6 +80,7 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
|
|||||||
destroyExecutor =
|
destroyExecutor =
|
||||||
Executors.newSingleThreadExecutor(
|
Executors.newSingleThreadExecutor(
|
||||||
new ThreadFactoryBuilder()
|
new ThreadFactoryBuilder()
|
||||||
|
.setThreadFactory(new LoggingContextAwareThreadFactory())
|
||||||
.setNameFormat("SshCommandDestroy-%s")
|
.setNameFormat("SshCommandDestroy-%s")
|
||||||
.setDaemon(true)
|
.setDaemon(true)
|
||||||
.build());
|
.build());
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ junit_tests(
|
|||||||
"//lib/auto:auto-value",
|
"//lib/auto:auto-value",
|
||||||
"//lib/auto:auto-value-annotations",
|
"//lib/auto:auto-value-annotations",
|
||||||
"//lib/commons:codec",
|
"//lib/commons:codec",
|
||||||
|
"//lib/flogger:api",
|
||||||
"//lib/guice",
|
"//lib/guice",
|
||||||
"//lib/jgit/org.eclipse.jgit:jgit",
|
"//lib/jgit/org.eclipse.jgit:jgit",
|
||||||
"//lib/jgit/org.eclipse.jgit.junit:junit",
|
"//lib/jgit/org.eclipse.jgit.junit:junit",
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
// 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();
|
||||||
|
try (TraceContext traceContext = new TraceContext("foo", "bar")) {
|
||||||
|
SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
|
||||||
|
assertThat(tagMap.keySet()).containsExactly("foo");
|
||||||
|
assertThat(tagMap.get("foo")).containsExactly("bar");
|
||||||
|
|
||||||
|
Thread thread =
|
||||||
|
new LoggingContextAwareThreadFactory(r -> new Thread(r, "test-thread"))
|
||||||
|
.newThread(
|
||||||
|
() -> {
|
||||||
|
// Verify that the tags 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");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute in background.
|
||||||
|
thread.start();
|
||||||
|
thread.join();
|
||||||
|
|
||||||
|
// Verify that tags in the outer thread are still set.
|
||||||
|
tagMap = LoggingContext.getInstance().getTags().asMap();
|
||||||
|
assertThat(tagMap.keySet()).containsExactly("foo");
|
||||||
|
assertThat(tagMap.get("foo")).containsExactly("bar");
|
||||||
|
}
|
||||||
|
assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loggingContextPropagationToSameThread() throws Exception {
|
||||||
|
assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
|
||||||
|
try (TraceContext traceContext = new TraceContext("foo", "bar")) {
|
||||||
|
SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
|
||||||
|
assertThat(tagMap.keySet()).containsExactly("foo");
|
||||||
|
assertThat(tagMap.get("foo")).containsExactly("bar");
|
||||||
|
|
||||||
|
Thread thread =
|
||||||
|
new LoggingContextAwareThreadFactory()
|
||||||
|
.newThread(
|
||||||
|
() -> {
|
||||||
|
// Verify that the tags 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");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute in the same thread.
|
||||||
|
thread.run();
|
||||||
|
|
||||||
|
// Verify that tags in the outer thread are still set.
|
||||||
|
tagMap = LoggingContext.getInstance().getTags().asMap();
|
||||||
|
assertThat(tagMap.keySet()).containsExactly("foo");
|
||||||
|
assertThat(tagMap.get("foo")).containsExactly("bar");
|
||||||
|
}
|
||||||
|
assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assert_;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.ImmutableSetMultimap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
@@ -109,6 +110,27 @@ public class MutableTagsTest {
|
|||||||
assertTags(ImmutableMap.of("name", ImmutableSet.of("value")));
|
assertTags(ImmutableMap.of("name", ImmutableSet.of("value")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setTags() {
|
||||||
|
tags.add("name", "value");
|
||||||
|
assertTags(ImmutableMap.of("name", ImmutableSet.of("value")));
|
||||||
|
|
||||||
|
tags.set(ImmutableSetMultimap.of("foo", "bar", "foo", "baz", "bar", "baz"));
|
||||||
|
assertTags(
|
||||||
|
ImmutableMap.of("foo", ImmutableSet.of("bar", "baz"), "bar", ImmutableSet.of("baz")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void asMap() {
|
||||||
|
tags.add("name", "value");
|
||||||
|
assertThat(tags.asMap()).containsExactlyEntriesIn(ImmutableSetMultimap.of("name", "value"));
|
||||||
|
|
||||||
|
tags.set(ImmutableSetMultimap.of("foo", "bar", "foo", "baz", "bar", "baz"));
|
||||||
|
assertThat(tags.asMap())
|
||||||
|
.containsExactlyEntriesIn(
|
||||||
|
ImmutableSetMultimap.of("foo", "bar", "foo", "baz", "bar", "baz"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void clearTags() {
|
public void clearTags() {
|
||||||
tags.add("name1", "value1");
|
tags.add("name1", "value1");
|
||||||
|
|||||||
Submodule plugins/hooks updated: 07672f3188...ca64db3126
Reference in New Issue
Block a user