Line data Source code
1 : // Copyright (C) 2019 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.ImmutableList; 18 : import com.google.common.collect.Iterables; 19 : import com.google.common.flogger.FluentLogger; 20 : import com.google.gerrit.extensions.registration.DynamicSet; 21 : import com.google.gerrit.extensions.registration.Extension; 22 : import org.eclipse.jgit.lib.Config; 23 : 24 : /** 25 : * Context for capturing performance log records. When the context is closed the performance log 26 : * records are handed over to the registered {@link PerformanceLogger}s. 27 : * 28 : * <p>Capturing performance log records is disabled if there are no {@link PerformanceLogger} 29 : * registered (in this case the captured performance log records would never be used). 30 : * 31 : * <p>It's important to enable capturing of performance log records in a context that ensures to 32 : * consume the captured performance log records. Otherwise captured performance log records might 33 : * leak into other requests that are executed by the same thread (if a thread pool is used to 34 : * process requests). 35 : */ 36 : public class PerformanceLogContext implements AutoCloseable { 37 105 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 38 : 39 : // Do not use PluginSetContext. PluginSetContext traces the plugin latency with a timer metric 40 : // which would result in a performance log and we don't want to log the performance of writing 41 : // a performance log in the performance log (endless loop). 42 : private final DynamicSet<PerformanceLogger> performanceLoggers; 43 : 44 : private final boolean oldPerformanceLogging; 45 : private final ImmutableList<PerformanceLogRecord> oldPerformanceLogRecords; 46 : 47 : public PerformanceLogContext( 48 105 : Config gerritConfig, DynamicSet<PerformanceLogger> performanceLoggers) { 49 105 : this.performanceLoggers = performanceLoggers; 50 : 51 : // Just in case remember the old state and reset performance log entries. 52 105 : this.oldPerformanceLogging = LoggingContext.getInstance().isPerformanceLogging(); 53 105 : this.oldPerformanceLogRecords = LoggingContext.getInstance().getPerformanceLogRecords(); 54 105 : LoggingContext.getInstance().clearPerformanceLogEntries(); 55 : 56 : // Do not create performance log entries if performance logging is disabled or if no 57 : // PerformanceLogger is registered. 58 105 : boolean enablePerformanceLogging = 59 105 : gerritConfig.getBoolean("tracing", "performanceLogging", false); 60 105 : LoggingContext.getInstance() 61 105 : .performanceLogging( 62 105 : enablePerformanceLogging && !Iterables.isEmpty(performanceLoggers.entries())); 63 105 : } 64 : 65 : @Override 66 : public void close() { 67 105 : if (LoggingContext.getInstance().isPerformanceLogging()) { 68 3 : runEach(performanceLoggers, LoggingContext.getInstance().getPerformanceLogRecords()); 69 : } 70 : 71 : // Restore old state. Required to support nesting of PerformanceLogContext's. 72 105 : LoggingContext.getInstance().performanceLogging(oldPerformanceLogging); 73 105 : LoggingContext.getInstance().setPerformanceLogRecords(oldPerformanceLogRecords); 74 105 : } 75 : 76 : /** 77 : * Invokes all performance loggers. 78 : * 79 : * <p>Similar to how {@code com.google.gerrit.server.plugincontext.PluginContext} invokes plugins 80 : * but without recording metrics for invoking {@link PerformanceLogger}s. 81 : * 82 : * @param performanceLoggers the performance loggers that should be invoked 83 : * @param performanceLogRecords the performance log records that should be handed over to the 84 : * performance loggers 85 : */ 86 : private static void runEach( 87 : DynamicSet<PerformanceLogger> performanceLoggers, 88 : ImmutableList<PerformanceLogRecord> performanceLogRecords) { 89 3 : performanceLoggers 90 3 : .entries() 91 3 : .forEach( 92 : p -> { 93 3 : try (TraceContext traceContext = newPluginTrace(p)) { 94 3 : performanceLogRecords.forEach(r -> r.writeTo(p.get())); 95 0 : } catch (RuntimeException e) { 96 0 : logger.atWarning().withCause(e).log( 97 0 : "Failure in %s of plugin %s", p.get().getClass(), p.getPluginName()); 98 3 : } 99 3 : }); 100 3 : } 101 : 102 : /** 103 : * Opens a trace context for a plugin that implements {@link PerformanceLogger}. 104 : * 105 : * <p>Basically the same as {@code 106 : * com.google.gerrit.server.plugincontext.PluginContext#newTrace(Extension<T>)}. We have this 107 : * method here to avoid a dependency on PluginContext which lives in 108 : * "//java/com/google/gerrit/server". This package ("//java/com/google/gerrit/server/logging") 109 : * should have as few dependencies as possible. 110 : * 111 : * @param extension performance logger extension 112 : * @return the trace context 113 : */ 114 : private static TraceContext newPluginTrace(Extension<PerformanceLogger> extension) { 115 3 : return TraceContext.open().addPluginTag(extension.getPluginName()); 116 : } 117 : }