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

org.apache.log4j.PaxLoggingConfigurator Maven / Gradle / Ivy

/*
 * Copyright 2006 Niclas Hedhman.
 * Copyright 2012 Guillaume Nodet.
 *
 * 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 org.apache.log4j;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

import org.apache.log4j.config.PaxPropertySetter;
import org.apache.log4j.config.PropertySetter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.AppenderAttachable;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.OptionHandler;
import org.ops4j.pax.logging.log4j1.internal.bridges.AppenderBridgeImpl;
import org.ops4j.pax.logging.log4j1.internal.bridges.ErrorHandlerBridgeImpl;
import org.ops4j.pax.logging.log4j1.internal.bridges.FilterBridgeImpl;
import org.ops4j.pax.logging.log4j1.internal.bridges.LayoutBridgeImpl;
import org.ops4j.pax.logging.spi.support.PaxAppenderProxy;
import org.osgi.framework.BundleContext;

/**
 * OSGi-specific {@link PropertyConfigurator} that can parse {@code log4j.properties} file and
 * dynamically reference some log4j services (appenders, layouts, error handlers, filters) from OSGi
 * registry.
 *
 * @since 2007-10-26
 */
public class PaxLoggingConfigurator extends PropertyConfigurator {

    public static final String OSGI_PREFIX = "osgi:";

    // these are private in super class, so have to be repeat here
    private static final String LOGGER_REF = "logger-ref";
    private static final String ROOT_REF = "root-ref";
    private static final String APPENDER_REF_TAG = "appender-ref";

    private LoggerRepository repository;
    private BundleContext m_bundleContext;
    private List proxies = new ArrayList<>();

    public PaxLoggingConfigurator(BundleContext bundleContext) {
        m_bundleContext = bundleContext;
    }

    public List getProxies() {
        return proxies;
    }

    @Override
    public void doConfigure(Properties properties, LoggerRepository hierarchy) {
        repository = hierarchy;

        // super.doConfigure will do this:
        // - handle global threshold
        // - configure root logger (log4j.rootLogger)
        // - configure loggers (with appenders) and renderers (log4j.logger.* , log4j.renderer.*)
        // pax-logging overrides some methods to alter the parsing procedure
        super.doConfigure(properties, hierarchy);
    }

    /**
     * pax-logging adds proxies (that resolve to OSGi-registry services) for components
     * (like appenders) if the names of the components start with {@link PaxLoggingConfigurator#OSGI_PREFIX osgi:}
     *
     * Superclass version is not easily extensible (see: template method pattern), so we had to copy it
     * and adjust.
     *
     * @param props
     * @param appenderName
     * @return
     */
    @Override
    Appender parseAppender(Properties props, String appenderName) {
        Appender appender = registryGet(appenderName);
        if (appender != null) {
            LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
            return appender;
        }
        if (appenderName.startsWith(OSGI_PREFIX)) {
            // pax-logging-log4j1: appender is not a class name - it's part of OSGi service filter.
            String osgiAppenderName = appenderName.substring(OSGI_PREFIX.length());
            PaxAppenderProxy paxAppender = new PaxAppenderProxy(m_bundleContext, osgiAppenderName);
            proxies.add(paxAppender);
            appender = new AppenderBridgeImpl(paxAppender);
            appender.setName(appenderName);
        } else {
            // Appender was not previously initialized.
            String prefix = APPENDER_PREFIX + appenderName;
            String layoutPrefix = prefix + ".layout";

            appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
                    Appender.class,
                    null);
            if (appender == null) {
                LogLog.error("Could not instantiate appender named \"" + appenderName + "\".");
                return null;
            }
            appender.setName(appenderName);

            if (appender instanceof OptionHandler) {
                if (appender.requiresLayout()) {
                    String layoutClass = OptionConverter.findAndSubst(layoutPrefix, props);
                    if (layoutClass != null && layoutClass.startsWith(OSGI_PREFIX)) {
                        // pax-logging-log4j1: layout is not a class name - it's part of OSGi service filter.
                        String osgiLayoutName = layoutClass.substring(OSGI_PREFIX.length());
                        Layout fallback = null;
                        // pax-logging-log4j1: log4j.appender.appenderName.layout.fallback may specify fallback class for layout
                        // and log4j.appender.appenderName.layout.fallback.option[1-N] may specify its properties
                        String fallbackClass = OptionConverter.findAndSubst(layoutPrefix + ".fallback", props);
                        if (fallbackClass != null) {
                            fallback = (Layout) OptionConverter.instantiateByKey(
                                    props, layoutPrefix + ".fallback", Layout.class, null);
                            if (fallback != null) {
                                PaxPropertySetter.setProperties(fallback, props, layoutPrefix + ".fallback.");
                            }
                        }
                        Layout layout = new LayoutBridgeImpl(m_bundleContext, osgiLayoutName, fallback);
                        appender.setLayout(layout);
                    } else {
                        Layout layout = (Layout) OptionConverter.instantiateByKey(props,
                                layoutPrefix,
                                Layout.class,
                                null);
                        if (layout != null) {
                            appender.setLayout(layout);
                            LogLog.debug("Parsing layout options for \"" + appenderName + "\".");
                            //configureOptionHandler(layout, layoutPrefix + ".", props);
                            PaxPropertySetter.setProperties(layout, props, layoutPrefix + ".");
                            LogLog.debug("End of parsing for \"" + appenderName + "\".");
                        }
                    }
                }
                final String errorHandlerPrefix = prefix + ".errorhandler";
                String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
                if (errorHandlerClass != null && errorHandlerClass.startsWith(OSGI_PREFIX)) {
                    // pax-logging-log4j1: error handler is not a class name - it's part of OSGi service filter.
                    String errorHandlerName = errorHandlerClass.substring(OSGI_PREFIX.length());
                    ErrorHandler fallback = null;
                    // pax-logging-log4j1: log4j.appender.appenderName.errorhandler.fallback may specify fallback class for error handler
                    // and log4j.appender.appenderName.errorhandler.fallback.option[1-N] may specify its properties
                    String fallbackClass = OptionConverter.findAndSubst(errorHandlerPrefix + ".fallback", props);
                    if (fallbackClass != null) {
                        fallback = (ErrorHandler) OptionConverter.instantiateByKey(
                                props, errorHandlerPrefix + ".fallback", ErrorHandler.class, null);
                        if (fallback != null) {
                            parseErrorHandler(fallback, errorHandlerPrefix + ".fallback", props, repository);
                        }
                    }
                    ErrorHandler eh = new ErrorHandlerBridgeImpl(m_bundleContext, errorHandlerName, fallback);
                    appender.setErrorHandler(eh);
                } else if (errorHandlerClass != null) {
                    ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props,
                            errorHandlerPrefix,
                            ErrorHandler.class,
                            null);
                    if (eh != null) {
                        appender.setErrorHandler(eh);
                        LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\".");
                        parseErrorHandler(eh, errorHandlerPrefix, props, repository);
                        LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\".");
                    }

                }
                if (appender instanceof AppenderAttachable) {
                    // this fragment is not part of original log4j:log4j and I can't track the reason why
                    // it was added...
                    final String appenderPrefix = prefix + ".appenders";
                    String appenderNames = OptionConverter.findAndSubst(appenderPrefix, props);
                    StringTokenizer st = new StringTokenizer(appenderNames, ", ");
                    Appender childAppender;
                    String childAppenderName;
                    while (st.hasMoreTokens()) {
                        childAppenderName = st.nextToken().trim();
                        LogLog.debug("Parsing appender named \"" + childAppenderName + "\".");
                        childAppender = parseAppender(props, childAppenderName);
                        if (childAppender != null) {
                            ((AppenderAttachable) appender).addAppender(childAppender);
                        }
                    }
                }
                //configureOptionHandler((OptionHandler) appender, prefix + ".", props);
                PaxPropertySetter.setProperties(appender, props, prefix + ".");
                LogLog.debug("Parsed \"" + appenderName + "\" options.");
            }
        }
        parseAppenderFilters(props, appenderName, appender);
        registryPut(appender);
        return appender;
    }

    private void parseErrorHandler(
            final ErrorHandler eh,
            final String errorHandlerPrefix,
            final Properties props,
            final LoggerRepository hierarchy) {
        boolean rootRef = OptionConverter.toBoolean(
                OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false);
        if (rootRef) {
            eh.setLogger(hierarchy.getRootLogger());
        }
        String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF, props);
        if (loggerName != null) {
            Logger logger = (loggerFactory == null) ? hierarchy.getLogger(loggerName)
                    : hierarchy.getLogger(loggerName, loggerFactory);
            eh.setLogger(logger);
        }
        String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props);
        if (appenderName != null) {
            Appender backup = parseAppender(props, appenderName);
            if (backup != null) {
                eh.setBackupAppender(backup);
            }
        }
        final Properties edited = new Properties();
        final String[] keys = new String[] {
                errorHandlerPrefix + "." + ROOT_REF,
                errorHandlerPrefix + "." + LOGGER_REF,
                errorHandlerPrefix + "." + APPENDER_REF_TAG
        };
        for (Iterator iter = props.entrySet().iterator(); iter.hasNext(); ) {
            Map.Entry entry = (Map.Entry) iter.next();
            int i = 0;
            for (; i < keys.length; i++) {
                if (keys[i].equals(entry.getKey())) break;
            }
            if (i == keys.length) {
                edited.put(entry.getKey(), entry.getValue());
            }
        }
        PaxPropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
    }

    @Override
    void parseAppenderFilters(Properties props, String appenderName, Appender appender) {
        // extract filters and filter options from props into a hashtable mapping
        // the property name defining the filter class to a list of pre-parsed
        // name-value pairs associated to that filter
        final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
        int fIdx = filterPrefix.length();
        Hashtable> filters = new Hashtable<>();
        Enumeration e = props.keys();
        String name = "";
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            if (key.startsWith(filterPrefix)) {
                int dotIdx = key.indexOf('.', fIdx);
                String filterKey = key;
                if (dotIdx != -1) {
                    filterKey = key.substring(0, dotIdx);
                    name = key.substring(dotIdx + 1);
                }
                Vector filterOpts = filters.get(filterKey);
                if (filterOpts == null) {
                    filterOpts = new Vector<>();
                    filters.put(filterKey, filterOpts);
                }
                if (dotIdx != -1) {
                    String value = OptionConverter.findAndSubst(key, props);
                    filterOpts.add(new NameValue(name, value));
                }
            }
        }

        // sort filters by IDs, insantiate filters, set filter options,
        // add filters to the appender
        Enumeration g = new SortedKeyEnumeration(filters);
        while (g.hasMoreElements()) {
            String key = (String) g.nextElement();
            String clazz = props.getProperty(key);
            if (clazz != null && clazz.startsWith(OSGI_PREFIX)) {
                // pax-logging-log4j1: filter is not a class name - it's part of OSGi service filter.
                String filterName = clazz.substring(OSGI_PREFIX.length());
                Filter fallback = null;
                // pax-logging-log4j1: log4j.appender.appenderName.filter.fallback may specify fallback class for filter
                // and log4j.appender.appenderName.filter.fallback.option[1-N] may specify its properties
                String fallbackClass = OptionConverter.findAndSubst(key + ".fallback", props);
                if (fallbackClass != null) {
                    fallback = (Filter) OptionConverter.instantiateByKey(
                            props, key + ".fallback", Filter.class, null);
                    if (fallback != null) {
                        PaxPropertySetter.setProperties(fallback, props, key + ".fallback.");
                    }
                }
                Filter filter = new FilterBridgeImpl(m_bundleContext, filterName, fallback);
                appender.addFilter(filter);
            } else if (clazz != null) {
                LogLog.debug("Filter key: [" + key + "] class: [" + props.getProperty(key) + "] props: " + filters.get(key));
                Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null);
                if (filter != null) {
                    PropertySetter propSetter = new PropertySetter(filter);
                    Vector v = filters.get(key);
                    Enumeration filterProps = v.elements();
                    while (filterProps.hasMoreElements()) {
                        NameValue kv = filterProps.nextElement();
                        propSetter.setProperty(kv.key, kv.value);
                    }
                    propSetter.activate();
                    LogLog.debug("Adding filter of type [" + filter.getClass()
                            + "] to appender named [" + appender.getName() + "].");
                    appender.addFilter(filter);
                }
            } else {
                LogLog.warn("Missing class definition for filter: [" + key + "]");
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy