From 4fe1df56bd74e9eec8847bdb15c5be51f528e8c8 Mon Sep 17 00:00:00 2001 From: cpovirk Date: Wed, 13 Dec 2023 11:40:36 -0800 Subject: [PATCH] Defer logger construction, which can be expensive under Android. RELNOTES=`util.concurrent`: Changed our implementations to avoid eagerly initializing loggers during class loading. PiperOrigin-RevId: 590666126 --- .../AbstractExecutionThreadService.java | 14 ++--- .../util/concurrent/AbstractFuture.java | 28 +++++----- .../concurrent/AbstractScheduledService.java | 13 +++-- .../util/concurrent/AggregateFuture.java | 5 +- .../util/concurrent/AggregateFutureState.java | 5 +- .../common/util/concurrent/ClosingFuture.java | 23 ++++---- .../concurrent/CycleDetectingLockFactory.java | 5 +- .../common/util/concurrent/ExecutionList.java | 15 +++-- .../util/concurrent/ImmediateFuture.java | 15 +++-- .../common/util/concurrent/LazyLogger.java | 56 +++++++++++++++++++ .../util/concurrent/ListenerCallQueue.java | 23 ++++---- .../util/concurrent/SequentialExecutor.java | 5 +- .../util/concurrent/ServiceManager.java | 40 +++++++------ .../concurrent/UncaughtExceptionHandlers.java | 11 ++-- .../AbstractExecutionThreadService.java | 14 ++--- .../util/concurrent/AbstractFuture.java | 28 +++++----- .../concurrent/AbstractScheduledService.java | 13 +++-- .../util/concurrent/AggregateFuture.java | 5 +- .../util/concurrent/AggregateFutureState.java | 5 +- .../common/util/concurrent/ClosingFuture.java | 23 ++++---- .../concurrent/CycleDetectingLockFactory.java | 5 +- .../common/util/concurrent/ExecutionList.java | 15 +++-- .../util/concurrent/ImmediateFuture.java | 15 +++-- .../common/util/concurrent/LazyLogger.java | 56 +++++++++++++++++++ .../util/concurrent/ListenerCallQueue.java | 23 ++++---- .../util/concurrent/SequentialExecutor.java | 5 +- .../util/concurrent/ServiceManager.java | 40 +++++++------ .../concurrent/UncaughtExceptionHandlers.java | 11 ++-- 28 files changed, 334 insertions(+), 182 deletions(-) create mode 100644 android/guava/src/com/google/common/util/concurrent/LazyLogger.java create mode 100644 guava/src/com/google/common/util/concurrent/LazyLogger.java diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java b/android/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java index b06647ea3c9e..43d4e5d5d54b 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java @@ -23,7 +23,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; -import java.util.logging.Logger; /** * Base class for services that can implement {@link #startUp}, {@link #run} and {@link #shutDown} @@ -37,8 +36,7 @@ @J2ktIncompatible @ElementTypesAreNonnullByDefault public abstract class AbstractExecutionThreadService implements Service { - private static final Logger logger = - Logger.getLogger(AbstractExecutionThreadService.class.getName()); + private static final LazyLogger logger = new LazyLogger(AbstractExecutionThreadService.class); /* use AbstractService for state management */ private final Service delegate = @@ -65,10 +63,12 @@ protected final void doStart() { // TODO(lukes): if guava ever moves to java7, this would be a good // candidate for a suppressed exception, or maybe we could generalize // Closer.Suppressor - logger.log( - Level.WARNING, - "Error while attempting to shut down the service after failure.", - ignored); + logger + .get() + .log( + Level.WARNING, + "Error while attempting to shut down the service after failure.", + ignored); } notifyFailed(t); return; diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java b/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java index e2232c272331..2f7c6fe12867 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java @@ -42,7 +42,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.locks.LockSupport; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -141,11 +140,7 @@ public final boolean cancel(boolean mayInterruptIfRunning) { } } - // Logger to log exceptions caught when running listeners. - // Holder class to delay initialization, for performance reasons. - private static final class LoggerHolder { - static final Logger log = Logger.getLogger(AbstractFuture.class.getName()); - } + static final LazyLogger log = new LazyLogger(AbstractFuture.class); // A heuristic for timed gets. If the remaining timeout is less than this, spin instead of // blocking. This value is what AbstractQueuedSynchronizer uses. @@ -192,9 +187,12 @@ private static final class LoggerHolder { // Log after all static init is finished; if an installed logger uses any Futures methods, it // shouldn't break in cases where reflection is missing/broken. if (thrownAtomicReferenceFieldUpdaterFailure != null) { - LoggerHolder.log.log(Level.SEVERE, "UnsafeAtomicHelper is broken!", thrownUnsafeFailure); - LoggerHolder.log.log( - Level.SEVERE, "SafeAtomicHelper is broken!", thrownAtomicReferenceFieldUpdaterFailure); + log.get().log(Level.SEVERE, "UnsafeAtomicHelper is broken!", thrownUnsafeFailure); + log.get() + .log( + Level.SEVERE, + "SafeAtomicHelper is broken!", + thrownAtomicReferenceFieldUpdaterFailure); } } @@ -1301,10 +1299,14 @@ private static void executeListener(Runnable runnable, Executor executor) { // Log it and keep going -- bad runnable and/or executor. Don't punish the other runnables if // we're given a bad one. We only catch RuntimeException because we want Errors to propagate // up. - LoggerHolder.log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + runnable + " with executor " + executor, - e); + log.get() + .log( + Level.SEVERE, + "RuntimeException while executing runnable " + + runnable + + " with executor " + + executor, + e); } } diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java b/android/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java index 826374bad133..594496884888 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java @@ -37,7 +37,6 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -102,7 +101,7 @@ @J2ktIncompatible @ElementTypesAreNonnullByDefault public abstract class AbstractScheduledService implements Service { - private static final Logger logger = Logger.getLogger(AbstractScheduledService.class.getName()); + private static final LazyLogger logger = new LazyLogger(AbstractScheduledService.class); /** * A scheduler defines the policy for how the {@link AbstractScheduledService} should run its @@ -209,10 +208,12 @@ public void run() { shutDown(); } catch (Exception ignored) { restoreInterruptIfIsInterruptedException(ignored); - logger.log( - Level.WARNING, - "Error while attempting to shut down the service after failure.", - ignored); + logger + .get() + .log( + Level.WARNING, + "Error while attempting to shut down the service after failure.", + ignored); } notifyFailed(t); // requireNonNull is safe now, just as it was above. diff --git a/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java b/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java index ec96cbb89e41..6a8ae32f7c00 100644 --- a/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -44,7 +43,7 @@ @ElementTypesAreNonnullByDefault abstract class AggregateFuture extends AggregateFutureState { - private static final Logger logger = Logger.getLogger(AggregateFuture.class.getName()); + private static final LazyLogger logger = new LazyLogger(AggregateFuture.class); /** * The input futures. After {@link #init}, this field is read only by {@link #afterDone()} (to @@ -230,7 +229,7 @@ private static void log(Throwable throwable) { (throwable instanceof Error) ? "Input Future failed with Error" : "Got more than one input Future failure. Logging failures after the first"; - logger.log(SEVERE, message, throwable); + logger.get().log(SEVERE, message, throwable); } @Override diff --git a/android/guava/src/com/google/common/util/concurrent/AggregateFutureState.java b/android/guava/src/com/google/common/util/concurrent/AggregateFutureState.java index 8bda635b7479..eef960b85b96 100644 --- a/android/guava/src/com/google/common/util/concurrent/AggregateFutureState.java +++ b/android/guava/src/com/google/common/util/concurrent/AggregateFutureState.java @@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -51,7 +50,7 @@ abstract class AggregateFutureState private static final AtomicHelper ATOMIC_HELPER; - private static final Logger log = Logger.getLogger(AggregateFutureState.class.getName()); + private static final LazyLogger log = new LazyLogger(AggregateFutureState.class); static { AtomicHelper helper; @@ -73,7 +72,7 @@ abstract class AggregateFutureState // Log after all static init is finished; if an installed logger uses any Futures methods, it // shouldn't break in cases where reflection is missing/broken. if (thrownReflectionFailure != null) { - log.log(Level.SEVERE, "SafeAtomicHelper is broken!", thrownReflectionFailure); + log.get().log(Level.SEVERE, "SafeAtomicHelper is broken!", thrownReflectionFailure); } } diff --git a/android/guava/src/com/google/common/util/concurrent/ClosingFuture.java b/android/guava/src/com/google/common/util/concurrent/ClosingFuture.java index 9b3f89f36ce4..c43074b3c7db 100644 --- a/android/guava/src/com/google/common/util/concurrent/ClosingFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ClosingFuture.java @@ -58,7 +58,6 @@ import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -196,7 +195,7 @@ // TODO(dpb): GWT compatibility. public final class ClosingFuture { - private static final Logger logger = Logger.getLogger(ClosingFuture.class.getName()); + private static final LazyLogger logger = new LazyLogger(ClosingFuture.class); /** * An object that can capture objects to be closed later, when a {@link ClosingFuture} pipeline is @@ -1018,7 +1017,7 @@ public String toString() { */ public FluentFuture finishToFuture() { if (compareAndUpdateState(OPEN, WILL_CLOSE)) { - logger.log(FINER, "will close {0}", this); + logger.get().log(FINER, "will close {0}", this); future.addListener( new Runnable() { @Override @@ -1118,7 +1117,7 @@ public void run() { */ @CanIgnoreReturnValue public boolean cancel(boolean mayInterruptIfRunning) { - logger.log(FINER, "cancelling {0}", this); + logger.get().log(FINER, "cancelling {0}", this); boolean cancelled = future.cancel(mayInterruptIfRunning); if (cancelled) { close(); @@ -1127,7 +1126,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { } private void close() { - logger.log(FINER, "closing {0}", this); + logger.get().log(FINER, "closing {0}", this); closeables.close(); } @@ -2131,7 +2130,7 @@ public String toString() { @Override protected void finalize() { if (state.get().equals(OPEN)) { - logger.log(SEVERE, "Uh oh! An open ClosingFuture has leaked and will close: {0}", this); + logger.get().log(SEVERE, "Uh oh! An open ClosingFuture has leaked and will close: {0}", this); FluentFuture unused = finishToFuture(); } } @@ -2155,13 +2154,17 @@ private static void closeQuietly(@CheckForNull final Closeable closeable, Execut * that we have to account for sneaky checked exception. */ restoreInterruptIfIsInterruptedException(e); - logger.log(WARNING, "thrown by close()", e); + logger.get().log(WARNING, "thrown by close()", e); } }); } catch (RejectedExecutionException e) { - if (logger.isLoggable(WARNING)) { - logger.log( - WARNING, String.format("while submitting close to %s; will close inline", executor), e); + if (logger.get().isLoggable(WARNING)) { + logger + .get() + .log( + WARNING, + String.format("while submitting close to %s; will close inline", executor), + e); } closeQuietly(closeable, directExecutor()); } diff --git a/android/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java b/android/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java index 0ec799cc493d..331913b170bb 100644 --- a/android/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java +++ b/android/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java @@ -41,7 +41,6 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; /** @@ -211,7 +210,7 @@ public void handlePotentialDeadlock(PotentialDeadlockException e) { WARN { @Override public void handlePotentialDeadlock(PotentialDeadlockException e) { - logger.log(Level.SEVERE, "Detected potential deadlock", e); + logger.get().log(Level.SEVERE, "Detected potential deadlock", e); } }, @@ -447,7 +446,7 @@ public ReentrantReadWriteLock newReentrantReadWriteLock(E rank, boolean fair) { //////// Implementation ///////// - private static final Logger logger = Logger.getLogger(CycleDetectingLockFactory.class.getName()); + private static final LazyLogger logger = new LazyLogger(CycleDetectingLockFactory.class); final Policy policy; diff --git a/android/guava/src/com/google/common/util/concurrent/ExecutionList.java b/android/guava/src/com/google/common/util/concurrent/ExecutionList.java index 5f9dfc28ac21..10b933aa193b 100644 --- a/android/guava/src/com/google/common/util/concurrent/ExecutionList.java +++ b/android/guava/src/com/google/common/util/concurrent/ExecutionList.java @@ -21,7 +21,6 @@ import com.google.errorprone.annotations.concurrent.GuardedBy; import java.util.concurrent.Executor; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; /** @@ -45,7 +44,7 @@ @ElementTypesAreNonnullByDefault public final class ExecutionList { /** Logger to log exceptions caught when running runnables. */ - private static final Logger log = Logger.getLogger(ExecutionList.class.getName()); + private static final LazyLogger log = new LazyLogger(ExecutionList.class); /** * The runnable, executor pairs to execute. This acts as a stack threaded through the {@link @@ -148,10 +147,14 @@ private static void executeListener(Runnable runnable, Executor executor) { // Log it and keep going -- bad runnable and/or executor. Don't punish the other runnables if // we're given a bad one. We only catch RuntimeException because we want Errors to propagate // up. - log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + runnable + " with executor " + executor, - e); + log.get() + .log( + Level.SEVERE, + "RuntimeException while executing runnable " + + runnable + + " with executor " + + executor, + e); } } diff --git a/android/guava/src/com/google/common/util/concurrent/ImmediateFuture.java b/android/guava/src/com/google/common/util/concurrent/ImmediateFuture.java index 0a6e2303b877..62ae63eaaa4c 100644 --- a/android/guava/src/com/google/common/util/concurrent/ImmediateFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ImmediateFuture.java @@ -22,7 +22,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -33,7 +32,7 @@ class ImmediateFuture implements ListenableFuture { static final ListenableFuture NULL = new ImmediateFuture<@Nullable Object>(null); - private static final Logger log = Logger.getLogger(ImmediateFuture.class.getName()); + private static final LazyLogger log = new LazyLogger(ImmediateFuture.class); @ParametricNullness private final V value; @@ -51,10 +50,14 @@ public void addListener(Runnable listener, Executor executor) { } catch (Exception e) { // sneaky checked exception // ListenableFuture's contract is that it will not throw unchecked exceptions, so log the bad // runnable and/or executor and swallow it. - log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + listener + " with executor " + executor, - e); + log.get() + .log( + Level.SEVERE, + "RuntimeException while executing runnable " + + listener + + " with executor " + + executor, + e); } } diff --git a/android/guava/src/com/google/common/util/concurrent/LazyLogger.java b/android/guava/src/com/google/common/util/concurrent/LazyLogger.java new file mode 100644 index 000000000000..9ae0f784e57e --- /dev/null +++ b/android/guava/src/com/google/common/util/concurrent/LazyLogger.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtCompatible; +import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** A holder for a {@link Logger} that is initialized only when requested. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class LazyLogger { + private final String loggerName; + private volatile @Nullable Logger logger; + + LazyLogger(Class ownerOfLogger) { + this.loggerName = ownerOfLogger.getName(); + } + + Logger get() { + /* + * We use double-checked locking. We could the try racy single-check idiom, but that would + * depend on Logger not contain mutable state. + * + * We could use Suppliers.memoizingSupplier here, but I micro-optimized to this implementation + * to avoid the extra class for the lambda (and maybe more for memoizingSupplier itself) and the + * indirection. + * + * One thing to *avoid* is a change to make each Logger user use memoizingSupplier directly: + * That may introduce an extra class for each lambda (currently a dozen). + */ + Logger local = logger; + if (local != null) { + return local; + } + synchronized (this) { + local = logger; + if (local != null) { + return local; + } + return logger = Logger.getLogger(loggerName); + } + } +} diff --git a/android/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java b/android/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java index 897c95d5e187..e6284e1c7ded 100644 --- a/android/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java +++ b/android/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java @@ -27,7 +27,6 @@ import java.util.Queue; import java.util.concurrent.Executor; import java.util.logging.Level; -import java.util.logging.Logger; /** * A list of listeners for implementing a concurrency friendly observable object. @@ -58,7 +57,7 @@ @ElementTypesAreNonnullByDefault final class ListenerCallQueue { // TODO(cpovirk): consider using the logger associated with listener.getClass(). - private static final Logger logger = Logger.getLogger(ListenerCallQueue.class.getName()); + private static final LazyLogger logger = new LazyLogger(ListenerCallQueue.class); // TODO(chrisn): promote AppendOnlyCollection for use here. private final List> listeners = @@ -177,10 +176,12 @@ void dispatch() { isThreadScheduled = false; } // Log it and keep going. - logger.log( - Level.SEVERE, - "Exception while running callbacks for " + listener + " on " + executor, - e); + logger + .get() + .log( + Level.SEVERE, + "Exception while running callbacks for " + listener + " on " + executor, + e); throw e; } } @@ -210,10 +211,12 @@ public void run() { nextToRun.call(listener); } catch (Exception e) { // sneaky checked exception // Log it and keep going. - logger.log( - Level.SEVERE, - "Exception while executing callback: " + listener + " " + nextLabel, - e); + logger + .get() + .log( + Level.SEVERE, + "Exception while executing callback: " + listener + " " + nextLabel, + e); } } } finally { diff --git a/android/guava/src/com/google/common/util/concurrent/SequentialExecutor.java b/android/guava/src/com/google/common/util/concurrent/SequentialExecutor.java index 224bd1f120cb..ebc33178d0c5 100644 --- a/android/guava/src/com/google/common/util/concurrent/SequentialExecutor.java +++ b/android/guava/src/com/google/common/util/concurrent/SequentialExecutor.java @@ -31,7 +31,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; /** @@ -52,7 +51,7 @@ @GwtIncompatible @ElementTypesAreNonnullByDefault final class SequentialExecutor implements Executor { - private static final Logger log = Logger.getLogger(SequentialExecutor.class.getName()); + private static final LazyLogger log = new LazyLogger(SequentialExecutor.class); enum WorkerRunningState { /** Runnable is not running and not queued for execution */ @@ -238,7 +237,7 @@ private void workOnQueue() { try { task.run(); } catch (Exception e) { // sneaky checked exception - log.log(Level.SEVERE, "Exception while executing runnable " + task, e); + log.get().log(Level.SEVERE, "Exception while executing runnable " + task, e); } finally { task = null; } diff --git a/android/guava/src/com/google/common/util/concurrent/ServiceManager.java b/android/guava/src/com/google/common/util/concurrent/ServiceManager.java index dd4828cb876f..38727f504247 100644 --- a/android/guava/src/com/google/common/util/concurrent/ServiceManager.java +++ b/android/guava/src/com/google/common/util/concurrent/ServiceManager.java @@ -62,7 +62,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; -import java.util.logging.Logger; /** * A manager for monitoring and controlling a set of {@linkplain Service services}. This class @@ -122,7 +121,7 @@ @GwtIncompatible @ElementTypesAreNonnullByDefault public final class ServiceManager implements ServiceManagerBridge { - private static final Logger logger = Logger.getLogger(ServiceManager.class.getName()); + private static final LazyLogger logger = new LazyLogger(ServiceManager.class); private static final ListenerCallQueue.Event HEALTHY_EVENT = new ListenerCallQueue.Event() { @Override @@ -205,10 +204,13 @@ public ServiceManager(Iterable services) { if (copy.isEmpty()) { // Having no services causes the manager to behave strangely. Notably, listeners are never // fired. To avoid this we substitute a placeholder service. - logger.log( - Level.WARNING, - "ServiceManager configured with no services. Is your application configured properly?", - new EmptyServiceManagerWarning()); + logger + .get() + .log( + Level.WARNING, + "ServiceManager configured with no services. Is your application configured" + + " properly?", + new EmptyServiceManagerWarning()); copy = ImmutableList.of(new NoOpService()); } this.state = new ServiceManagerState(copy); @@ -275,7 +277,7 @@ public ServiceManager startAsync() { // service or listener). Our contract says it is safe to call this method if // all services were NEW when it was called, and this has already been verified above, so we // don't propagate the exception. - logger.log(Level.WARNING, "Unable to start Service " + service, e); + logger.get().log(Level.WARNING, "Unable to start Service " + service, e); } } return this; @@ -661,7 +663,7 @@ void transitionService(final Service service, State from, State to) { // N.B. if we miss the STARTING event then we may never record a startup time. stopwatch.stop(); if (!(service instanceof NoOpService)) { - logger.log(Level.FINE, "Started {0} in {1}.", new Object[] {service, stopwatch}); + logger.get().log(Level.FINE, "Started {0} in {1}.", new Object[] {service, stopwatch}); } } // Queue our listeners @@ -750,7 +752,7 @@ public void starting() { if (state != null) { state.transitionService(service, NEW, STARTING); if (!(service instanceof NoOpService)) { - logger.log(Level.FINE, "Starting {0}.", service); + logger.get().log(Level.FINE, "Starting {0}.", service); } } } @@ -776,10 +778,12 @@ public void terminated(State from) { ServiceManagerState state = this.state.get(); if (state != null) { if (!(service instanceof NoOpService)) { - logger.log( - Level.FINE, - "Service {0} has terminated. Previous state was: {1}", - new Object[] {service, from}); + logger + .get() + .log( + Level.FINE, + "Service {0} has terminated. Previous state was: {1}", + new Object[] {service, from}); } state.transitionService(service, from, TERMINATED); } @@ -793,10 +797,12 @@ public void failed(State from, Throwable failure) { // there is a higher likelihood that the cause will be in the logs. boolean log = !(service instanceof NoOpService); if (log) { - logger.log( - Level.SEVERE, - "Service " + service + " has failed in the " + from + " state.", - failure); + logger + .get() + .log( + Level.SEVERE, + "Service " + service + " has failed in the " + from + " state.", + failure); } state.transitionService(service, from, FAILED); } diff --git a/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java b/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java index 1dc1094d4f94..14df42aea235 100644 --- a/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java +++ b/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java @@ -21,7 +21,6 @@ import com.google.common.annotations.VisibleForTesting; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Locale; -import java.util.logging.Logger; /** * Factories for {@link UncaughtExceptionHandler} instances. @@ -57,7 +56,7 @@ public static UncaughtExceptionHandler systemExit() { @VisibleForTesting static final class Exiter implements UncaughtExceptionHandler { - private static final Logger logger = Logger.getLogger(Exiter.class.getName()); + private static final LazyLogger logger = new LazyLogger(Exiter.class); private final Runtime runtime; @@ -68,8 +67,12 @@ static final class Exiter implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { try { - logger.log( - SEVERE, String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), e); + logger + .get() + .log( + SEVERE, + String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), + e); } catch (RuntimeException | Error errorInLogging) { // If logging fails, e.g. due to missing memory, at least try to log the // message and the cause for the failed logging. diff --git a/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java b/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java index c52f9d4561b8..512d809551d5 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java +++ b/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java @@ -24,7 +24,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; -import java.util.logging.Logger; /** * Base class for services that can implement {@link #startUp}, {@link #run} and {@link #shutDown} @@ -38,8 +37,7 @@ @J2ktIncompatible @ElementTypesAreNonnullByDefault public abstract class AbstractExecutionThreadService implements Service { - private static final Logger logger = - Logger.getLogger(AbstractExecutionThreadService.class.getName()); + private static final LazyLogger logger = new LazyLogger(AbstractExecutionThreadService.class); /* use AbstractService for state management */ private final Service delegate = @@ -66,10 +64,12 @@ protected final void doStart() { // TODO(lukes): if guava ever moves to java7, this would be a good // candidate for a suppressed exception, or maybe we could generalize // Closer.Suppressor - logger.log( - Level.WARNING, - "Error while attempting to shut down the service after failure.", - ignored); + logger + .get() + .log( + Level.WARNING, + "Error while attempting to shut down the service after failure.", + ignored); } notifyFailed(t); return; diff --git a/guava/src/com/google/common/util/concurrent/AbstractFuture.java b/guava/src/com/google/common/util/concurrent/AbstractFuture.java index a044b39ae885..eee352c5b48c 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractFuture.java +++ b/guava/src/com/google/common/util/concurrent/AbstractFuture.java @@ -42,7 +42,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.locks.LockSupport; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -141,11 +140,7 @@ public final boolean cancel(boolean mayInterruptIfRunning) { } } - // Logger to log exceptions caught when running listeners. - // Holder class to delay initialization, for performance reasons. - private static final class LoggerHolder { - static final Logger log = Logger.getLogger(AbstractFuture.class.getName()); - } + static final LazyLogger log = new LazyLogger(AbstractFuture.class); // A heuristic for timed gets. If the remaining timeout is less than this, spin instead of // blocking. This value is what AbstractQueuedSynchronizer uses. @@ -192,9 +187,12 @@ private static final class LoggerHolder { // Log after all static init is finished; if an installed logger uses any Futures methods, it // shouldn't break in cases where reflection is missing/broken. if (thrownAtomicReferenceFieldUpdaterFailure != null) { - LoggerHolder.log.log(Level.SEVERE, "UnsafeAtomicHelper is broken!", thrownUnsafeFailure); - LoggerHolder.log.log( - Level.SEVERE, "SafeAtomicHelper is broken!", thrownAtomicReferenceFieldUpdaterFailure); + log.get().log(Level.SEVERE, "UnsafeAtomicHelper is broken!", thrownUnsafeFailure); + log.get() + .log( + Level.SEVERE, + "SafeAtomicHelper is broken!", + thrownAtomicReferenceFieldUpdaterFailure); } } @@ -1301,10 +1299,14 @@ private static void executeListener(Runnable runnable, Executor executor) { // Log it and keep going -- bad runnable and/or executor. Don't punish the other runnables if // we're given a bad one. We only catch RuntimeException because we want Errors to propagate // up. - LoggerHolder.log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + runnable + " with executor " + executor, - e); + log.get() + .log( + Level.SEVERE, + "RuntimeException while executing runnable " + + runnable + + " with executor " + + executor, + e); } } diff --git a/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java b/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java index d6865f0a8c4a..2529d4749469 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java +++ b/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java @@ -40,7 +40,6 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -105,7 +104,7 @@ @J2ktIncompatible @ElementTypesAreNonnullByDefault public abstract class AbstractScheduledService implements Service { - private static final Logger logger = Logger.getLogger(AbstractScheduledService.class.getName()); + private static final LazyLogger logger = new LazyLogger(AbstractScheduledService.class); /** * A scheduler defines the policy for how the {@link AbstractScheduledService} should run its @@ -239,10 +238,12 @@ public void run() { shutDown(); } catch (Exception ignored) { restoreInterruptIfIsInterruptedException(ignored); - logger.log( - Level.WARNING, - "Error while attempting to shut down the service after failure.", - ignored); + logger + .get() + .log( + Level.WARNING, + "Error while attempting to shut down the service after failure.", + ignored); } notifyFailed(t); // requireNonNull is safe now, just as it was above. diff --git a/guava/src/com/google/common/util/concurrent/AggregateFuture.java b/guava/src/com/google/common/util/concurrent/AggregateFuture.java index ec96cbb89e41..6a8ae32f7c00 100644 --- a/guava/src/com/google/common/util/concurrent/AggregateFuture.java +++ b/guava/src/com/google/common/util/concurrent/AggregateFuture.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -44,7 +43,7 @@ @ElementTypesAreNonnullByDefault abstract class AggregateFuture extends AggregateFutureState { - private static final Logger logger = Logger.getLogger(AggregateFuture.class.getName()); + private static final LazyLogger logger = new LazyLogger(AggregateFuture.class); /** * The input futures. After {@link #init}, this field is read only by {@link #afterDone()} (to @@ -230,7 +229,7 @@ private static void log(Throwable throwable) { (throwable instanceof Error) ? "Input Future failed with Error" : "Got more than one input Future failure. Logging failures after the first"; - logger.log(SEVERE, message, throwable); + logger.get().log(SEVERE, message, throwable); } @Override diff --git a/guava/src/com/google/common/util/concurrent/AggregateFutureState.java b/guava/src/com/google/common/util/concurrent/AggregateFutureState.java index 8bda635b7479..eef960b85b96 100644 --- a/guava/src/com/google/common/util/concurrent/AggregateFutureState.java +++ b/guava/src/com/google/common/util/concurrent/AggregateFutureState.java @@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -51,7 +50,7 @@ abstract class AggregateFutureState private static final AtomicHelper ATOMIC_HELPER; - private static final Logger log = Logger.getLogger(AggregateFutureState.class.getName()); + private static final LazyLogger log = new LazyLogger(AggregateFutureState.class); static { AtomicHelper helper; @@ -73,7 +72,7 @@ abstract class AggregateFutureState // Log after all static init is finished; if an installed logger uses any Futures methods, it // shouldn't break in cases where reflection is missing/broken. if (thrownReflectionFailure != null) { - log.log(Level.SEVERE, "SafeAtomicHelper is broken!", thrownReflectionFailure); + log.get().log(Level.SEVERE, "SafeAtomicHelper is broken!", thrownReflectionFailure); } } diff --git a/guava/src/com/google/common/util/concurrent/ClosingFuture.java b/guava/src/com/google/common/util/concurrent/ClosingFuture.java index 46c5cd187a15..efdf56d8aa52 100644 --- a/guava/src/com/google/common/util/concurrent/ClosingFuture.java +++ b/guava/src/com/google/common/util/concurrent/ClosingFuture.java @@ -58,7 +58,6 @@ import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -196,7 +195,7 @@ // TODO(dpb): GWT compatibility. public final class ClosingFuture { - private static final Logger logger = Logger.getLogger(ClosingFuture.class.getName()); + private static final LazyLogger logger = new LazyLogger(ClosingFuture.class); /** * An object that can capture objects to be closed later, when a {@link ClosingFuture} pipeline is @@ -1021,7 +1020,7 @@ public String toString() { */ public FluentFuture finishToFuture() { if (compareAndUpdateState(OPEN, WILL_CLOSE)) { - logger.log(FINER, "will close {0}", this); + logger.get().log(FINER, "will close {0}", this); future.addListener( new Runnable() { @Override @@ -1121,7 +1120,7 @@ public void run() { */ @CanIgnoreReturnValue public boolean cancel(boolean mayInterruptIfRunning) { - logger.log(FINER, "cancelling {0}", this); + logger.get().log(FINER, "cancelling {0}", this); boolean cancelled = future.cancel(mayInterruptIfRunning); if (cancelled) { close(); @@ -1130,7 +1129,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { } private void close() { - logger.log(FINER, "closing {0}", this); + logger.get().log(FINER, "closing {0}", this); closeables.close(); } @@ -2144,7 +2143,7 @@ public String toString() { @Override protected void finalize() { if (state.get().equals(OPEN)) { - logger.log(SEVERE, "Uh oh! An open ClosingFuture has leaked and will close: {0}", this); + logger.get().log(SEVERE, "Uh oh! An open ClosingFuture has leaked and will close: {0}", this); FluentFuture unused = finishToFuture(); } } @@ -2168,13 +2167,17 @@ private static void closeQuietly(@CheckForNull final AutoCloseable closeable, Ex * that we have to account for sneaky checked exception. */ restoreInterruptIfIsInterruptedException(e); - logger.log(WARNING, "thrown by close()", e); + logger.get().log(WARNING, "thrown by close()", e); } }); } catch (RejectedExecutionException e) { - if (logger.isLoggable(WARNING)) { - logger.log( - WARNING, String.format("while submitting close to %s; will close inline", executor), e); + if (logger.get().isLoggable(WARNING)) { + logger + .get() + .log( + WARNING, + String.format("while submitting close to %s; will close inline", executor), + e); } closeQuietly(closeable, directExecutor()); } diff --git a/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java b/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java index 0ec799cc493d..331913b170bb 100644 --- a/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java +++ b/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java @@ -41,7 +41,6 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; /** @@ -211,7 +210,7 @@ public void handlePotentialDeadlock(PotentialDeadlockException e) { WARN { @Override public void handlePotentialDeadlock(PotentialDeadlockException e) { - logger.log(Level.SEVERE, "Detected potential deadlock", e); + logger.get().log(Level.SEVERE, "Detected potential deadlock", e); } }, @@ -447,7 +446,7 @@ public ReentrantReadWriteLock newReentrantReadWriteLock(E rank, boolean fair) { //////// Implementation ///////// - private static final Logger logger = Logger.getLogger(CycleDetectingLockFactory.class.getName()); + private static final LazyLogger logger = new LazyLogger(CycleDetectingLockFactory.class); final Policy policy; diff --git a/guava/src/com/google/common/util/concurrent/ExecutionList.java b/guava/src/com/google/common/util/concurrent/ExecutionList.java index 5f9dfc28ac21..10b933aa193b 100644 --- a/guava/src/com/google/common/util/concurrent/ExecutionList.java +++ b/guava/src/com/google/common/util/concurrent/ExecutionList.java @@ -21,7 +21,6 @@ import com.google.errorprone.annotations.concurrent.GuardedBy; import java.util.concurrent.Executor; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; /** @@ -45,7 +44,7 @@ @ElementTypesAreNonnullByDefault public final class ExecutionList { /** Logger to log exceptions caught when running runnables. */ - private static final Logger log = Logger.getLogger(ExecutionList.class.getName()); + private static final LazyLogger log = new LazyLogger(ExecutionList.class); /** * The runnable, executor pairs to execute. This acts as a stack threaded through the {@link @@ -148,10 +147,14 @@ private static void executeListener(Runnable runnable, Executor executor) { // Log it and keep going -- bad runnable and/or executor. Don't punish the other runnables if // we're given a bad one. We only catch RuntimeException because we want Errors to propagate // up. - log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + runnable + " with executor " + executor, - e); + log.get() + .log( + Level.SEVERE, + "RuntimeException while executing runnable " + + runnable + + " with executor " + + executor, + e); } } diff --git a/guava/src/com/google/common/util/concurrent/ImmediateFuture.java b/guava/src/com/google/common/util/concurrent/ImmediateFuture.java index 0a6e2303b877..62ae63eaaa4c 100644 --- a/guava/src/com/google/common/util/concurrent/ImmediateFuture.java +++ b/guava/src/com/google/common/util/concurrent/ImmediateFuture.java @@ -22,7 +22,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -33,7 +32,7 @@ class ImmediateFuture implements ListenableFuture { static final ListenableFuture NULL = new ImmediateFuture<@Nullable Object>(null); - private static final Logger log = Logger.getLogger(ImmediateFuture.class.getName()); + private static final LazyLogger log = new LazyLogger(ImmediateFuture.class); @ParametricNullness private final V value; @@ -51,10 +50,14 @@ public void addListener(Runnable listener, Executor executor) { } catch (Exception e) { // sneaky checked exception // ListenableFuture's contract is that it will not throw unchecked exceptions, so log the bad // runnable and/or executor and swallow it. - log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + listener + " with executor " + executor, - e); + log.get() + .log( + Level.SEVERE, + "RuntimeException while executing runnable " + + listener + + " with executor " + + executor, + e); } } diff --git a/guava/src/com/google/common/util/concurrent/LazyLogger.java b/guava/src/com/google/common/util/concurrent/LazyLogger.java new file mode 100644 index 000000000000..9ae0f784e57e --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/LazyLogger.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtCompatible; +import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** A holder for a {@link Logger} that is initialized only when requested. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class LazyLogger { + private final String loggerName; + private volatile @Nullable Logger logger; + + LazyLogger(Class ownerOfLogger) { + this.loggerName = ownerOfLogger.getName(); + } + + Logger get() { + /* + * We use double-checked locking. We could the try racy single-check idiom, but that would + * depend on Logger not contain mutable state. + * + * We could use Suppliers.memoizingSupplier here, but I micro-optimized to this implementation + * to avoid the extra class for the lambda (and maybe more for memoizingSupplier itself) and the + * indirection. + * + * One thing to *avoid* is a change to make each Logger user use memoizingSupplier directly: + * That may introduce an extra class for each lambda (currently a dozen). + */ + Logger local = logger; + if (local != null) { + return local; + } + synchronized (this) { + local = logger; + if (local != null) { + return local; + } + return logger = Logger.getLogger(loggerName); + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java b/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java index 897c95d5e187..e6284e1c7ded 100644 --- a/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java +++ b/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java @@ -27,7 +27,6 @@ import java.util.Queue; import java.util.concurrent.Executor; import java.util.logging.Level; -import java.util.logging.Logger; /** * A list of listeners for implementing a concurrency friendly observable object. @@ -58,7 +57,7 @@ @ElementTypesAreNonnullByDefault final class ListenerCallQueue { // TODO(cpovirk): consider using the logger associated with listener.getClass(). - private static final Logger logger = Logger.getLogger(ListenerCallQueue.class.getName()); + private static final LazyLogger logger = new LazyLogger(ListenerCallQueue.class); // TODO(chrisn): promote AppendOnlyCollection for use here. private final List> listeners = @@ -177,10 +176,12 @@ void dispatch() { isThreadScheduled = false; } // Log it and keep going. - logger.log( - Level.SEVERE, - "Exception while running callbacks for " + listener + " on " + executor, - e); + logger + .get() + .log( + Level.SEVERE, + "Exception while running callbacks for " + listener + " on " + executor, + e); throw e; } } @@ -210,10 +211,12 @@ public void run() { nextToRun.call(listener); } catch (Exception e) { // sneaky checked exception // Log it and keep going. - logger.log( - Level.SEVERE, - "Exception while executing callback: " + listener + " " + nextLabel, - e); + logger + .get() + .log( + Level.SEVERE, + "Exception while executing callback: " + listener + " " + nextLabel, + e); } } } finally { diff --git a/guava/src/com/google/common/util/concurrent/SequentialExecutor.java b/guava/src/com/google/common/util/concurrent/SequentialExecutor.java index 224bd1f120cb..ebc33178d0c5 100644 --- a/guava/src/com/google/common/util/concurrent/SequentialExecutor.java +++ b/guava/src/com/google/common/util/concurrent/SequentialExecutor.java @@ -31,7 +31,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.CheckForNull; /** @@ -52,7 +51,7 @@ @GwtIncompatible @ElementTypesAreNonnullByDefault final class SequentialExecutor implements Executor { - private static final Logger log = Logger.getLogger(SequentialExecutor.class.getName()); + private static final LazyLogger log = new LazyLogger(SequentialExecutor.class); enum WorkerRunningState { /** Runnable is not running and not queued for execution */ @@ -238,7 +237,7 @@ private void workOnQueue() { try { task.run(); } catch (Exception e) { // sneaky checked exception - log.log(Level.SEVERE, "Exception while executing runnable " + task, e); + log.get().log(Level.SEVERE, "Exception while executing runnable " + task, e); } finally { task = null; } diff --git a/guava/src/com/google/common/util/concurrent/ServiceManager.java b/guava/src/com/google/common/util/concurrent/ServiceManager.java index 42652c96768a..0bda0afecc4b 100644 --- a/guava/src/com/google/common/util/concurrent/ServiceManager.java +++ b/guava/src/com/google/common/util/concurrent/ServiceManager.java @@ -65,7 +65,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; -import java.util.logging.Logger; /** * A manager for monitoring and controlling a set of {@linkplain Service services}. This class @@ -125,7 +124,7 @@ @GwtIncompatible @ElementTypesAreNonnullByDefault public final class ServiceManager implements ServiceManagerBridge { - private static final Logger logger = Logger.getLogger(ServiceManager.class.getName()); + private static final LazyLogger logger = new LazyLogger(ServiceManager.class); private static final ListenerCallQueue.Event HEALTHY_EVENT = new ListenerCallQueue.Event() { @Override @@ -208,10 +207,13 @@ public ServiceManager(Iterable services) { if (copy.isEmpty()) { // Having no services causes the manager to behave strangely. Notably, listeners are never // fired. To avoid this we substitute a placeholder service. - logger.log( - Level.WARNING, - "ServiceManager configured with no services. Is your application configured properly?", - new EmptyServiceManagerWarning()); + logger + .get() + .log( + Level.WARNING, + "ServiceManager configured with no services. Is your application configured" + + " properly?", + new EmptyServiceManagerWarning()); copy = ImmutableList.of(new NoOpService()); } this.state = new ServiceManagerState(copy); @@ -278,7 +280,7 @@ public ServiceManager startAsync() { // service or listener). Our contract says it is safe to call this method if // all services were NEW when it was called, and this has already been verified above, so we // don't propagate the exception. - logger.log(Level.WARNING, "Unable to start Service " + service, e); + logger.get().log(Level.WARNING, "Unable to start Service " + service, e); } } return this; @@ -706,7 +708,7 @@ void transitionService(final Service service, State from, State to) { // N.B. if we miss the STARTING event then we may never record a startup time. stopwatch.stop(); if (!(service instanceof NoOpService)) { - logger.log(Level.FINE, "Started {0} in {1}.", new Object[] {service, stopwatch}); + logger.get().log(Level.FINE, "Started {0} in {1}.", new Object[] {service, stopwatch}); } } // Queue our listeners @@ -798,7 +800,7 @@ public void starting() { if (state != null) { state.transitionService(service, NEW, STARTING); if (!(service instanceof NoOpService)) { - logger.log(Level.FINE, "Starting {0}.", service); + logger.get().log(Level.FINE, "Starting {0}.", service); } } } @@ -824,10 +826,12 @@ public void terminated(State from) { ServiceManagerState state = this.state.get(); if (state != null) { if (!(service instanceof NoOpService)) { - logger.log( - Level.FINE, - "Service {0} has terminated. Previous state was: {1}", - new Object[] {service, from}); + logger + .get() + .log( + Level.FINE, + "Service {0} has terminated. Previous state was: {1}", + new Object[] {service, from}); } state.transitionService(service, from, TERMINATED); } @@ -846,10 +850,12 @@ public void failed(State from, Throwable failure) { */ log &= from != State.STARTING; if (log) { - logger.log( - Level.SEVERE, - "Service " + service + " has failed in the " + from + " state.", - failure); + logger + .get() + .log( + Level.SEVERE, + "Service " + service + " has failed in the " + from + " state.", + failure); } state.transitionService(service, from, FAILED); } diff --git a/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java b/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java index 1dc1094d4f94..14df42aea235 100644 --- a/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java +++ b/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java @@ -21,7 +21,6 @@ import com.google.common.annotations.VisibleForTesting; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Locale; -import java.util.logging.Logger; /** * Factories for {@link UncaughtExceptionHandler} instances. @@ -57,7 +56,7 @@ public static UncaughtExceptionHandler systemExit() { @VisibleForTesting static final class Exiter implements UncaughtExceptionHandler { - private static final Logger logger = Logger.getLogger(Exiter.class.getName()); + private static final LazyLogger logger = new LazyLogger(Exiter.class); private final Runtime runtime; @@ -68,8 +67,12 @@ static final class Exiter implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { try { - logger.log( - SEVERE, String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), e); + logger + .get() + .log( + SEVERE, + String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), + e); } catch (RuntimeException | Error errorInLogging) { // If logging fails, e.g. due to missing memory, at least try to log the // message and the cause for the failed logging.