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

com.icthh.xm.commons.logging.util.LogObjectPrinter Maven / Gradle / Ivy

There is a newer version: 4.0.21
Show newest version
package com.icthh.xm.commons.logging.util;

import com.icthh.xm.commons.logging.LoggingAspectConfig;
import com.icthh.xm.commons.logging.config.LoggingConfig.LogConfiguration.LogInput;
import com.icthh.xm.commons.logging.config.LoggingConfig.LogConfiguration.LogResult;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.IntStream;

import static com.icthh.xm.commons.logging.config.LoggingConfig.DEFAULT_LOG_INPUT_COLLECTION_AWARE;
import static com.icthh.xm.commons.logging.config.LoggingConfig.DEFAULT_LOG_INPUT_DETAILS;
import static com.icthh.xm.commons.logging.config.LoggingConfig.DEFAULT_LOG_RESULT_COLLECTION_AWARE;
import static com.icthh.xm.commons.logging.config.LoggingConfig.DEFAULT_LOG_RESULT_DETAILS;

/**
 * Utility class for object printing in Logging aspects.
 */
@Slf4j
public final class LogObjectPrinter {

    private static final String XM_PACKAGE_NAME = "com.icthh.xm";
    private static final String PASSWORD_MASK = "*****";
    private static final Set MASK_SET = new HashSet<>();
    private static final String[] EMPTY_ARRAY = new String[0];
    private static final String PRINT_EMPTY_LIST = "[]";
    private static final String PRINT_HIDDEN = "#hidden#";
    private static final String PRINT_QUESTION = "?";
    private static final String PRINT_SEMICOLON = ":";
    private static final String PRINT_EMPTY_METHOD = PRINT_QUESTION + PRINT_SEMICOLON + PRINT_QUESTION;


    static {
        MASK_SET.add("newPassword");
        MASK_SET.add("password");
        MASK_SET.add("defaultPassword");
        MASK_SET.add("secret");
    }

    private LogObjectPrinter() {
    }

    /**
     * Builds log string for exception.
     *
     * @param throwable the exception
     * @return exception description string
     */
    public static String printException(Throwable throwable) {
        return String.valueOf(throwable);
    }

    /**
     * Builds log string for exception with stack trace.
     *
     * @param throwable the exception
     * @return exception description string with stack trace
     */
    public static String printExceptionWithStackInfo(Throwable throwable) {
        StringBuilder out = new StringBuilder();
        printExceptionWithStackInfo(throwable, out);
        return out.toString();
    }

    private static void printExceptionWithStackInfo(Throwable throwable, StringBuilder out) {
        out.append(throwable);
        if (throwable != null) {
            appendStackTrace(throwable, out);
            if (throwable.getCause() != null) {
                out.append(" -> ");
                printExceptionWithStackInfo(throwable.getCause(), out);
            }
        }
    }

    private static void appendStackTrace(Throwable throwable, StringBuilder out) {
        val stackTrace = throwable.getStackTrace();
        if (stackTrace == null || stackTrace.length < 1) {
            return;
        }

        out.append(' ').append(stackTrace[0]);

        for (int i = 1; i < stackTrace.length; i++) {
            String stackTraceLine = String.valueOf(stackTrace[i]);
            if (stackTrace[i].getClassName().startsWith(XM_PACKAGE_NAME) && !stackTraceLine.contains("")) {
                out.append(" ... ").append(stackTraceLine).append("...");
                break;
            }
        }
    }

    /**
     * Join URL path into one string.
     *
     * @param arr  first url paths
     * @param arr2 other url paths
     * @param   url part path type
     * @return URL representation string
     */
    @SafeVarargs
    public static  String joinUrlPaths(final T[] arr, final T... arr2) {
        try {
            T[] url = ArrayUtils.addAll(arr, arr2);
            String res = StringUtils.join(url);
            return (res == null) ? "" : res;
        } catch (IndexOutOfBoundsException | IllegalArgumentException | ArrayStoreException e) {
            log.warn("Error while join URL paths from: {}, {}", arr, arr2);
            return "printerror:" + e;
        }
    }

    /**
     * Gets method description string from join point.
     *
     * @param joinPoint aspect join point
     * @return method description string from join point
     */
    public static String getCallMethod(JoinPoint joinPoint) {
        if (joinPoint != null && joinPoint.getSignature() != null) {
            Class declaringType = joinPoint.getSignature().getDeclaringType();
            String className = (declaringType != null) ? declaringType.getSimpleName() : PRINT_QUESTION;
            String methodName = joinPoint.getSignature().getName();
            return className + PRINT_SEMICOLON + methodName;
        }
        return PRINT_EMPTY_METHOD;
    }

    /**
     * Gets join point input params description string.
     *
     * @param joinPoint         aspect join point
     * @param includeParamNames input parameters names to be printed. NOTE! can be overridden with @{@link
     *                          LoggingAspectConfig}
     * @return join point input params description string
     */
    public static String printInputParams(JoinPoint joinPoint, String... includeParamNames) {
        try {
            if (joinPoint == null) {
                return "joinPoint is null";
            }

            Signature signature = joinPoint.getSignature();
            if (!(signature instanceof MethodSignature)) {
                return PRINT_EMPTY_LIST;
            }

            Optional config = AopAnnotationUtils.getConfigAnnotation(joinPoint);

            String[] includeParams = includeParamNames;
            String[] excludeParams = EMPTY_ARRAY;
            boolean inputCollectionAware = LoggingAspectConfig.DEFAULT_INPUT_COLLECTION_AWARE;

            if (config.isPresent()) {
                if (!config.get().inputDetails()) {
                    return PRINT_HIDDEN;
                }
                inputCollectionAware = config.get().inputCollectionAware();
                if (ArrayUtils.isNotEmpty(config.get().inputIncludeParams())) {
                    includeParams = config.get().inputIncludeParams();
                }
                if (ArrayUtils.isEmpty(includeParams) && ArrayUtils.isNotEmpty(config.get().inputExcludeParams())) {
                    excludeParams = config.get().inputExcludeParams();
                }
            }

            MethodSignature ms = (MethodSignature) signature;
            String[] params = ms.getParameterNames();
            return ArrayUtils.isNotEmpty(params) ? renderParams(joinPoint,
                                                                params,
                                                                includeParams,
                                                                excludeParams,
                                                                inputCollectionAware) : PRINT_EMPTY_LIST;
        } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
            log.warn("Error while print params: {}, params = {}", e, joinPoint.getArgs());
            return "printerror: " + e;
        }
    }

    /**
     * Gets join point input params description string.
     *
     * @param joinPoint - aspect join point
     * @param config - input parameters config {@link LogInput}
     * @return join point input params description string
     */
    public static String printInputParams(JoinPoint joinPoint, LogInput config) {
        try {
            if (joinPoint == null) {
                return "joinPoint is null";
            }

            if (config == null) {
                return printInputParams(joinPoint);
            }

            Signature signature = joinPoint.getSignature();
            if (!(signature instanceof MethodSignature)) {
                return PRINT_EMPTY_LIST;
            }

            if (config.getDetails() == null) {
                config.setDetails(DEFAULT_LOG_INPUT_DETAILS);
            }

            if (config.getCollectionAware() == null) {
                config.setCollectionAware(DEFAULT_LOG_INPUT_COLLECTION_AWARE);
            }

            if (!config.getDetails()) {
                return PRINT_HIDDEN;
            }

            if (config.getExcludeParams() == null) {
                config.setExcludeParams(List.of());
            }

            if (CollectionUtils.isNotEmpty(config.getIncludeParams())) {
                config.setExcludeParams(List.of());
            } else {
                config.setIncludeParams(List.of());
            }

            MethodSignature ms = (MethodSignature) signature;
            String[] params = ms.getParameterNames();
            return ArrayUtils.isNotEmpty(params) ? renderParams(joinPoint,
                params,
                config.getIncludeParams().toArray(String[]::new),
                config.getExcludeParams().toArray(String[]::new),
                config.getCollectionAware()) : PRINT_EMPTY_LIST;
        } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
            log.warn("Error while print params: {}, params = {}", e, joinPoint.getArgs());
            return "printerror: " + e;
        }
    }

    static String renderParams(JoinPoint joinPoint, String[] params, String[] includeParamNames,
                                       String[] excludeParamNames, boolean inputCollectionAware) {

        Set includeSet = prepareNameSet(includeParamNames);
        Set excludeSet = prepareNameSet(excludeParamNames);
        List requestList = new ArrayList<>();

        Map paramMap = joinPointToParamMap(joinPoint, params);

        if (!includeSet.isEmpty()) {
            includeSet
                .stream().filter(paramMap::containsKey)
                .forEach(key -> requestList.add(buildParam(key, paramMap.get(key), inputCollectionAware)));
        } else if (!excludeSet.isEmpty()) {
            paramMap.forEach((key, value) -> {
                if (!excludeSet.contains(key)) {
                    requestList.add(buildParam(key, value, inputCollectionAware));
                }
            });
        } else {
            paramMap.forEach((key, value) -> requestList.add(buildParam(key, value, inputCollectionAware)));
        }

        return StringUtils.join(requestList, ',');
    }

    private static Map joinPointToParamMap(JoinPoint joinPoint, String[] params) {
        val map = new LinkedHashMap();
        IntStream.range(0, params.length).boxed().forEach(index -> map.put(params[index], joinPoint.getArgs()[index]));
        return map;
    }

    private static Set prepareNameSet(String[] paramNames) {
        return !ArrayUtils.isEmpty(paramNames) ? new LinkedHashSet<>(Arrays.asList(paramNames)) : Collections
            .emptySet();
    }

    private static String buildParam(String name, Object value, boolean inputCollectionAware) {

        StringBuilder builder = new StringBuilder(name).append("=");
        if (MASK_SET.contains(name)) {
            builder.append(PASSWORD_MASK);
        } else if (inputCollectionAware) {
            builder.append(printCollectionAware(value));
        } else {
            builder.append(printTypeAware(value));
        }
        return builder.toString();
    }

    /**
     * Print Result object according to input parameters and {@link LoggingAspectConfig}.
     * @param joinPoint         - intercepting join point
     * @param object            - result value to be printed
     */
    public static String printResult(final JoinPoint joinPoint, final Object object) {
        return printResult(joinPoint, object, LoggingAspectConfig.DEFAULT_RESULT_DETAILS);
    }

    /**
     * Print Result object according to input parameters and {@link LoggingAspectConfig}.
     *
     * @param joinPoint         - intercepting join point
     * @param object            - result value to be printed
     * @param printResultDetail - print result detail flag for programming approach. NOTE! can me overridden with {@link
     *                          LoggingAspectConfig}
     */
    public static String printResult(final JoinPoint joinPoint, final Object object, final boolean printResultDetail) {

        Optional config = AopAnnotationUtils.getConfigAnnotation(joinPoint);

        boolean resultDetails = printResultDetail;
        boolean resultCollectionAware = LoggingAspectConfig.DEFAULT_RESULT_COLLECTION_AWARE;

        if (config.isPresent()) {
            resultDetails = config.get().resultDetails();
            resultCollectionAware = config.get().resultCollectionAware();
        }

        if (!resultDetails) {
            return PRINT_HIDDEN;
        }

        if (resultCollectionAware) {
            return printCollectionAware(object);
        }

        return String.valueOf(object);

    }

    /**
     * Print Result object according to input parameters and {@link LogResult}.
     *
     * @param joinPoint - intercepting join point
     * @param object - result value to be printed
     * @param config - result config {@link LogResult}
     */
    public static String printResult(final JoinPoint joinPoint, final Object object, LogResult config) {
        if (config == null) {
            return printResult(joinPoint, object);
        }

        if (config.getResultDetails() == null) {
            config.setResultDetails(DEFAULT_LOG_RESULT_DETAILS);
        }

        if (config.getResultCollectionAware() == null) {
            config.setResultCollectionAware(DEFAULT_LOG_RESULT_COLLECTION_AWARE);
        }

        if (!config.getResultDetails()) {
            return PRINT_HIDDEN;
        }

        if (config.getResultCollectionAware()) {
            return printCollectionAware(object);
        }

        return String.valueOf(object);
    }

    /**
     * Gets object representation with size for collection case.
     *
     * @param object object instance to log
     * @return object representation with size for collection case
     */
    public static String printCollectionAware(final Object object) {
        return printCollectionAware(object, true);
    }

    /**
     * Gets object representation with size for collection case.
     *
     * @param object    object instance to log
     * @param printBody if {@code true} then prevent object string representation
     * @return object representation with size for collection case
     */
    public static String printCollectionAware(final Object object, final boolean printBody) {

        if (!printBody) {
            return PRINT_HIDDEN;
        }

        if (object == null) {
            return "null";
        }

        Class clazz = object.getClass();
        if (Collection.class.isAssignableFrom(clazz)) {
            return "[<"
                   + clazz.getSimpleName()
                   + "> size = "
                   + ((Collection) object).size() + "]";
        } else if (clazz.isArray()) {
            return "[<"
                   + clazz.getSimpleName()
                   + "> length = "
                   + Array.getLength(object) + "]";
        }

        return object.toString();

    }

    private static String printTypeAware(Object value) {
        if (value == null) {
            return null;
        }
        if (value.getClass().isArray()) {
            return Arrays.toString((Object[]) value);
        }
        return value.toString();
    }

    public enum Level {
        TRACE, DEBUG, INFO, WARN, ERROR, OFF_LOG
    }

    /**
     * Log a message at the target level according to the specified format
     * and arguments.
     * @param log org.slf4j.Logger for keep the original loggerName
     * @param level logging level
     * @param format format string
     * @param argArray list of arguments
     */
    public static void logWithLevel(Logger log, Level level, String format, Object... argArray) {
        switch (level) {
            case OFF_LOG:
                break;
            case TRACE:
                log.trace(format, argArray);
                break;
            case DEBUG:
                log.debug(format, argArray);
                break;
            case INFO:
                log.info(format, argArray);
                break;
            case WARN:
                log.warn(format, argArray);
                break;
            case ERROR:
                log.error(format, argArray);
                break;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy