| // Copyright 2018 The Bazel Authors. All rights reserved. | |
| // | |
| // 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.devtools.build.lib.util; | |
| import static com.google.common.base.Preconditions.checkState; | |
| import com.google.common.base.Supplier; | |
| import com.google.common.base.Suppliers; | |
| import com.google.errorprone.annotations.ForOverride; | |
| import java.nio.file.Path; | |
| import java.util.Optional; | |
| import java.util.logging.Handler; | |
| import java.util.logging.Logger; | |
| /** | |
| * A retriever for logging handler properties, e.g. the log file path. | |
| * | |
| * <p>A querier is intended for situations where a logging handler is configured on the JVM command | |
| * line, and where the code which needs to query the handler does not know the handler's class or | |
| * cannot import it. The command line then should in addition specify an appropriate child class of | |
| * {@link LogHandlerQuerier} via the {@code | |
| * -Dcom.google.devtools.build.lib.util.LogHandlerQuerier.class} flag, and an instance of that | |
| * appropriate child class can be obtained from {@code LogHandlerQuerier.getInstance()}. | |
| */ | |
| public abstract class LogHandlerQuerier { | |
| private static final Supplier<LogHandlerQuerier> configuredInstanceSupplier = | |
| Suppliers.memoize(LogHandlerQuerier::makeConfiguredInstance); | |
| private static LogHandlerQuerier makeConfiguredInstance() { | |
| String propertyName = LogHandlerQuerier.class.getName() + ".class"; | |
| String subclassName = System.getProperty(propertyName); | |
| checkState(subclassName != null, "System property %s is not defined", propertyName); | |
| try { | |
| return (LogHandlerQuerier) | |
| ClassLoader.getSystemClassLoader() | |
| .loadClass(subclassName) | |
| .getDeclaredConstructor() | |
| .newInstance(); | |
| } catch (ReflectiveOperationException e) { | |
| throw new IllegalStateException("System property " + propertyName + " value is invalid", e); | |
| } | |
| } | |
| /** | |
| * Returns the singleton instance of the LogHandlerQuerier child class which was configured as a | |
| * system property on the JVM command line via the {@code | |
| * -Dcom.google.devtools.build.lib.util.LogHandlerQuerier.class} flag. | |
| * | |
| * <p>This method is thread-safe. | |
| * | |
| * @throws IllegalStateException if the JVM property was not defined or if an instance of the | |
| * class named by the property could not be constructed | |
| */ | |
| public static LogHandlerQuerier getConfiguredInstance() { | |
| return configuredInstanceSupplier.get(); | |
| } | |
| /** | |
| * Returns a logger's handler's log file path, iterating through all handlers and the logger's | |
| * ancestors' handlers as necessary. | |
| * | |
| * <p>The method will stop iterating at the first log handler that it can query, returning the log | |
| * path if it is available for that log handler, or an empty {@link Optional} if the log file for | |
| * that handler is currently unavailable. | |
| * | |
| * @param logger a logger whose handlers, and ancestors' handlers if necessary, will be queried | |
| * @throws IllegalArgumentException if the {@link LogHandlerQuerier} cannot query any {@link | |
| * Handler} for this logger or its ancestors | |
| */ | |
| public Optional<Path> getLoggerFilePath(Logger logger) { | |
| for (; logger != null; logger = logger.getParent()) { | |
| for (Handler handler : logger.getHandlers()) { | |
| if (canQuery(handler)) { | |
| return getLogHandlerFilePath(handler); | |
| } | |
| } | |
| } | |
| throw new IllegalArgumentException("Failed to find a queryable logging handler"); | |
| } | |
| /** Checks if this {@link LogHandlerQuerier} can query the given handler. */ | |
| @ForOverride | |
| protected abstract boolean canQuery(Handler handler); | |
| /** | |
| * Returns a logging handler's log file path. | |
| * | |
| * @param handler logging handler to query | |
| * @return the log handler's log file path if the log file is currently available | |
| */ | |
| @ForOverride | |
| protected abstract Optional<Path> getLogHandlerFilePath(Handler handler); | |
| } |