All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.identityconnectors.common.logging.Log Maven / Gradle / Ivy

The newest version!
/*
 * ====================
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License("CDDL") (the "License").  You may not use this file
 * except in compliance with the License.
 *
 * You can obtain a copy of the License at
 * http://opensource.org/licenses/cddl1.php
 * See the License for the specific language governing permissions and limitations
 * under the License.
 *
 * When distributing the Covered Code, include this CDDL Header Notice in each file
 * and include the License file at http://opensource.org/licenses/cddl1.php.
 * If applicable, add the following below this CDDL Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * ====================
 * Portions Copyrighted 2014 ForgeRock AS.
 * Portions Copyrighted 2022 ConnId
 */
package org.identityconnectors.common.logging;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Properties;
import java.util.Set;

import org.identityconnectors.common.CollectionUtil;
import org.identityconnectors.common.IOUtil;
import org.identityconnectors.common.ReflectionUtil;
import org.identityconnectors.common.StringUtil;

/**
 * Yet another logging abstraction.
 */
public final class Log {

    /**
     * Default SPI implementation should all attempts to load the custom logger
     * fail.
     */
    private static Class defaultSPI = StdOutLogger.class;

    // Hack for OIM: this ought to be Log.class.getPackage().getName(). However,
    // OIM's
    // tcADPClassLoader does not use ClassLoader.definePackage(), and so
    // Log.class.getPackage()
    // returns null.
    private static final String PACKAGE = ReflectionUtil.getPackage(Log.class);

    private static final String LOGGER_NAME = Log.class.getName();

    private static final Set EXCLUDE_LIST = CollectionUtil.newReadOnlySet(
            "groovy.", "org.codehaus.groovy.", "gjdk.groovy.", "java.", "javax.", "sun.",
            "com.google.apphosting."// for GAE/J
    );

    /**
     * System property to set the logger class that is most appropriate.
     */
    public static final String LOGSPI_PROP = PACKAGE + ".class";

    /**
     * Filename 'connectors.properties' used to for SPI class in the
     * '$(java.home)/lib/' directory.
     */
    public static final String LOGSPI_PROPS_FILE = "connectors.properties";

    /**
     * Cache the SPI class so we one search for it once.
     */
    private static Class cacheSPI;

    /**
     * Basic logging levels.
     */
    public static enum Level {

        /**
         * Maps to java.util.logging.Level.FINE.
         */
        OK,

        /**
         * Maps to java.util.logging.Level.INFO.
         */
        INFO,

        /**
         * Maps to java.util.logging.Level.WARNING.
         */
        WARN,

        /**
         * Maps to java.util.logging.Level.SEVERE.
         */
        ERROR;
    }

    /**
     * Class to log.
     */
    private final Class clazz;

    /**
     * Implementation to use for logging.
     */
    private final LogSpi logImpl;

    /**
     * Create an instance of the log based on the SPI in the System properties.
     */
    private Log(final Class clazz, final LogSpi logImpl) {
        this.clazz = clazz;
        this.logImpl = logImpl;
    }

    /**
     * Used for testing..
     */
    static Log getLog(final Class clazz, final LogSpi logImpl) {
        return new Log(clazz, logImpl);
    }

    /**
     * Get the logger for the particular class. 
     * private static final Log LOG = Log.getLog(MyClass.class);
     * 
     *
     * @param clazz
     *            class to log information about.
     * @return logger to use for logging.
     */
    public static Log getLog(final Class clazz) {
        try {
            // check that we're not logging ourselves
            if (LogSpi.class.isAssignableFrom(clazz)) {
                throw new IllegalArgumentException();
            }
            // attempt to get an instance..
            final LogSpi logImpl = (LogSpi) getSpiClass().getDeclaredConstructor().newInstance();
            return new Log(clazz, logImpl);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Determine if its log-able at this level within this class.
     */
    public boolean isLoggable(final Level level) {
        return logImpl.isLoggable(clazz, level);
    }

    // =======================================================================
    // Helper Methods..
    // =======================================================================
    /**
     * Lowest level logging method.
     *
     * @param clazz
     *            Class that is being logged.
     * @param method
     *            Method name that is being logged.
     * @param level
     *            Logging level.
     * @param message
     *            Message about the log.
     * @param ex
     *            Exception to use process.
     */
    public void log(final Class clazz, final String method, final Log.Level level,
            final String message, final Throwable ex) {

        if (isLoggable(level)) {
            logImpl.log(clazz, method, level, message, ex);
        }
    }

    /**
     * Logs based on the parameters given. Uses the format parameter inside
     * {@link MessageFormat}.
     *
     * @param level
     *            the logging level at which to write the message.
     * @param ex
     *            [optional] exception stack trace to log.
     * @param format
     *            [optional] create a message of a particular format.
     * @param args
     *            [optional] parameters to the format string.
     */
    public void log(final Level level, final Throwable ex, final String format,
            final Object... args) {
        if (isLoggable(level)) {
            String message = format;
            if (format != null && args != null) {
                // consider using thread local pattern to cache these for
                // performance the pattern will always may always changed.
                message = MessageFormat.format(format, args);
            } else if (format == null && ex != null) {
                message = ex.getLocalizedMessage();
            }
            //To get the StackTrace is expensive. Extract the method name only if it's necessary!!!
            log(level, ex, message, logImpl.needToInferCaller(clazz, level) ? Thread.currentThread().getStackTrace() : null);
        }
    }

    protected void log(final Level level, final Throwable ex, final String message,
                       final StackTraceElement[] locations) {
        final StackTraceElement caller = extract(locations, EXCLUDE_LIST);
        if (null != caller) {
            logImpl.log(clazz, caller, level, message, ex);
        } else {
            logImpl.log(clazz, (String)null, level, message, ex);
        }
    }


    protected StackTraceElement extract(StackTraceElement[] steArray,
                                        Collection frameworkPackageList) {
        if (steArray == null) {
            return null;
        }

        for (StackTraceElement current : steArray) {
            if (!isInFrameworkPackageList(current.getClassName(), frameworkPackageList)) {
                return current;
            }
        }
        return null;
    }

    protected boolean isInFrameworkPackageList(String currentClass, Collection frameworkPackageList) {
        if (frameworkPackageList == null) {
            return false;
        }
        if (LOGGER_NAME.equals(currentClass)) {
            return true;
        }
        for (String s : frameworkPackageList) {
            if (currentClass.startsWith(s)) {
                return true;
            }
        }
        return false;
    }

    public void ok(final Throwable ex, final String format, final Object... args) {
        log(Level.OK, ex, format, args);
    }

    public void info(final Throwable ex, final String format, final Object... args) {
        log(Level.INFO, ex, format, args);
    }

    public void warn(final Throwable ex, final String format, final Object... args) {
        log(Level.WARN, ex, format, args);
    }

    public void error(final Throwable ex, final String format, final Object... args) {
        log(Level.ERROR, ex, format, args);
    }

    public void ok(final String format, final Object... args) {
        log(Level.OK, null, format, args);
    }

    public void info(final String format, final Object... args) {
        log(Level.INFO, null, format, args);
    }

    public void warn(final String format, final Object... args) {
        log(Level.WARN, null, format, args);
    }

    public void error(final String format, final Object... args) {
        log(Level.ERROR, null, format, args);
    }

    public boolean isOk() {
        return isLoggable(Level.OK);
    }

    public boolean isInfo() {
        return isLoggable(Level.INFO);
    }

    public boolean isWarning() {
        return isLoggable(Level.WARN);
    }

    public boolean isError() {
        return isLoggable(Level.ERROR);
    }

    /**
     * Finds the logging implementation Class object in the specified order:
     * 
    *
  1. query the system property using System.getProperty
  2. *
  3. read $java.home/lib/connectors.properties file
  4. *
  5. read * META-INF/services/org.identityconnectors.common.logging.class * file
  6. *
  7. use {@link org.identityconnectors.common.logging.StdOutLogger} * as a fallback.
  8. *
*/ private static Class findSpiClass() { // Use the system property first final String impl = System.getProperty(LOGSPI_PROP); if (StringUtil.isNotBlank(impl)) { return forName(impl); } // attempt to find the properties file.. final File javaHome = new File(System.getProperty("java.home")); final File javaHomeLib = new File(javaHome, "lib"); final File propsFile = new File(javaHomeLib, LOGSPI_PROPS_FILE); if (propsFile.isFile() && propsFile.canRead()) { FileInputStream fis = null; try { final Properties props = new Properties(); fis = new FileInputStream(propsFile); props.load(fis); // get the same system property from the properties file.. final String prop = props.getProperty(LOGSPI_PROP); if (StringUtil.isNotBlank(prop)) { return forName(prop); } } catch (IOException e) { // throw to alert the caller the file is corrupt throw new RuntimeException(e); } finally { IOUtil.quietClose(fis); } } // attempt to find through the jar META-INF/services.. final String serviceId = "META-INF/services/" + PACKAGE; final String clazz = IOUtil.getResourceAsString(Log.class, serviceId); if (StringUtil.isNotBlank(clazz)) { return forName(clazz.trim()); } // return the default.. return defaultSPI; } /** * Simple helper function to prevent duplicate code. */ private static Class forName(final String clazz) { try { return Class.forName(clazz); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } /** * Cache the search for the SPI class. */ static Class getSpiClass() { // initialize the SPI class cache object synchronized (Log.class) { if (cacheSPI == null) { cacheSPI = findSpiClass(); } } return cacheSPI; } /** * For test only. */ static void setSpiClass(final Class clazz) { synchronized (Log.class) { cacheSPI = clazz; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy