/*  Copyright 2007 Niclas Hedhman.
 *
 * 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.ops4j.pax.logging.spi.support;

import org.ops4j.pax.logging.PaxLogger;
import org.ops4j.pax.logging.PaxLoggingConstants;
import org.ops4j.pax.logging.PaxLoggingManager;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.service.log.LogLevel;

/**
 * <p>One instance of this class will be registered to listen for events generated by
 * the OSGi framework and pass those events to log service.</p>
 *
 * <p>By default, all events log by this class are logged at the DEBUG level. This can be
 * changed to a different level by setting the context or system property
 * {@code org.ops4j.pax.logging.service.frameworkEventsLogLevel} to DEBUG, INFO, WARNING, or ERROR.</p>
 *
 * <p>According OSGi Compendium R6, 101.6 Mapping of Events, each even has precise logging level associated
 * but we always use either configured level or {@code DEBUG} (pax-logging always did this).</p>
 */
public class FrameworkHandler
        implements SynchronousBundleListener, FrameworkListener, ServiceListener {

    private final PaxLoggingManager m_manager;

    /**
     * Level at which framework/bundle/service events will be logged even if 101.6 chapter (R6) says otherwise.
     */
    private final LogLevel loggingLevel;

    public FrameworkHandler(BundleContext context, final PaxLoggingManager manager) {
        m_manager = manager;

        String levelName = "DEBUG";
        if (!"".equals(System.getProperty(PaxLoggingConstants.LOGGING_CFG_FRAMEWORK_EVENTS_LOG_LEVEL, "").trim())) {
            levelName = System.getProperty(PaxLoggingConstants.LOGGING_CFG_FRAMEWORK_EVENTS_LOG_LEVEL);
        } else if (!"".equals(context.getProperty(PaxLoggingConstants.LOGGING_CFG_FRAMEWORK_EVENTS_LOG_LEVEL))) {
            levelName = context.getProperty(PaxLoggingConstants.LOGGING_CFG_FRAMEWORK_EVENTS_LOG_LEVEL);
        }
        loggingLevel = BackendSupport.convertR7LogLevel(levelName, LogLevel.DEBUG);
    }

    /**
     * {@link BundleEvent bundle events} are logged with {@link LogLevel#INFO} level (according to spec) unless
     * other level is specified by {@link PaxLoggingConstants#LOGGING_CFG_FRAMEWORK_EVENTS_LOG_LEVEL}.
     * @param bundleEvent
     */
    @Override
    public void bundleChanged(final BundleEvent bundleEvent) {
        String message;
        final int type = bundleEvent.getType();
        switch (type) {
            case BundleEvent.INSTALLED:
                message = "BundleEvent INSTALLED";
                break;
            case BundleEvent.STARTED:
                message = "BundleEvent STARTED";
                break;
            case BundleEvent.STOPPED:
                message = "BundleEvent STOPPED";
                break;
            case BundleEvent.UPDATED:
                message = "BundleEvent UPDATED";
                break;
            case BundleEvent.UNINSTALLED:
                message = "BundleEvent UNINSTALLED";
                break;
            case BundleEvent.RESOLVED:
                message = "BundleEvent RESOLVED";
                break;
            case BundleEvent.UNRESOLVED:
                message = "BundleEvent UNRESOLVED";
                break;
            case BundleEvent.STARTING:
                message = "BundleEvent STARTING";
                break;
            case BundleEvent.STOPPING:
                message = "BundleEvent STOPPING";
                break;
            default:
                message = "BundleEvent [unknown: " + type + "]";
                break;
        }
        // bundle event messages don't have to add " - <bundle symbolic name>", because SN is part of the event anyway
//        if (bundle != null) {
//            message += " - " + bundle.getSymbolicName();
//        }
        final Bundle bundle = bundleEvent.getBundle();
        doLog(loggingLevel, bundle, "org.osgi.framework.BundleEvent", message);
    }

    /**
     * Specification determines logging level for given framework events. But we're overriding the values.
     * @param frameworkEvent
     */
    @Override
    public void frameworkEvent(final FrameworkEvent frameworkEvent) {
        final int type = frameworkEvent.getType();
        String message;
        LogLevel level = loggingLevel;
        switch (type) {
            case FrameworkEvent.ERROR:
                message = "FrameworkEvent ERROR";
                level = LogLevel.ERROR;
                break;
            case FrameworkEvent.INFO:
                message = "FrameworkEvent INFO";
                break;
            case FrameworkEvent.PACKAGES_REFRESHED:
                message = "FrameworkEvent PACKAGES REFRESHED";
                break;
            case FrameworkEvent.STARTED:
                message = "FrameworkEvent STARTED";
                break;
            case FrameworkEvent.STARTLEVEL_CHANGED:
                message = "FrameworkEvent STARTLEVEL CHANGED";
                break;
            case FrameworkEvent.WARNING:
                message = "FrameworkEvent WARNING";
                level = LogLevel.WARN;
                break;
            default:
                message = "FrameworkEvent [unknown:" + type + "]";
                break;
        }
        final Bundle bundle = frameworkEvent.getBundle();
        final Throwable exception = frameworkEvent.getThrowable();
        if (exception != null) {
            doLog(level, bundle, "org.osgi.framework.FrameworkEvent", message, exception);
        } else {
            doLog(level, bundle, "org.osgi.framework.FrameworkEvent", message);
        }
    }

    @Override
    public void serviceChanged(final ServiceEvent serviceEvent) {
        final ServiceReference serviceRef = serviceEvent.getServiceReference();
        String message;
        final int type = serviceEvent.getType();
        switch (type) {
            case ServiceEvent.MODIFIED:
                message = "ServiceEvent MODIFIED";
                break;
            case ServiceEvent.REGISTERED:
                message = "ServiceEvent REGISTERED";
                break;
            case ServiceEvent.UNREGISTERING:
                message = "ServiceEvent UNREGISTERING";
                break;
            default:
                message = "ServiceEvent [unknown:" + type + "]";
                break;
        }
        // service events, even if specification doesn't say so, have serviceRef.toString() appended to the message
        message += " - " + serviceRef;
        Bundle bundle = serviceRef.getBundle();
        doLog(loggingLevel, bundle, "org.osgi.framework.ServiceEvent", message);
    }

    private void doLog(LogLevel loggingLevel, Bundle bundle, String category, String message, Object... args) {
        if (loggingLevel == null) {
            return;
        }

        PaxLogger logger = m_manager.getLogger(bundle, category, "");

        switch (loggingLevel) {
            case AUDIT:
                logger.audit(message, args);
                break;
            case ERROR:
                logger.error(message, args);
                break;
            case WARN:
                logger.warn(message, args);
                break;
            case INFO:
                logger.info(message, args);
                break;
            case DEBUG:
                logger.debug(message, args);
                break;
            case TRACE:
                logger.trace(message, args);
                break;
            default:
                break;
        }
    }

}

@@ -7455,32 +7455,44 @@
 eEvent%22, message
+, serviceRef
 );%0A    %7D%0A%0A    pr

Add ServiceRef to logging