Package org.apache.marmotta.platform.core.services.logging

Source Code of org.apache.marmotta.platform.core.services.logging.LoggingServiceImpl

/**
* 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.marmotta.platform.core.services.logging;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.filter.ThresholdFilter;
import ch.qos.logback.classic.net.SyslogAppender;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import com.google.common.base.Function;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.marmotta.platform.core.api.config.ConfigurationService;
import org.apache.marmotta.platform.core.api.logging.LoggingModule;
import org.apache.marmotta.platform.core.api.logging.LoggingService;
import org.apache.marmotta.platform.core.events.ConfigurationChangedEvent;
import org.apache.marmotta.platform.core.events.LoggingStartEvent;
import org.apache.marmotta.platform.core.exception.MarmottaConfigurationException;
import org.apache.marmotta.platform.core.model.logging.ConsoleOutput;
import org.apache.marmotta.platform.core.model.logging.LogFileOutput;
import org.apache.marmotta.platform.core.model.logging.LoggingOutput;
import org.apache.marmotta.platform.core.model.logging.SyslogOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
import java.io.File;
import java.text.Collator;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* LoggingServiceImpl
*
* @author Sebastian Schaffert
*
*/
@ApplicationScoped
public class LoggingServiceImpl implements LoggingService {
    private static Logger log = LoggerFactory.getLogger(LoggingService.class);

    @Inject
    private ConfigurationService configurationService;


    @Inject
    private Instance<LoggingModule> loggingModules;


    private LoadingCache<String,LogFileOutput> logfileCache;
    private LoadingCache<String,SyslogOutput>  syslogCache;

    private ConsoleOutput consoleOutput;

    private LoggerContext loggerContext;

    private Map<LoggingOutput, Appender<ILoggingEvent>> appenders;

    @PostConstruct
    public void initialize() {
        log.info("Apache Marmotta Logging Service starting up ...");

        appenders = new HashMap<>();

        logfileCache = CacheBuilder.newBuilder().maximumSize(10).expireAfterAccess(10, TimeUnit.MINUTES).build(
                new CacheLoader<String, LogFileOutput>() {
                    @Override
                    public LogFileOutput load(String key) throws Exception {

                        if(configurationService.isConfigurationSet("logging.file."+key+".name")) {
                            return new LogFileOutput(key, configurationService);
                        } else {
                            throw new MarmottaConfigurationException("logfile configuration "+key+" not found");
                        }
                    }
                }
        );

        syslogCache = CacheBuilder.newBuilder().maximumSize(5).expireAfterAccess(10, TimeUnit.MINUTES).build(
                new CacheLoader<String, SyslogOutput>() {
                    @Override
                    public SyslogOutput load(String key) throws Exception {
                        if(configurationService.isConfigurationSet("logging.syslog."+key+".name")) {
                            return new SyslogOutput(key, configurationService);
                        } else {
                            throw new MarmottaConfigurationException("syslog configuration "+key+" not found");
                        }
                    }
                }
        );

        consoleOutput = new ConsoleOutput(configurationService);

        loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();

        log.info("- configured logging modules: {}", StringUtils.join(Iterables.transform(listModules(), new Function<LoggingModule, Object>() {
            @Override
            public Object apply(LoggingModule input) {
                return input.getName();
            }
        }), ", "));

        log.info("- configured logging appenders: {}", StringUtils.join(Iterables.transform(listOutputConfigurations(), new Function<LoggingOutput, Object>() {
            @Override
            public Object apply(LoggingOutput input) {
                return input.getName();
            }
        }),", "));

    }

    public void startEventHandler(@Observes LoggingStartEvent event) {
        if(!configurationService.getBooleanConfiguration("testing.enabled")) {
            log.warn("LOGGING: Switching to Apache Marmotta logging configuration; further output will be found in {}{}log{}*.log", configurationService.getHome(), File.separator, File.separator);

            configureLoggers();
        }
    }

    public void configurationEventHandler(@Observes ConfigurationChangedEvent event) {
        if(!configurationService.getBooleanConfiguration("testing.enabled")) {
            if (event.containsChangedKeyWithPrefix("logging.")) {
                log.warn("LOGGING: Reloading logging configuration");

                configureLoggers();
            }
        }
    }


    /**
     * Configure all loggers according to their configuration and set some reasonable fallback for the root level
     * (WARN to console, INFO to main logfile)
     */
    private void configureLoggers() {
        // remove all existing appenders
        loggerContext.reset();

        for(LoggingOutput output : listOutputConfigurations()) {
            configureOutput(output);
        }
        for(LoggingModule module : listModules()) {
            configureModule(module);
        }

        // configure defaults for root logger
        ch.qos.logback.classic.Logger rootLogger = loggerContext.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
        rootLogger.detachAndStopAllAppenders();
        rootLogger.addAppender(appenders.get(getOutputConfiguration("console")));
        rootLogger.addAppender(appenders.get(getOutputConfiguration("main")));

    }

    /**
     * Configure output appenders using the configuration given as argument.
     *
     * @param output
     */
    private void configureOutput(LoggingOutput output) {
        Appender<ILoggingEvent> appender = appenders.get(output);

        // stop old existing appender
        if(appender != null) {
            appender.stop();
        }

        // create new appender based on configuration
        if(output instanceof ConsoleOutput) {
            appender = new ConsoleAppender<>();

            PatternLayoutEncoder pl = new PatternLayoutEncoder();
            pl.setContext(loggerContext);
            pl.setPattern(output.getPattern());
            pl.start();

            ((ConsoleAppender)appender).setEncoder(pl);

        } else if(output instanceof LogFileOutput) {
            String basePath = configurationService.getHome() + File.separator + "log" + File.separator;

            appender = new RollingFileAppender<>();
            ((RollingFileAppender)appender).setFile(basePath + ((LogFileOutput) output).getFileName());

            TimeBasedRollingPolicy policy = new TimeBasedRollingPolicy();
            policy.setContext(loggerContext);
            policy.setMaxHistory(((LogFileOutput) output).getKeepDays());
            policy.setFileNamePattern(basePath + ((LogFileOutput) output).getFileName() + ".%d{yyyy-MM-dd}.gz");
            policy.setParent((FileAppender) appender);
            policy.start();

            ((RollingFileAppender) appender).setRollingPolicy(policy);

            PatternLayoutEncoder pl = new PatternLayoutEncoder();
            pl.setContext(loggerContext);
            pl.setPattern(output.getPattern());
            pl.start();

            ((RollingFileAppender) appender).setEncoder(pl);

        } else if(output instanceof SyslogOutput) {
            appender = new SyslogAppender();
            ((SyslogAppender)appender).setSyslogHost(((SyslogOutput) output).getHostName());
            ((SyslogAppender)appender).setFacility(((SyslogOutput) output).getFacility());

            ((SyslogAppender) appender).setSuffixPattern(output.getPattern());
        } else {
            throw new IllegalArgumentException("unknown logging output type: "+output.getClass().getName());
        }

        appender.setContext(loggerContext);
        appender.setName(output.getId());

        ThresholdFilter filter = new ThresholdFilter();
        filter.setLevel(output.getMaxLevel().toString());
        filter.setContext(loggerContext);
        filter.start();
        appender.addFilter(filter);

        appender.start();

        appenders.put(output,appender);

    }


    /**
     * Add the logging configuration for the given module. This method will create loggers for all packages
     * covered by the module and add the appenders configured for the module
     * @param module
     */
    private void configureModule(LoggingModule module) {
        for(String pkg : module.getPackages()) {
            ch.qos.logback.classic.Logger logger = loggerContext.getLogger(pkg);
            logger.detachAndStopAllAppenders();
            logger.setAdditive(false);
            logger.setLevel(module.getCurrentLevel());

            for(LoggingOutput appender : module.getLoggingOutputs()) {
                logger.addAppender(appenders.get(appender));
            }
        }
    }

    /**
     * Provide a logger to the injection point given as argument.
     *
     * @see org.apache.marmotta.platform.core.api.logging.LoggingService#createLogger(javax.enterprise.inject.spi.InjectionPoint)
     */
    @Override
    @Produces
    public Logger createLogger(InjectionPoint injectionPoint) {
        return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass());
    }


    /**
     * Create a new logfile output configuration using the given parameters; further options can be set on the object
     * itself.
     *
     * @param id   unique identifier for the log output configuration
     * @param name human-readable name for configuration (displayed in UI)
     * @param file filename under MARMOTTA_HOME/log
     * @return
     */
    @Override
    public LogFileOutput createLogFileOutput(String id, String name, String file) {
        try {
            return logfileCache.get(id);
        } catch (ExecutionException e) {
            LogFileOutput r = new LogFileOutput(id, configurationService);
            r.setFileName(file);
            r.setName(name);
            return r;
        }
    }

    /**
     * Return a list of all output configurations, reading directly from the configuration service.
     *
     * @return
     */
    @Override
    public List<LoggingOutput> listOutputConfigurations() {
        List<LoggingOutput> logs = new ArrayList<>();

        logs.addAll(
                Lists.transform(configurationService.listConfigurationKeys(Pattern.compile("logging\\.(file|syslog)\\.([^\\.]+)\\.name")), new Function<Matcher, LoggingOutput>() {
                    @Override
                    public LoggingOutput apply(java.util.regex.Matcher input) {
                        return getOutputConfiguration(input.group(2));
                    }
                }
                ));

        logs.add(consoleOutput);

        return logs;
    }

    /**
     * Return the output configuration with the given ID.
     *
     * @param id
     * @return
     */
    @Override
    public LoggingOutput getOutputConfiguration(String id) {
        if("console".equals(id)) {
            return consoleOutput;
        }

        try {
            return logfileCache.get(id);
        } catch (ExecutionException e) {
        }

        try {
            return syslogCache.get(id);
        } catch (ExecutionException e) {
        }

        return null;
    }

    /**
     * Create a new syslog output configuration using the given parameters; further options can be set on the object
     * itself. If not set, the default hostname is "localhost" and the default facility is "LOCAL0".
     *
     * @param id   unique identifier for the log output configuration
     * @param name human-readable name for configuration (displayed in UI)
     * @return
     */
    @Override
    public SyslogOutput createSyslogOutput(String id, String name) {
        try {
            return syslogCache.get(id);
        } catch (ExecutionException e) {
            SyslogOutput r = new SyslogOutput(id,configurationService);
            r.setName(name);
            return r;
        }
    }

    /**
     * Return the console output configuration used by Marmotta. There is only a single console output for any
     * Marmotta instance.
     *
     * @return
     */
    @Override
    public ConsoleOutput getConsoleOutput() {
        return consoleOutput;
    }

    /**
     * Return a list of all modules found on the classpath. This list is assembled via CDI injection. Since modules are
     * managed beans, calling setters on the LoggingModule implementations will directly change the configuration.
     *
     * @return
     */
    @Override
    public List<LoggingModule> listModules() {
        List<LoggingModule> result = Lists.newArrayList(loggingModules);

        Collections.sort(result, new Comparator<LoggingModule>() {
            @Override
            public int compare(LoggingModule o1, LoggingModule o2) {
                return Collator.getInstance().compare(o1.getName(), o2.getName());
            }
        });

        return result;
    }
}
TOP

Related Classes of org.apache.marmotta.platform.core.services.logging.LoggingServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.