LogAnalyserImpl.java 10.3 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  ch.qos.logback.classic.Level
 *  ch.qos.logback.classic.spi.ILoggingEvent
 *  ch.qos.logback.core.filter.Filter
 *  ch.qos.logback.core.spi.FilterReply
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Deactivate
 *  org.apache.felix.scr.annotations.Modified
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.PropertyUnbounded
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.sling.commons.osgi.PropertiesUtil
 *  org.osgi.framework.BundleContext
 *  org.osgi.framework.ServiceRegistration
 *  org.osgi.service.cm.Configuration
 *  org.osgi.service.cm.ConfigurationAdmin
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.granite.logging.impl;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
import com.adobe.granite.logging.LogAnalyser;
import com.adobe.granite.logging.LogConfigurationEntry;
import com.adobe.granite.logging.LogEntry;
import com.adobe.granite.logging.LogLevel;
import com.adobe.granite.logging.impl.InternalLogConfigurationEntry;
import com.adobe.granite.logging.impl.LogEntryImpl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=1, immediate=1, label="Adobe Granite Log Analysis Service", description="Log analysis service for the operations dashboard")
@Service(value={LogAnalyser.class})
public class LogAnalyserImpl
extends Filter<ILoggingEvent>
implements LogAnalyser {
    private static final int DEFAULT_QUEUE_SIZE = 200;
    private static final int DEFAULT_MESSAGE_SIZE = 25;
    @Property(intValue={200}, label="Queue Size", description="Number of logging events to keep. A size below 1 disables keeping the log events.")
    private static final String PROPERTY_QUEUE_SIZE = "messages.queue.size";
    @Property(unbounded=PropertyUnbounded.ARRAY, label="Logger Configuration", description="Each configuration entry should have the format {category}:{level}.")
    private static final String PROPERTY_LOGGER_CONFIG = "logger.config";
    @Property(intValue={25}, label="Message Size", description="Maximum size of a message in kilo byte to be stored. A size lower than 1 means the whole message is stored.")
    private static final String PROPERTY_MESSAGE_SIZE = "messages.size";
    private final Logger logger;
    private final Deque<LogEntry> events;
    private int eventsSize;
    private int messageSize;
    private LogConfigurationEntry[] configuration;
    @Reference
    private ConfigurationAdmin configAdmin;
    private String pid;
    private ServiceRegistration<Filter> filterRegistration;

    public LogAnalyserImpl() {
        this.logger = LoggerFactory.getLogger(this.getClass());
        this.events = new LinkedList<LogEntry>();
        this.configuration = new LogConfigurationEntry[0];
    }

    @Activate
    @Modified
    protected void activate(BundleContext bc, Map<String, Object> props) {
        this.pid = (String)props.get("service.pid");
        this.eventsSize = PropertiesUtil.toInteger((Object)props.get("messages.queue.size"), (int)200);
        this.messageSize = PropertiesUtil.toInteger((Object)props.get("messages.size"), (int)25);
        this.messageSize = this.messageSize > 0 ? (this.messageSize *= 1024) : 0;
        String[] configs = PropertiesUtil.toStringArray((Object)props.get("logger.config"));
        if (configs != null && configs.length > 0) {
            ArrayList<InternalLogConfigurationEntry> entries = new ArrayList<InternalLogConfigurationEntry>();
            for (String config : configs) {
                int pos = config.indexOf(":");
                if (pos != -1) {
                    String category = config.substring(0, pos).trim();
                    String level = config.substring(pos + 1).trim();
                    try {
                        LogLevel logLevel = LogLevel.valueOf(level.toUpperCase());
                        InternalLogConfigurationEntry lce = new InternalLogConfigurationEntry(new LogConfigurationEntry(logLevel, category));
                        entries.add(lce);
                    }
                    catch (IllegalArgumentException ignore) {
                        this.logger.warn("Ignoring log analyser configuration due to invalid log level: " + config);
                    }
                    continue;
                }
                if (config.length() <= 0) continue;
                this.logger.warn("Ignoring log analyser configuration due to invalid format: " + config);
            }
            this.configuration = entries.toArray(new LogConfigurationEntry[entries.size()]);
        } else {
            this.configuration = new LogConfigurationEntry[0];
        }
        if (this.eventsSize > 0) {
            this.registerFilter(bc);
        } else {
            this.unregisterFilter();
        }
    }

    private void registerFilter(BundleContext bc) {
        if (this.filterRegistration == null) {
            Hashtable<String, String> props = new Hashtable<String, String>();
            props.put("appenders", "*");
            this.filterRegistration = bc.registerService(Filter.class, (Object)this, props);
        }
    }

    private void unregisterFilter() {
        if (this.filterRegistration != null) {
            this.filterRegistration.unregister();
            this.filterRegistration = null;
        }
    }

    @Deactivate
    protected void deactivate() {
        this.unregisterFilter();
        this.configuration = new LogConfigurationEntry[0];
        this.events.clear();
    }

    @Override
    public LogConfigurationEntry[] getLogConfiguration() {
        return this.configuration;
    }

    @Override
    public synchronized void setLogConfiguration(LogConfigurationEntry[] config) {
        String[] configProperty;
        LogConfigurationEntry[] newConfig;
        if (config == null) {
            newConfig = new LogConfigurationEntry[]{};
            configProperty = null;
        } else {
            newConfig = new LogConfigurationEntry[config.length];
            System.arraycopy(config, 0, newConfig, 0, config.length);
            configProperty = new String[config.length];
        }
        int index = 0;
        for (LogConfigurationEntry entry : newConfig) {
            newConfig[index] = new InternalLogConfigurationEntry(entry);
            configProperty[index] = entry.getLoggerName() + ":" + entry.getLogLevel().name();
            ++index;
        }
        this.configuration = newConfig;
        if (this.pid != null) {
            try {
                Configuration cfg = this.configAdmin.getConfiguration(this.pid);
                Hashtable<String, String[]> props = cfg.getProperties();
                if (props == null) {
                    props = new Hashtable<String, String[]>();
                }
                if (configProperty == null) {
                    props.remove("logger.config");
                } else {
                    props.put("logger.config", configProperty);
                }
                cfg.update(props);
            }
            catch (IOException cfg) {
                // empty catch block
            }
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public List<LogEntry> getLastLogEntries(int size) {
        ArrayList<LogEntry> entries = new ArrayList<LogEntry>();
        Deque<LogEntry> deque = this.events;
        synchronized (deque) {
            if (size == -1 || this.events.size() <= size) {
                entries.addAll(this.events);
            } else {
                Iterator<LogEntry> iter = this.events.iterator();
                for (int index = 0; index < size && iter.hasNext(); ++index) {
                    entries.add(iter.next());
                }
            }
        }
        return entries;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private void append(ILoggingEvent event) {
        Deque<LogEntry> deque = this.events;
        synchronized (deque) {
            while (this.eventsSize <= this.events.size()) {
                this.events.removeLast();
            }
            this.events.offerFirst(new LogEntryImpl(event, this.messageSize));
        }
    }

    public FilterReply decide(ILoggingEvent event) {
        if (this.eventsSize < 1) {
            return FilterReply.NEUTRAL;
        }
        Level level = event.getLevel();
        boolean append = level.isGreaterOrEqual(Level.ERROR);
        if (!append) {
            LogConfigurationEntry[] currentConfig;
            String loggerName = event.getLoggerName();
            for (LogConfigurationEntry e : currentConfig = this.configuration) {
                InternalLogConfigurationEntry entry = (InternalLogConfigurationEntry)e;
                if (!entry.matches(loggerName, level)) continue;
                append = true;
                break;
            }
        }
        if (append) {
            this.append(event);
        }
        return FilterReply.NEUTRAL;
    }

    protected void bindConfigAdmin(ConfigurationAdmin configurationAdmin) {
        this.configAdmin = configurationAdmin;
    }

    protected void unbindConfigAdmin(ConfigurationAdmin configurationAdmin) {
        if (this.configAdmin == configurationAdmin) {
            this.configAdmin = null;
        }
    }
}