Line data Source code
1 : // Copyright (C) 2018 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.logging; 16 : 17 : import com.google.common.collect.ImmutableSetMultimap; 18 : import com.google.common.flogger.FluentLogger; 19 : 20 : /** 21 : * Wrapper for a {@link Runnable} that copies the {@link LoggingContext} from the current thread to 22 : * the thread that executes the runnable. 23 : * 24 : * <p>The state of the logging context that is copied to the thread that executes the runnable is 25 : * fixed at the creation time of this wrapper. If the runnable is submitted to an executor and is 26 : * executed later this means that changes that are done to the logging context in between creating 27 : * and executing the runnable do not apply. 28 : * 29 : * <p>Example: 30 : * 31 : * <pre>{@code 32 : * try (TraceContext traceContext = TraceContext.newTrace(true, ...)) { 33 : * executor 34 : * .submit(new LoggingContextAwareRunnable( 35 : * () -> { 36 : * // Tracing is enabled since the runnable is created within the TraceContext. 37 : * // Tracing is even enabled if the executor runs the runnable only after the 38 : * // TraceContext was closed. 39 : * 40 : * // The tag "foo=bar" is not set, since it was added to the logging context only 41 : * // after this runnable was created. 42 : * 43 : * // do stuff 44 : * })) 45 : * .get(); 46 : * traceContext.addTag("foo", "bar"); 47 : * } 48 : * }</pre> 49 : * 50 : * @see LoggingContextAwareCallable 51 : */ 52 : public class LoggingContextAwareRunnable implements Runnable { 53 148 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 54 : 55 : private final Runnable runnable; 56 : private final Thread callingThread; 57 : private final ImmutableSetMultimap<String, String> tags; 58 : private final boolean forceLogging; 59 : private final boolean performanceLogging; 60 : private final MutablePerformanceLogRecords mutablePerformanceLogRecords; 61 : private final boolean aclLogging; 62 : private final MutableAclLogRecords mutableAclLogRecords; 63 : 64 : /** 65 : * Creates a LoggingContextAwareRunnable that wraps the given {@link Runnable}. 66 : * 67 : * @param runnable Runnable that should be wrapped. 68 : * @param mutablePerformanceLogRecords instance of {@link MutablePerformanceLogRecords} to which 69 : * performance log records that are created from the runnable are added 70 : * @param mutableAclLogRecords instance of {@link MutableAclLogRecords} to which ACL log records 71 : * that are created from the runnable are added 72 : */ 73 : LoggingContextAwareRunnable( 74 : Runnable runnable, 75 : MutablePerformanceLogRecords mutablePerformanceLogRecords, 76 148 : MutableAclLogRecords mutableAclLogRecords) { 77 148 : this.runnable = runnable; 78 148 : this.callingThread = Thread.currentThread(); 79 148 : this.tags = LoggingContext.getInstance().getTagsAsMap(); 80 148 : this.forceLogging = LoggingContext.getInstance().isLoggingForced(); 81 148 : this.performanceLogging = LoggingContext.getInstance().isPerformanceLogging(); 82 148 : this.mutablePerformanceLogRecords = mutablePerformanceLogRecords; 83 148 : this.aclLogging = LoggingContext.getInstance().isAclLogging(); 84 148 : this.mutableAclLogRecords = mutableAclLogRecords; 85 148 : } 86 : 87 : public Runnable unwrap() { 88 146 : return runnable; 89 : } 90 : 91 : @Override 92 : public void run() { 93 148 : if (callingThread.equals(Thread.currentThread())) { 94 : // propagation of logging context is not needed 95 15 : runnable.run(); 96 15 : return; 97 : } 98 : 99 148 : LoggingContext loggingCtx = LoggingContext.getInstance(); 100 : 101 148 : if (!loggingCtx.isEmpty()) { 102 0 : logger.atWarning().log("Logging context is not empty: %s", loggingCtx); 103 : } 104 : 105 : // propagate logging context 106 148 : loggingCtx.setTags(tags); 107 148 : loggingCtx.forceLogging(forceLogging); 108 148 : loggingCtx.performanceLogging(performanceLogging); 109 148 : loggingCtx.setMutablePerformanceLogRecords(mutablePerformanceLogRecords); 110 148 : loggingCtx.aclLogging(aclLogging); 111 148 : loggingCtx.setMutableAclLogRecords(mutableAclLogRecords); 112 : try { 113 148 : runnable.run(); 114 : } finally { 115 : // Cleanup logging context. This is important if the thread is pooled and reused. 116 148 : loggingCtx.clear(); 117 : } 118 148 : } 119 : }