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

org.apache.sshd.common.util.logging.LoggingUtils Maven / Gradle / Ivy

There is a newer version: 2.14.0
Show newest version
/*
 * 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.sshd.common.util.logging;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;

import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.ReflectionUtils;
import org.slf4j.Logger;

/**
 * @author Apache MINA SSHD Project
 */
public final class LoggingUtils {

    private LoggingUtils() {
        throw new UnsupportedOperationException("No instance");
    }

    /**
     * Scans using reflection API for all fields that are {@code public static final}
     * that start with the given common prefix (case sensitive) and are of type
     * {@link Number}.
     *
     * @param clazz The {@link Class} to query
     * @param commonPrefix The expected common prefix
     * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
     * value and mapping=the field's name
     * @see #generateMnemonicMap(Class, Predicate)
     */
    public static NavigableMap generateMnemonicMap(Class clazz, final String commonPrefix) {
        return generateMnemonicMap(clazz, f -> {
            String name = f.getName();
            return name.startsWith(commonPrefix);
        });
    }

    /**
     * Scans using reflection API for all numeric {@code public static final} fields
     * that are also accepted by the predicate. Any field that is not such or fail to retrieve
     * its value, or has a duplicate value is silently skipped.
     *
     * @param clazz The {@link Class} to query
     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
     * (besides being a {@link Number} and {@code public static final}).
     * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
     * value and mapping=the field's name
     * @see #getMnemonicFields(Class, Predicate)
     */
    public static NavigableMap generateMnemonicMap(Class clazz, Predicate acceptor) {
        Collection fields = getMnemonicFields(clazz, acceptor);
        if (GenericUtils.isEmpty(fields)) {
            return Collections.emptyNavigableMap();
        }

        NavigableMap result = new TreeMap<>(Comparator.naturalOrder());
        for (Field f : fields) {
            String name = f.getName();
            try {
                Number value = (Number) f.get(null);
                String prev = result.put(NumberUtils.toInteger(value), name);
                if (prev != null) {
                    //noinspection UnnecessaryContinue
                    continue;   // debug breakpoint
                }
            } catch (Exception e) {
                //noinspection UnnecessaryContinue
                continue;   // debug breakpoint
            }
        }

        return result;
    }

    /**
     * Scans using reflection API for all numeric {@code public static final} fields
     * that have a common prefix and whose value is used by several of the other
     * matching fields
     *
     * @param clazz The {@link Class} to query
     * @param commonPrefix The expected common prefix
     * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
     * fields in this map. The key is the field's name and value is its associated opcode.
     * @see #getAmbiguousMenmonics(Class, Predicate)
     */
    public static Map getAmbiguousMenmonics(Class clazz, String commonPrefix) {
        return getAmbiguousMenmonics(clazz, f -> {
            String name = f.getName();
            return name.startsWith(commonPrefix);
        });
    }

    /**
     * Scans using reflection API for all numeric {@code public static final} fields
     * that are also accepted by the predicate and whose value is used by several of the other
     * matching fields
     *
     * @param clazz The {@link Class} to query
     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
     * (besides being a {@link Number} and {@code public static final}).
     * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
     * fields in this map. The key is the field's name and value is its associated opcode.
     * @see #getMnemonicFields(Class, Predicate)
     */
    public static Map getAmbiguousMenmonics(Class clazz, Predicate acceptor) {
        Collection fields = getMnemonicFields(clazz, acceptor);
        if (GenericUtils.isEmpty(fields)) {
            return Collections.emptyMap();
        }

        Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        Map> opcodesMap = new TreeMap<>(Comparator.naturalOrder());
        for (Field f : fields) {
            String name = f.getName();
            try {
                Number value = (Number) f.get(null);
                Integer key = NumberUtils.toInteger(value);
                List nameList = opcodesMap.get(key);
                if (nameList == null) {
                    nameList = new ArrayList<>();
                    opcodesMap.put(key, nameList);
                }
                nameList.add(name);

                int numOpcodes = nameList.size();
                if (numOpcodes > 1) {
                    result.put(name, key);
                    if (numOpcodes == 2) {  // add the 1st name as well
                        result.put(nameList.get(0), key);
                    }
                }
            } catch (Exception e) {
                continue;   // debug breakpoint
            }
        }

        return result;
    }

    /**
     * Scans using reflection API for all numeric {@code public static final} fields
     * that are also accepted by the predicate.
     *
     * @param clazz The {@link Class} to query
     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
     * (besides being a {@link Number} and {@code public static final}).
     * @return A {@link Collection} of all the fields that have satisfied all conditions
     */
    public static Collection getMnemonicFields(Class clazz, Predicate acceptor) {
        return ReflectionUtils.getMatchingFields(clazz, f -> {
            int mods = f.getModifiers();
            if ((!Modifier.isPublic(mods)) || (!Modifier.isStatic(mods)) || (!Modifier.isFinal(mods))) {
                return false;
            }

            Class type = f.getType();
            if (!NumberUtils.isNumericClass(type)) {
                return false;
            }

            return acceptor.test(f);
        });
    }

    /**
     * Verifies if the given level is above the required threshold for logging.
     *
     * @param level     The {@link Level} to evaluate
     * @param threshold The threshold {@link Level}
     * @return {@code true} if the evaluated level is above the required
     * threshold.
     * 

* Note(s): *

*
    *
  • * If either argument is {@code null} then result is {@code false}. *

  • * *
  • * If the evaluated level is {@link Level#OFF} then result is {@code false} * regardless of the threshold. *

  • * *
  • * If the threshold is {@link Level#ALL} and the evaluated level is * not {@link Level#OFF} the result is {@code true}. *

  • * *
  • * Otherwise, the evaluated level {@link Level#intValue()} must be * greater or equal to the threshold. *

  • *
*/ public static boolean isLoggable(Level level, Level threshold) { if ((level == null) || (threshold == null)) { return false; } else if (Level.OFF.equals(level) || Level.OFF.equals(threshold)) { return false; } else if (Level.ALL.equals(threshold)) { return true; } else { return level.intValue() >= threshold.intValue(); } } public static SimplifiedLog wrap(final Logger logger) { if (logger == null) { return SimplifiedLog.EMPTY; } else { return new SimplifiedLog() { @Override public void log(Level level, Object message, Throwable t) { if (isEnabled(level)) { logMessage(logger, level, message, t); } } @Override public boolean isEnabled(Level level) { return isLoggable(logger, level); } }; } } // NOTE: assume that level enabled has been checked !!! public static void logMessage(Logger logger, Level level, Object message, Throwable t) { if ((logger == null) || (level == null) || Level.OFF.equals(level)) { return; } else if (Level.SEVERE.equals(level)) { logger.error(Objects.toString(message), t); } else if (Level.WARNING.equals(level)) { logger.warn(Objects.toString(message), t); } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) { logger.info(Objects.toString(message), t); } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) { logger.debug(Objects.toString(message), t); } else { logger.trace(Objects.toString(message), t); } } /** * @param logger The {@link Logger} instance - ignored if {@code null} * @param level The validate log {@link Level} - ignored if {@code null} * @return

{@code true} if the level is enabled for the logger. The * mapping of the level to the logger is as follows:

*
    *
  • {@link Level#OFF} always returns {@code false}
  • *
  • {@link Level#SEVERE} returns {@link Logger#isErrorEnabled()}
  • *
  • {@link Level#WARNING} returns {@link Logger#isWarnEnabled()}
  • *
  • {@link Level#INFO} and {@link Level#ALL} returns {@link Logger#isInfoEnabled()}
  • *
  • {@link Level#CONFIG} and {@link Level#FINE} returns {@link Logger#isDebugEnabled()}
  • *
  • All other levels return {@link Logger#isTraceEnabled()}
  • *
*/ public static boolean isLoggable(Logger logger, Level level) { if ((logger == null) || (level == null) || Level.OFF.equals(level)) { return false; } else if (Level.SEVERE.equals(level)) { return logger.isErrorEnabled(); } else if (Level.WARNING.equals(level)) { return logger.isWarnEnabled(); } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) { return logger.isInfoEnabled(); } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) { return logger.isDebugEnabled(); } else { return logger.isTraceEnabled(); } } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @param level The log {@link Level} mapped as follows:
* *
    *
  • {@link Level#OFF} - {@link #nologClosure(Logger)}
  • *
  • {@link Level#SEVERE} - {@link #errorClosure(Logger)}
  • *
  • {@link Level#WARNING} - {@link #warnClosure(Logger)}
  • *
  • {@link Level#INFO}/{@link Level#ALL} - {@link #infoClosure(Logger)}
  • *
  • {@link Level#CONFIG}/{@link Level#FINE} - {@link #debugClosure(Logger)}
  • *
  • All others - {@link #traceClosure(Logger)}
  • *
* @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if the specific level is enabled */ public static Consumer loggingClosure(Logger logger, Level level) { return loggingClosure(logger, level, null); } public static Consumer loggingClosure(Logger logger, Level level, Throwable t) { Objects.requireNonNull(level, "No level provided"); if (Level.OFF.equals(level)) { return nologClosure(logger); } else if (Level.SEVERE.equals(level)) { return errorClosure(logger, t); } else if (Level.WARNING.equals(level)) { return warnClosure(logger, t); } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) { return infoClosure(logger, t); } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) { return debugClosure(logger, t); } else { return traceClosure(logger, t); } } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @return A consumer whose {@link Consumer#accept(Object)} method logs nothing when invoked */ public static Consumer nologClosure(Logger logger) { Objects.requireNonNull(logger, "No logger provided"); return t -> { /* do nothing */ }; } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()} */ public static Consumer errorClosure(Logger logger) { return errorClosure(logger, null); } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null} * @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()} */ public static Consumer errorClosure(Logger logger, Throwable thrown) { Objects.requireNonNull(logger, "No logger provided"); return new Consumer() { @Override public void accept(T input) { if (logger.isErrorEnabled()) { String msg = String.valueOf(input); if (thrown == null) { logger.error(msg); } else { logger.error(msg, thrown); } } } @Override public String toString() { return "ERROR"; } }; } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if {@link Logger#isWarnEnabled()} */ public static Consumer warnClosure(Logger logger) { return warnClosure(logger, null); } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null} * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)} * value of its argument if {@link Logger#isWarnEnabled()} */ public static Consumer warnClosure(Logger logger, Throwable thrown) { Objects.requireNonNull(logger, "No logger provided"); return new Consumer() { @Override public void accept(T input) { if (logger.isWarnEnabled()) { String msg = String.valueOf(input); if (thrown == null) { logger.warn(msg); } else { logger.warn(msg, thrown); } } } @Override public String toString() { return "WARN"; } }; } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)} * value of its argument if {@link Logger#isInfoEnabled()} */ public static Consumer infoClosure(Logger logger) { return infoClosure(logger, null); } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null} * @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if {@link Logger#isInfoEnabled()} */ public static Consumer infoClosure(Logger logger, Throwable thrown) { Objects.requireNonNull(logger, "No logger provided"); return new Consumer() { @Override public void accept(T input) { if (logger.isInfoEnabled()) { String msg = String.valueOf(input); if (thrown == null) { logger.info(msg); } else { logger.info(msg, thrown); } } } @Override public String toString() { return "INFO"; } }; } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()} */ public static Consumer debugClosure(Logger logger) { return debugClosure(logger, null); } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null} * @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()} */ public static Consumer debugClosure(Logger logger, Throwable thrown) { Objects.requireNonNull(logger, "No logger provided"); return new Consumer() { @Override public void accept(T input) { if (logger.isDebugEnabled()) { String msg = String.valueOf(input); if (thrown == null) { logger.debug(msg); } else { logger.debug(msg, thrown); } } } @Override public String toString() { return "DEBUG"; } }; } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()} */ public static Consumer traceClosure(Logger logger) { return traceClosure(logger, null); } /** * @param Generic message type consumer * @param logger The {@link Logger} instance to use * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null} * @return A consumer whose {@link Consumer#accept(Object)} method logs the * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()} */ public static Consumer traceClosure(Logger logger, Throwable thrown) { Objects.requireNonNull(logger, "No logger provided"); return new Consumer() { @Override public void accept(T input) { if (logger.isTraceEnabled()) { String msg = String.valueOf(input); if (thrown == null) { logger.trace(msg); } else { logger.trace(msg, thrown); } } } @Override public String toString() { return "TRACE"; } }; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy