LCOV - code coverage report
Current view: top level - server/logging - LoggingContext.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 112 123 91.1 %
Date: 2022-11-19 15:00:39 Functions: 31 32 96.9 %

          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             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750