| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.ignite.logger.java; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.util.UUID; |
| import java.util.logging.ConsoleHandler; |
| import java.util.logging.FileHandler; |
| import java.util.logging.Handler; |
| import java.util.logging.LogManager; |
| import java.util.logging.Logger; |
| import org.apache.ignite.IgniteCheckedException; |
| import org.apache.ignite.IgniteLogger; |
| import org.apache.ignite.internal.util.tostring.GridToStringExclude; |
| import org.apache.ignite.internal.util.typedef.F; |
| import org.apache.ignite.internal.util.typedef.internal.A; |
| import org.apache.ignite.internal.util.typedef.internal.S; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.apache.ignite.logger.LoggerNodeIdAndApplicationAware; |
| import org.jetbrains.annotations.Nullable; |
| |
| import static java.util.logging.Level.FINE; |
| import static java.util.logging.Level.FINEST; |
| import static java.util.logging.Level.INFO; |
| import static java.util.logging.Level.SEVERE; |
| import static java.util.logging.Level.WARNING; |
| import static org.apache.ignite.IgniteSystemProperties.IGNITE_CONSOLE_APPENDER; |
| import static org.apache.ignite.IgniteSystemProperties.IGNITE_QUIET; |
| |
| /** |
| * Logger to use with Java logging. Implementation simply delegates to Java Logging. |
| * <p> |
| * Here is an example of configuring Java logger in Ignite configuration Spring |
| * file to work over log4j implementation. Note that we use the same configuration file |
| * as we provide by default: |
| * <pre name="code" class="xml"> |
| * ... |
| * <property name="gridLogger"> |
| * <bean class="org.apache.ignite.logger.java.JavaLogger"> |
| * <constructor-arg type="java.util.logging.Logger"> |
| * <bean class="java.util.logging.Logger"> |
| * <constructor-arg type="java.lang.String" value="global"/> |
| * </bean> |
| * </constructor-arg> |
| * </bean> |
| * </property> |
| * ... |
| * </pre> |
| * or |
| * <pre name="code" class="xml"> |
| * ... |
| * <property name="gridLogger"> |
| * <bean class="org.apache.ignite.logger.java.JavaLogger"/> |
| * </property> |
| * ... |
| * </pre> |
| * And the same configuration if you'd like to configure Ignite in your code: |
| * <pre name="code" class="java"> |
| * IgniteConfiguration cfg = new IgniteConfiguration(); |
| * ... |
| * IgniteLogger log = new JavaLogger(Logger.global); |
| * ... |
| * cfg.setGridLogger(log); |
| * </pre> |
| * or which is actually the same: |
| * <pre name="code" class="java"> |
| * IgniteConfiguration cfg = new IgniteConfiguration(); |
| * ... |
| * IgniteLogger log = new JavaLogger(); |
| * ... |
| * cfg.setGridLogger(log); |
| * </pre> |
| * Please take a look at {@link java.util.logging.Logger} javadoc |
| * for additional information. |
| * <p> |
| * It's recommended to use Ignite logger injection instead of using/instantiating |
| * logger in your task/job code. See {@link org.apache.ignite.resources.LoggerResource} annotation about logger |
| * injection. |
| */ |
| public class JavaLogger implements IgniteLogger, LoggerNodeIdAndApplicationAware { |
| /** */ |
| public static final String DFLT_CONFIG_PATH = "config/java.util.logging.properties"; |
| |
| /** */ |
| private static final Object mux = new Object(); |
| |
| /** */ |
| private static volatile boolean inited; |
| |
| /** */ |
| private static volatile boolean quiet0; |
| |
| /** Java Logging implementation proxy. */ |
| @GridToStringExclude |
| @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") |
| private Logger impl; |
| |
| /** Path to configuration file. */ |
| @GridToStringExclude |
| private String cfg; |
| |
| /** Quiet flag. */ |
| private final boolean quiet; |
| |
| /** Work directory. */ |
| @GridToStringExclude |
| private volatile String workDir; |
| |
| /** Node ID. */ |
| @GridToStringExclude |
| private volatile UUID nodeId; |
| |
| /** |
| * Creates new logger. |
| */ |
| public JavaLogger() { |
| this(!isConfigured()); |
| } |
| |
| /** |
| * Checks if logger is already configured within this VM or not. |
| * |
| * @return {@code True} if logger was already configured, {@code false} otherwise. |
| */ |
| public static boolean isConfigured() { |
| return System.getProperty("java.util.logging.config.file") != null; |
| } |
| |
| /** |
| * Reads default JUL configuration. |
| */ |
| private void defaultConfiguration() { |
| final URL cfgUrl = U.resolveIgniteUrl(DFLT_CONFIG_PATH); |
| |
| if (cfgUrl == null) { |
| warning("Failed to resolve default logging config file: " + DFLT_CONFIG_PATH); |
| |
| return; |
| } |
| |
| try (InputStream in = cfgUrl.openStream()) { |
| LogManager.getLogManager().readConfiguration(in); |
| } |
| catch (IOException e) { |
| error("Failed to read logging configuration: " + cfgUrl, e); |
| } |
| |
| cfg = cfgUrl.getPath(); |
| } |
| |
| /** |
| * Creates new logger. |
| * |
| * @param init If {@code true}, then a default console appender will be created. |
| * If {@code false}, then no implicit initialization will take place, |
| * and java logger should be configured prior to calling this constructor. |
| */ |
| public JavaLogger(boolean init) { |
| impl = Logger.getLogger(""); |
| |
| if (init) { |
| // Implementation has already been inited, passing NULL. |
| configure(null); |
| |
| quiet = quiet0; |
| } |
| else |
| quiet = true; |
| } |
| |
| /** |
| * Creates new logger with given implementation. |
| * |
| * @param impl Java Logging implementation to use. |
| */ |
| public JavaLogger(final Logger impl) { |
| assert impl != null; |
| |
| configure(impl); |
| |
| quiet = quiet0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public IgniteLogger getLogger(Object ctgr) { |
| return new JavaLogger(ctgr == null ? Logger.getLogger("") : Logger.getLogger( |
| ctgr instanceof Class ? ((Class)ctgr).getName() : String.valueOf(ctgr))); |
| } |
| |
| /** |
| * Configures handlers when needed. |
| * |
| * @param initImpl Optional log implementation. |
| */ |
| private void configure(@Nullable Logger initImpl) { |
| if (initImpl != null) |
| impl = initImpl; |
| |
| if (inited) |
| return; |
| |
| synchronized (mux) { |
| if (inited) |
| return; |
| |
| if (isConfigured()) { |
| boolean consoleHndFound = findHandler(impl, ConsoleHandler.class) != null; |
| |
| // User configured console appender, thus log is not quiet. |
| quiet0 = !consoleHndFound; |
| inited = true; |
| cfg = System.getProperty("java.util.logging.config.file"); |
| |
| return; |
| } |
| |
| defaultConfiguration(); |
| |
| boolean quiet = Boolean.valueOf(System.getProperty(IGNITE_QUIET, "true")); |
| boolean useConsoleAppender = Boolean.valueOf(System.getProperty(IGNITE_CONSOLE_APPENDER, "true")); |
| |
| if (useConsoleAppender) { |
| ConsoleHandler consoleHnd = findHandler(impl, ConsoleHandler.class); |
| |
| if (consoleHnd != null) |
| consoleHnd.setLevel(quiet ? SEVERE : INFO); |
| else |
| System.err.println("Console logging handler is not configured."); |
| } |
| else { |
| Handler[] handlers = Logger.getLogger("").getHandlers(); |
| |
| if (!F.isEmpty(handlers)) { |
| for (Handler h : handlers) { |
| if (h instanceof ConsoleHandler) |
| impl.removeHandler(h); |
| } |
| } |
| } |
| |
| quiet0 = quiet; |
| inited = true; |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void trace(String msg) { |
| if (!impl.isLoggable(FINEST)) |
| warning("Logging at TRACE level without checking if TRACE level is enabled: " + msg); |
| |
| impl.finest(msg); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void debug(String msg) { |
| if (!impl.isLoggable(FINE)) |
| warning("Logging at DEBUG level without checking if DEBUG level is enabled: " + msg); |
| |
| impl.fine(msg); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void info(String msg) { |
| if (!impl.isLoggable(INFO)) |
| warning("Logging at INFO level without checking if INFO level is enabled: " + msg); |
| |
| impl.info(msg); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void warning(String msg) { |
| impl.warning(msg); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void warning(String msg, @Nullable Throwable e) { |
| impl.log(WARNING, msg, e); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void error(String msg) { |
| impl.severe(msg); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isQuiet() { |
| return quiet; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void error(String msg, @Nullable Throwable e) { |
| impl.log(SEVERE, msg, e); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isTraceEnabled() { |
| return impl.isLoggable(FINEST); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isDebugEnabled() { |
| return impl.isLoggable(FINE); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isInfoEnabled() { |
| return impl.isLoggable(INFO); |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public String fileName() { |
| JavaLoggerFileHandler gridFileHnd = findHandler(impl, JavaLoggerFileHandler.class); |
| |
| if (gridFileHnd != null) |
| return gridFileHnd.fileName(); |
| |
| FileHandler fileHnd = findHandler(impl, FileHandler.class); |
| |
| return fileName(fileHnd); |
| } |
| |
| /** |
| * @param fileHnd File handler. |
| * @return Current log file or {@code null} if it can not be retrieved from file handler. |
| */ |
| @Nullable static String fileName(FileHandler fileHnd) { |
| if (fileHnd == null) |
| return null; |
| |
| try { |
| File[] logFiles = U.field(fileHnd, "files"); |
| |
| return logFiles[0].getAbsolutePath(); |
| } |
| catch (Exception ignored) { |
| return null; |
| } |
| } |
| |
| /** |
| * Set work directory. |
| * |
| * @param workDir Work directory. |
| */ |
| public void setWorkDirectory(String workDir) { |
| this.workDir = workDir; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void setApplicationAndNode(@Nullable String application, UUID nodeId) { |
| A.notNull(nodeId, "nodeId"); |
| |
| if (this.nodeId != null) |
| return; |
| |
| synchronized (mux) { |
| // Double check. |
| if (this.nodeId != null) |
| return; |
| |
| this.nodeId = nodeId; |
| } |
| |
| JavaLoggerFileHandler fileHnd = findHandler(impl, JavaLoggerFileHandler.class); |
| |
| if (fileHnd == null) |
| return; |
| |
| try { |
| fileHnd.nodeId(application, nodeId, workDir); |
| } |
| catch (IgniteCheckedException | IOException e) { |
| throw new RuntimeException("Failed to enable file handler.", e); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public UUID getNodeId() { |
| return nodeId; |
| } |
| |
| /** |
| * Returns first found handler of specified class type or {@code null} if that handler isn't configured. |
| * |
| * @param log Logger. |
| * @param cls Class. |
| * @param <T> Class type. |
| * @return First found handler of specified class type or {@code null} if that handler isn't configured. |
| */ |
| private static <T> T findHandler(Logger log, Class<T> cls) { |
| while (log != null) { |
| for (Handler hnd : log.getHandlers()) { |
| if (cls.isInstance(hnd)) |
| return (T)hnd; |
| } |
| |
| log = log.getParent(); |
| } |
| |
| return null; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public String toString() { |
| return S.toString(JavaLogger.class, this, "config", this.cfg); |
| } |
| } |