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

com.dimajix.flowman.common.Logging Maven / Gradle / Ivy

There is a newer version: 1.2.0-synapse3.3-spark3.3-hadoop3.3
Show newest version
/*
 * Copyright (C) 2020 The Flowman Authors
 *
 * 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 com.dimajix.flowman.common;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import java.util.Properties;

import lombok.val;
import org.apache.log4j.PropertyConfigurator;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public final class Logging {
    private static Logger logger = null;
    private static Properties log4j1Properties = null;
    private static String level = null;
    private static boolean color = true;

    private Logging() {
    }

    private static Logger getLogger() {
        if (logger == null) {
            logger = LoggerFactory.getLogger(Logging.class);
        }
        return logger;
    }

    public static void init() {
        val log4jConfig = System.getProperty("log4j.configuration");
        if (isCustomLog4j2()) {
            // Do nothing, config is already correctly loaded
        }
        else if (log4jConfig != null) {
            URL url;
            try {
                url = new URL(log4jConfig);
            }
            catch (MalformedURLException ex) {
                try {
                    url = new File(log4jConfig).toURI().toURL();
                }
                catch (MalformedURLException ex2) {
                    url = null;
                }
            }

            if (url != null) {
                initlog4j1(url);
            }
        }
        else {
            initDefault();
        }
    }

    public static void setColorEnabled(boolean enabled) {
        color = enabled;
        if (isLog4j2() && !isCustomLog4j2()) {
            initDefault();
        }
    }

    private static void initDefault() {
        val loader = Thread.currentThread().getContextClassLoader();
        val log4j2cfg = loader.getResource("META-INF/flowman/conf/log4j2.properties");
        if (log4j2cfg != null) {
            LoggingImplLog4j2.init(log4j2cfg, color);
        }
        else {
            val log4jcfg = loader.getResource("META-INF/flowman/conf/log4j.properties");
            if (log4jcfg != null) {
                initlog4j1(log4jcfg);
            }
            else {
                if (isLog4j2()) {
                    val url = loader.getResource("com/dimajix/flowman/log4j2-defaults.properties");
                    LoggingImplLog4j2.init(url, color);
                } else {
                    val url = loader.getResource("com/dimajix/flowman/log4j-defaults.properties");
                    initlog4j1(url);
                }
            }
        }
    }

    private static void initlog4j1(URL configUrl) {
        log4j1Properties = loadProperties(configUrl);
        reconfigureLogging();
        getLogger().debug("Loaded logging configuration from " + configUrl);
    }

    private static Properties loadProperties(URL url) {
        try {
            val urlConnection = url.openConnection();
            urlConnection.setUseCaches(false);
            val inputStream = urlConnection.getInputStream();
            try {
                val loaded = new Properties();
                loaded.load(inputStream);
                return loaded;
            }
            finally {
                inputStream.close();
            }
        } catch (IOException e) {
            getLogger().warn("Could not read configuration file from URL [" + url + "].", e);
            getLogger().warn("Ignoring configuration file [" + url + "].");
            return null;
        }
    }

    public static void setSparkLogging(String logLevel) {
        // Adjust Spark logging level
        getLogger().debug("Setting Spark log level to " + logLevel);

        val upperCased = logLevel.toUpperCase(Locale.ENGLISH);
        val l = org.apache.log4j.Level.toLevel(upperCased);
        org.apache.log4j.Logger.getLogger("org").setLevel(l);
        org.apache.log4j.Logger.getLogger("akka").setLevel(l);
        org.apache.log4j.Logger.getLogger("hive").setLevel(l);
    }

    public static void setLogging(String logLevel) {
        getLogger().debug("Setting global log level to " + logLevel);
        level = logLevel.toUpperCase(Locale.ENGLISH);

        reconfigureLogging();
    }

    private static void reconfigureLogging() {
        // Create new logging properties with all levels being replaced
        if (log4j1Properties != null) {
            LoggingImplLog4j1.reconfigure(log4j1Properties, level);
        }
        else if (isLog4j2()) {
            if (level != null) {
                LoggingImplLog4j2.reconfigure(level);
            }
        }

        if (level != null) {
            val l = org.apache.log4j.Level.toLevel(level);
            org.apache.log4j.Logger.getRootLogger().setLevel(l);
        }
    }

    private static boolean isLog4j2() {
        // This distinguishes the log4j 1.2 binding, currently
        // org.slf4j.impl.Log4jLoggerFactory, from the log4j 2.0 binding, currently
        // org.apache.logging.slf4j.Log4jLoggerFactory
        val binderClass = LoggerFactory.getILoggerFactory().getClass().getName();
        //val binderClass = StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr();
        return "org.apache.logging.slf4j.Log4jLoggerFactory".equals(binderClass);
    }

    private static boolean isCustomLog4j2() {
        val log4jConfigFile = System.getProperty("log4j.configurationFile");
        val log4j2ConfigFile = System.getProperty("log4j2.configurationFile");
        return isLog4j2() && (log4jConfigFile != null || log4j2ConfigFile != null);
    }


    private static class LoggingImplLog4j1 {
        public static void reconfigure(Properties props, String level) {
            val newProps = new Properties();
            props.forEach((k,v) -> newProps.setProperty((String)k, (String)v));
            if (level != null) {
                for (val key : newProps.keySet()) {
                    if (key instanceof String) {
                        val skey = (String)key;
                        if (skey.startsWith("log4j.logger."))
                            newProps.setProperty(skey, level);
                    }
                }
            }
            PropertyConfigurator.configure(newProps);
        }
    }


/**
 * Utility class for isolating log4j 2.x dependencies. Otherwise a ClassNotFoundException may be thrown if log4j 2.x
 * is not on the classpath, even if the code is not executed.
 */
    private static class LoggingImplLog4j2 {
        public static void init(URL configUrl, boolean color) {
            val props = Logging.loadProperties(configUrl);
            // Disable ANSI magic by adding new entries to the log4j config
            if (!color) {
                for (val key : props.keySet()) {
                    if (key instanceof String) {
                        val skey = (String) key;
                        val parts = skey.split(".");
                        if (parts.length >= 3 && parts[0].equals("appender") && parts[2].equals("layout"))
                            props.setProperty("appender." + parts[1] + ".layout.disableAnsi", "true");
                    }
                }
            }
            val config = new PropertiesConfigurationBuilder()
                .setRootProperties(props)
                .build();
            val context = (LoggerContext)LogManager.getContext(false);
            context.start(config);
        }

        public static void reconfigure(String level) {
            val context =  (LoggerContext) LogManager.getContext(false);
            val l = Level.getLevel(level);
            val config = context.getConfiguration();
            config.getRootLogger().setLevel(l);
            config.getLoggers().values().forEach(x -> x.setLevel(l));
            context.updateLoggers();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy