1 /* 2 * Copyright (C) 2015 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.producers.monitoring; 18 19 import com.google.common.collect.ImmutableList; 20 import com.google.common.collect.Iterables; 21 import dagger.internal.Beta; 22 import java.util.Collection; 23 import java.util.logging.Level; 24 import java.util.logging.Logger; 25 26 /** 27 * Utility methods relating to timing. 28 * 29 * @since 2.1 30 */ 31 // TODO(beder): Reduce the visibility of this class to package-private. 32 @Beta 33 @SuppressWarnings("GoodTime") // Should be using java.time.Instant/Duration as opposed to nanos 34 public final class TimingRecorders { 35 private static final Logger logger = Logger.getLogger(TimingRecorders.class.getName()); 36 37 /** 38 * Returns a timing recorder factory that delegates to the given factories, and ensures that any 39 * method called on this object, even transitively, does not throw a {@link RuntimeException} or 40 * return null. 41 * 42 * <p>If the delegate recorders throw an {@link Error}, then that will escape this recorder 43 * implementation. Errors are treated as unrecoverable conditions, and may cause the entire 44 * component's execution to fail. 45 */ 46 public static ProductionComponentTimingRecorder.Factory delegatingProductionComponentTimingRecorderFactory( Collection<ProductionComponentTimingRecorder.Factory> factories)47 delegatingProductionComponentTimingRecorderFactory( 48 Collection<ProductionComponentTimingRecorder.Factory> factories) { 49 switch (factories.size()) { 50 case 0: 51 return noOpProductionComponentTimingRecorderFactory(); 52 case 1: 53 return new NonThrowingProductionComponentTimingRecorder.Factory( 54 Iterables.getOnlyElement(factories)); 55 default: 56 return new DelegatingProductionComponentTimingRecorder.Factory(factories); 57 } 58 } 59 60 /** 61 * A component recorder that delegates to a single recorder, and catches and logs all exceptions 62 * that the delegate throws. 63 */ 64 private static final class NonThrowingProductionComponentTimingRecorder 65 implements ProductionComponentTimingRecorder { 66 private final ProductionComponentTimingRecorder delegate; 67 NonThrowingProductionComponentTimingRecorder(ProductionComponentTimingRecorder delegate)68 NonThrowingProductionComponentTimingRecorder(ProductionComponentTimingRecorder delegate) { 69 this.delegate = delegate; 70 } 71 72 @Override producerTimingRecorderFor(ProducerToken token)73 public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) { 74 try { 75 ProducerTimingRecorder recorder = delegate.producerTimingRecorderFor(token); 76 return recorder == null 77 ? ProducerTimingRecorder.noOp() 78 : new NonThrowingProducerTimingRecorder(recorder); 79 } catch (RuntimeException e) { 80 logProducerTimingRecorderForException(e, delegate, token); 81 return ProducerTimingRecorder.noOp(); 82 } 83 } 84 85 static final class Factory implements ProductionComponentTimingRecorder.Factory { 86 private final ProductionComponentTimingRecorder.Factory delegate; 87 Factory(ProductionComponentTimingRecorder.Factory delegate)88 Factory(ProductionComponentTimingRecorder.Factory delegate) { 89 this.delegate = delegate; 90 } 91 92 @Override create(Object component)93 public ProductionComponentTimingRecorder create(Object component) { 94 try { 95 ProductionComponentTimingRecorder recorder = delegate.create(component); 96 return recorder == null 97 ? noOpProductionComponentTimingRecorder() 98 : new NonThrowingProductionComponentTimingRecorder(recorder); 99 } catch (RuntimeException e) { 100 logCreateException(e, delegate, component); 101 return noOpProductionComponentTimingRecorder(); 102 } 103 } 104 } 105 } 106 107 /** 108 * A producer recorder that delegates to a single recorder, and catches and logs all exceptions 109 * that the delegate throws. 110 */ 111 private static final class NonThrowingProducerTimingRecorder extends ProducerTimingRecorder { 112 private final ProducerTimingRecorder delegate; 113 NonThrowingProducerTimingRecorder(ProducerTimingRecorder delegate)114 NonThrowingProducerTimingRecorder(ProducerTimingRecorder delegate) { 115 this.delegate = delegate; 116 } 117 118 @Override recordMethod(long startedNanos, long durationNanos)119 public void recordMethod(long startedNanos, long durationNanos) { 120 try { 121 delegate.recordMethod(startedNanos, durationNanos); 122 } catch (RuntimeException e) { 123 logProducerTimingRecorderMethodException(e, delegate, "recordMethod"); 124 } 125 } 126 127 @Override recordSuccess(long latencyNanos)128 public void recordSuccess(long latencyNanos) { 129 try { 130 delegate.recordSuccess(latencyNanos); 131 } catch (RuntimeException e) { 132 logProducerTimingRecorderMethodException(e, delegate, "recordSuccess"); 133 } 134 } 135 136 @Override recordFailure(Throwable exception, long latencyNanos)137 public void recordFailure(Throwable exception, long latencyNanos) { 138 try { 139 delegate.recordFailure(exception, latencyNanos); 140 } catch (RuntimeException e) { 141 logProducerTimingRecorderMethodException(e, delegate, "recordFailure"); 142 } 143 } 144 145 @Override recordSkip(Throwable exception)146 public void recordSkip(Throwable exception) { 147 try { 148 delegate.recordSkip(exception); 149 } catch (RuntimeException e) { 150 logProducerTimingRecorderMethodException(e, delegate, "recordSkip"); 151 } 152 } 153 } 154 155 /** 156 * A component recorder that delegates to several recorders, and catches and logs all exceptions 157 * that the delegates throw. 158 */ 159 private static final class DelegatingProductionComponentTimingRecorder 160 implements ProductionComponentTimingRecorder { 161 private final ImmutableList<ProductionComponentTimingRecorder> delegates; 162 DelegatingProductionComponentTimingRecorder( ImmutableList<ProductionComponentTimingRecorder> delegates)163 DelegatingProductionComponentTimingRecorder( 164 ImmutableList<ProductionComponentTimingRecorder> delegates) { 165 this.delegates = delegates; 166 } 167 168 @Override producerTimingRecorderFor(ProducerToken token)169 public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) { 170 ImmutableList.Builder<ProducerTimingRecorder> recordersBuilder = ImmutableList.builder(); 171 for (ProductionComponentTimingRecorder delegate : delegates) { 172 try { 173 ProducerTimingRecorder recorder = delegate.producerTimingRecorderFor(token); 174 if (recorder != null) { 175 recordersBuilder.add(recorder); 176 } 177 } catch (RuntimeException e) { 178 logProducerTimingRecorderForException(e, delegate, token); 179 } 180 } 181 ImmutableList<ProducerTimingRecorder> recorders = recordersBuilder.build(); 182 switch (recorders.size()) { 183 case 0: 184 return ProducerTimingRecorder.noOp(); 185 case 1: 186 return new NonThrowingProducerTimingRecorder(Iterables.getOnlyElement(recorders)); 187 default: 188 return new DelegatingProducerTimingRecorder(recorders); 189 } 190 } 191 192 static final class Factory implements ProductionComponentTimingRecorder.Factory { 193 private final ImmutableList<? extends ProductionComponentTimingRecorder.Factory> delegates; 194 Factory(Iterable<? extends ProductionComponentTimingRecorder.Factory> delegates)195 Factory(Iterable<? extends ProductionComponentTimingRecorder.Factory> delegates) { 196 this.delegates = ImmutableList.copyOf(delegates); 197 } 198 199 @Override create(Object component)200 public ProductionComponentTimingRecorder create(Object component) { 201 ImmutableList.Builder<ProductionComponentTimingRecorder> recordersBuilder = 202 ImmutableList.builder(); 203 for (ProductionComponentTimingRecorder.Factory delegate : delegates) { 204 try { 205 ProductionComponentTimingRecorder recorder = delegate.create(component); 206 if (recorder != null) { 207 recordersBuilder.add(recorder); 208 } 209 } catch (RuntimeException e) { 210 logCreateException(e, delegate, component); 211 } 212 } 213 ImmutableList<ProductionComponentTimingRecorder> recorders = recordersBuilder.build(); 214 switch (recorders.size()) { 215 case 0: 216 return noOpProductionComponentTimingRecorder(); 217 case 1: 218 return new NonThrowingProductionComponentTimingRecorder( 219 Iterables.getOnlyElement(recorders)); 220 default: 221 return new DelegatingProductionComponentTimingRecorder(recorders); 222 } 223 } 224 } 225 } 226 227 /** 228 * A producer recorder that delegates to several recorders, and catches and logs all exceptions 229 * that the delegates throw. 230 */ 231 private static final class DelegatingProducerTimingRecorder extends ProducerTimingRecorder { 232 private final ImmutableList<ProducerTimingRecorder> delegates; 233 DelegatingProducerTimingRecorder(ImmutableList<ProducerTimingRecorder> delegates)234 DelegatingProducerTimingRecorder(ImmutableList<ProducerTimingRecorder> delegates) { 235 this.delegates = delegates; 236 } 237 238 @Override recordMethod(long startedNanos, long durationNanos)239 public void recordMethod(long startedNanos, long durationNanos) { 240 for (ProducerTimingRecorder delegate : delegates) { 241 try { 242 delegate.recordMethod(startedNanos, durationNanos); 243 } catch (RuntimeException e) { 244 logProducerTimingRecorderMethodException(e, delegate, "recordMethod"); 245 } 246 } 247 } 248 249 @Override recordSuccess(long latencyNanos)250 public void recordSuccess(long latencyNanos) { 251 for (ProducerTimingRecorder delegate : delegates) { 252 try { 253 delegate.recordSuccess(latencyNanos); 254 } catch (RuntimeException e) { 255 logProducerTimingRecorderMethodException(e, delegate, "recordSuccess"); 256 } 257 } 258 } 259 260 @Override recordFailure(Throwable exception, long latencyNanos)261 public void recordFailure(Throwable exception, long latencyNanos) { 262 for (ProducerTimingRecorder delegate : delegates) { 263 try { 264 delegate.recordFailure(exception, latencyNanos); 265 } catch (RuntimeException e) { 266 logProducerTimingRecorderMethodException(e, delegate, "recordFailure"); 267 } 268 } 269 } 270 271 @Override recordSkip(Throwable exception)272 public void recordSkip(Throwable exception) { 273 for (ProducerTimingRecorder delegate : delegates) { 274 try { 275 delegate.recordSkip(exception); 276 } catch (RuntimeException e) { 277 logProducerTimingRecorderMethodException(e, delegate, "recordSkip"); 278 } 279 } 280 } 281 } 282 283 /** Returns a recorder factory that returns no-op component recorders. */ 284 public static ProductionComponentTimingRecorder.Factory noOpProductionComponentTimingRecorderFactory()285 noOpProductionComponentTimingRecorderFactory() { 286 return NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER_FACTORY; 287 } 288 289 /** Returns a component recorder that returns no-op producer recorders. */ noOpProductionComponentTimingRecorder()290 public static ProductionComponentTimingRecorder noOpProductionComponentTimingRecorder() { 291 return NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER; 292 } 293 294 private static final ProductionComponentTimingRecorder.Factory 295 NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER_FACTORY = 296 new ProductionComponentTimingRecorder.Factory() { 297 @Override 298 public ProductionComponentTimingRecorder create(Object component) { 299 return noOpProductionComponentTimingRecorder(); 300 } 301 }; 302 303 private static final ProductionComponentTimingRecorder 304 NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER = 305 new ProductionComponentTimingRecorder() { 306 @Override 307 public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) { 308 return ProducerTimingRecorder.noOp(); 309 } 310 }; 311 logCreateException( RuntimeException e, ProductionComponentTimingRecorder.Factory factory, Object component)312 private static void logCreateException( 313 RuntimeException e, ProductionComponentTimingRecorder.Factory factory, Object component) { 314 logger.log( 315 Level.SEVERE, 316 "RuntimeException while calling ProductionComponentTimingRecorder.Factory.create on" 317 + " factory " 318 + factory 319 + " with component " 320 + component, 321 e); 322 } 323 logProducerTimingRecorderForException( RuntimeException e, ProductionComponentTimingRecorder recorder, ProducerToken token)324 private static void logProducerTimingRecorderForException( 325 RuntimeException e, ProductionComponentTimingRecorder recorder, ProducerToken token) { 326 logger.log( 327 Level.SEVERE, 328 "RuntimeException while calling ProductionComponentTimingRecorder.producerTimingRecorderFor" 329 + "on recorder " 330 + recorder 331 + " with token " 332 + token, 333 e); 334 } 335 logProducerTimingRecorderMethodException( RuntimeException e, ProducerTimingRecorder recorder, String method)336 private static void logProducerTimingRecorderMethodException( 337 RuntimeException e, ProducerTimingRecorder recorder, String method) { 338 logger.log( 339 Level.SEVERE, 340 "RuntimeException while calling ProducerTimingRecorder." 341 + method 342 + " on recorder " 343 + recorder, 344 e); 345 } 346 TimingRecorders()347 private TimingRecorders() {} 348 } 349