org.apache.sshd.common.util.logging.LoggingUtils Maven / Gradle / Ivy
/*
* 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 super Field> 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 super Field> 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 super Field> 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