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 static java.util.Objects.requireNonNull; 18 : 19 : import com.google.common.base.MoreObjects; 20 : import com.google.common.collect.ImmutableList; 21 : import com.google.common.collect.ImmutableSetMultimap; 22 : import com.google.common.flogger.context.Tags; 23 : import com.google.inject.Provider; 24 : import java.util.List; 25 : import java.util.concurrent.Callable; 26 : import java.util.logging.Level; 27 : 28 : /** 29 : * Logging context for Flogger. 30 : * 31 : * <p>To configure this logging context for Flogger set the following system property (also see 32 : * {@link com.google.common.flogger.backend.system.DefaultPlatform}): 33 : * 34 : * <ul> 35 : * <li>{@code 36 : * flogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance}. 37 : * </ul> 38 : */ 39 : public class LoggingContext extends com.google.common.flogger.backend.system.LoggingContext { 40 154 : private static final LoggingContext INSTANCE = new LoggingContext(); 41 : 42 154 : private static final ThreadLocal<MutableTags> tags = new ThreadLocal<>(); 43 154 : private static final ThreadLocal<Boolean> forceLogging = new ThreadLocal<>(); 44 154 : private static final ThreadLocal<Boolean> performanceLogging = new ThreadLocal<>(); 45 154 : private static final ThreadLocal<Boolean> aclLogging = new ThreadLocal<>(); 46 : 47 : /** 48 : * When copying the logging context to a new thread we need to ensure that the mutable log records 49 : * (performance logs and ACL logs) that are added in the new thread are added to the same multable 50 : * log records instance (see {@link LoggingContextAwareRunnable} and {@link 51 : * LoggingContextAwareCallable}). This is important since performance log records are processed 52 : * only at the end of the request and performance log records that are created in another thread 53 : * should not get lost. 54 : */ 55 154 : private static final ThreadLocal<MutablePerformanceLogRecords> performanceLogRecords = 56 : new ThreadLocal<>(); 57 : 58 154 : private static final ThreadLocal<MutableAclLogRecords> aclLogRecords = new ThreadLocal<>(); 59 : 60 : private LoggingContext() {} 61 : 62 : /** This method is expected to be called via reflection (and might otherwise be unused). */ 63 : public static LoggingContext getInstance() { 64 154 : return INSTANCE; 65 : } 66 : 67 : public static Runnable copy(Runnable runnable) { 68 148 : if (runnable instanceof LoggingContextAwareRunnable) { 69 146 : return runnable; 70 : } 71 : 72 148 : return new LoggingContextAwareRunnable( 73 : runnable, 74 148 : getInstance().getMutablePerformanceLogRecords(), 75 148 : getInstance().getMutableAclRecords()); 76 : } 77 : 78 : public static <T> Callable<T> copy(Callable<T> callable) { 79 87 : if (callable instanceof LoggingContextAwareCallable) { 80 0 : return callable; 81 : } 82 : 83 87 : return new LoggingContextAwareCallable<>( 84 : callable, 85 87 : getInstance().getMutablePerformanceLogRecords(), 86 87 : getInstance().getMutableAclRecords()); 87 : } 88 : 89 : public boolean isEmpty() { 90 148 : return tags.get() == null 91 148 : && forceLogging.get() == null 92 148 : && performanceLogging.get() == null 93 148 : && (performanceLogRecords.get() == null || performanceLogRecords.get().isEmtpy()) 94 148 : && aclLogging.get() == null 95 148 : && (aclLogRecords.get() == null || aclLogRecords.get().isEmpty()); 96 : } 97 : 98 : public void clear() { 99 148 : tags.remove(); 100 148 : forceLogging.remove(); 101 148 : performanceLogging.remove(); 102 148 : performanceLogRecords.remove(); 103 148 : aclLogging.remove(); 104 148 : aclLogRecords.remove(); 105 148 : } 106 : 107 : @Override 108 : public boolean shouldForceLogging(String loggerName, Level level, boolean isEnabled) { 109 130 : return isLoggingForced(); 110 : } 111 : 112 : @Override 113 : public Tags getTags() { 114 130 : MutableTags mutableTags = tags.get(); 115 130 : return mutableTags != null ? mutableTags.getTags() : Tags.empty(); 116 : } 117 : 118 : public ImmutableSetMultimap<String, String> getTagsAsMap() { 119 148 : MutableTags mutableTags = tags.get(); 120 148 : return mutableTags != null ? mutableTags.asMap() : ImmutableSetMultimap.of(); 121 : } 122 : 123 : boolean addTag(String name, String value) { 124 151 : return getMutableTags().add(name, value); 125 : } 126 : 127 : void removeTag(String name, String value) { 128 151 : MutableTags mutableTags = getMutableTags(); 129 151 : mutableTags.remove(name, value); 130 151 : if (mutableTags.isEmpty()) { 131 151 : tags.remove(); 132 : } 133 151 : } 134 : 135 : void setTags(ImmutableSetMultimap<String, String> newTags) { 136 148 : if (newTags.isEmpty()) { 137 148 : tags.remove(); 138 148 : return; 139 : } 140 103 : getMutableTags().set(newTags); 141 103 : } 142 : 143 : void clearTags() { 144 1 : tags.remove(); 145 1 : } 146 : 147 : private MutableTags getMutableTags() { 148 151 : MutableTags mutableTags = tags.get(); 149 151 : if (mutableTags == null) { 150 151 : mutableTags = new MutableTags(); 151 151 : tags.set(mutableTags); 152 : } 153 151 : return mutableTags; 154 : } 155 : 156 : boolean isLoggingForced() { 157 148 : return Boolean.TRUE.equals(forceLogging.get()); 158 : } 159 : 160 : boolean forceLogging(boolean force) { 161 148 : Boolean oldValue = forceLogging.get(); 162 148 : if (force) { 163 7 : forceLogging.set(true); 164 : } else { 165 148 : forceLogging.remove(); 166 : } 167 148 : return Boolean.TRUE.equals(oldValue); 168 : } 169 : 170 : boolean isPerformanceLogging() { 171 154 : Boolean isPerformanceLogging = performanceLogging.get(); 172 154 : return isPerformanceLogging != null ? isPerformanceLogging : false; 173 : } 174 : 175 : /** 176 : * Enables performance logging. 177 : * 178 : * <p>It's important to enable performance logging only in a context that ensures to consume the 179 : * captured performance log records. Otherwise captured performance log records might leak into 180 : * other requests that are executed by the same thread (if a thread pool is used to process 181 : * requests). 182 : * 183 : * @param enable whether performance logging should be enabled. 184 : */ 185 : void performanceLogging(boolean enable) { 186 148 : if (enable) { 187 3 : performanceLogging.set(true); 188 : } else { 189 148 : performanceLogging.remove(); 190 : } 191 148 : } 192 : 193 : /** 194 : * Adds a performance log record, if performance logging is enabled. 195 : * 196 : * @param recordProvider Provider for the performance log record. This provider is only invoked if 197 : * performance logging is enabled. This means if performance logging is disabled, we avoid the 198 : * creation of a {@link PerformanceLogRecord}. 199 : */ 200 : public void addPerformanceLogRecord(Provider<PerformanceLogRecord> recordProvider) { 201 154 : if (!isPerformanceLogging()) { 202 : // return early and avoid the creation of a PerformanceLogRecord 203 154 : return; 204 : } 205 : 206 3 : getMutablePerformanceLogRecords().add(recordProvider.get()); 207 3 : } 208 : 209 : ImmutableList<PerformanceLogRecord> getPerformanceLogRecords() { 210 105 : MutablePerformanceLogRecords records = performanceLogRecords.get(); 211 105 : if (records != null) { 212 98 : return records.list(); 213 : } 214 29 : return ImmutableList.of(); 215 : } 216 : 217 : void clearPerformanceLogEntries() { 218 105 : performanceLogRecords.remove(); 219 105 : } 220 : 221 : /** 222 : * Set the performance log records in this logging context. Existing log records are overwritten. 223 : * 224 : * <p>This method makes a defensive copy of the passed in list. 225 : * 226 : * @param newPerformanceLogRecords performance log records that should be set 227 : */ 228 : void setPerformanceLogRecords(List<PerformanceLogRecord> newPerformanceLogRecords) { 229 105 : if (newPerformanceLogRecords.isEmpty()) { 230 105 : performanceLogRecords.remove(); 231 105 : return; 232 : } 233 : 234 1 : getMutablePerformanceLogRecords().set(newPerformanceLogRecords); 235 1 : } 236 : 237 : /** 238 : * Sets a {@link MutablePerformanceLogRecords} instance for storing performance log records. 239 : * 240 : * <p><strong>Attention:</strong> The passed in {@link MutablePerformanceLogRecords} instance is 241 : * directly stored in the logging context. 242 : * 243 : * <p>This method is intended to be only used when the logging context is copied to a new thread. 244 : * 245 : * @param mutablePerformanceLogRecords the {@link MutablePerformanceLogRecords} instance in which 246 : * performance log records should be stored 247 : */ 248 : void setMutablePerformanceLogRecords(MutablePerformanceLogRecords mutablePerformanceLogRecords) { 249 148 : performanceLogRecords.set(requireNonNull(mutablePerformanceLogRecords)); 250 148 : } 251 : 252 : private MutablePerformanceLogRecords getMutablePerformanceLogRecords() { 253 148 : MutablePerformanceLogRecords records = performanceLogRecords.get(); 254 148 : if (records == null) { 255 148 : records = new MutablePerformanceLogRecords(); 256 148 : performanceLogRecords.set(records); 257 : } 258 148 : return records; 259 : } 260 : 261 : public boolean isAclLogging() { 262 151 : Boolean isAclLogging = aclLogging.get(); 263 151 : return isAclLogging != null ? isAclLogging : false; 264 : } 265 : 266 : /** 267 : * Enables ACL logging. 268 : * 269 : * <p>It's important to enable ACL logging only in a context that ensures to consume the captured 270 : * ACL log records. Otherwise captured ACL log records might leak into other requests that are 271 : * executed by the same thread (if a thread pool is used to process requests). 272 : * 273 : * @param enable whether ACL logging should be enabled. 274 : * @return whether ACL logging was be enabled before invoking this method (old value). 275 : */ 276 : boolean aclLogging(boolean enable) { 277 148 : Boolean oldValue = aclLogging.get(); 278 148 : if (enable) { 279 1 : aclLogging.set(true); 280 : } else { 281 148 : aclLogging.remove(); 282 : } 283 148 : return oldValue != null ? oldValue : false; 284 : } 285 : 286 : /** 287 : * Adds an ACL log record. 288 : * 289 : * @param aclLogRecord ACL log record 290 : */ 291 : public void addAclLogRecord(String aclLogRecord) { 292 22 : if (!isAclLogging()) { 293 21 : return; 294 : } 295 : 296 1 : getMutableAclRecords().add(aclLogRecord); 297 1 : } 298 : 299 : ImmutableList<String> getAclLogRecords() { 300 151 : MutableAclLogRecords records = aclLogRecords.get(); 301 151 : if (records != null) { 302 148 : return records.list(); 303 : } 304 146 : return ImmutableList.of(); 305 : } 306 : 307 : /** 308 : * Set the ACL log records in this logging context. Existing log records are overwritten. 309 : * 310 : * <p>This method makes a defensive copy of the passed in list. 311 : * 312 : * @param newAclLogRecords ACL log records that should be set 313 : */ 314 : void setAclLogRecords(List<String> newAclLogRecords) { 315 1 : if (newAclLogRecords.isEmpty()) { 316 1 : aclLogRecords.remove(); 317 1 : return; 318 : } 319 : 320 0 : getMutableAclRecords().set(newAclLogRecords); 321 0 : } 322 : 323 : /** 324 : * Sets a {@link MutableAclLogRecords} instance for storing ACL log records. 325 : * 326 : * <p><strong>Attention:</strong> The passed in {@link MutableAclLogRecords} instance is directly 327 : * stored in the logging context. 328 : * 329 : * <p>This method is intended to be only used when the logging context is copied to a new thread 330 : * to ensure that the ACL log records that are added in the new thread are added to the same 331 : * {@link MutableAclLogRecords} instance (see {@link LoggingContextAwareRunnable} and {@link 332 : * LoggingContextAwareCallable}). This is important since ACL log records are processed only at 333 : * the end of the request and ACL log records that are created in another thread should not get 334 : * lost. 335 : * 336 : * @param mutableAclLogRecords the {@link MutableAclLogRecords} instance in which ACL log records 337 : * should be stored 338 : */ 339 : void setMutableAclLogRecords(MutableAclLogRecords mutableAclLogRecords) { 340 148 : aclLogRecords.set(requireNonNull(mutableAclLogRecords)); 341 148 : } 342 : 343 : private MutableAclLogRecords getMutableAclRecords() { 344 148 : MutableAclLogRecords records = aclLogRecords.get(); 345 148 : if (records == null) { 346 148 : records = new MutableAclLogRecords(); 347 148 : aclLogRecords.set(records); 348 : } 349 148 : return records; 350 : } 351 : 352 : @Override 353 : public String toString() { 354 0 : return MoreObjects.toStringHelper(this) 355 0 : .add("tags", tags.get()) 356 0 : .add("forceLogging", forceLogging.get()) 357 0 : .add("performanceLogging", performanceLogging.get()) 358 0 : .add("performanceLogRecords", performanceLogRecords.get()) 359 0 : .add("aclLogging", aclLogging.get()) 360 0 : .add("aclLogRecords", aclLogRecords.get()) 361 0 : .toString(); 362 : } 363 : }