java:反射封装Slf4j日志工具类

java:反射封装Slf4j日志工具类

1 前言

使用Slf4j时,针对日志打印进行工具类封装使用。

依赖如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <version>2.5.4</version>
    <artifactId>spring-boot-starter-parent</artifactId>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--  slf4j 日志门面  -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.32</version>
    </dependency>

    <!-- logback日志实现 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.5</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>

    <!--  spring-boot 3.12.0  -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>

</dependencies>

2 使用

Slf4j日志工具类,反射封装:

LogUtils:

package com.xiaoxu.crawler.utils;

import com.xiaoxu.crawler.base.constant.PlaceholderConstant;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Date;


/**
 * @author xiaoxu
 * @date 2022-11-21 10:52
 * crawlerJ:com.xiaoxu.crawler.utils.LogUtil
 */
public class LogUtils implements PlaceholderConstant {

    /* log日志时间pattern */
    private static final String logPattern = "yyyy-MM-dd HH:mm:ss.SSS";

    /* 日志方法stack层级 */
    private static final int logStackHierarchy = 6;

    public static <R> void logDetailInfo(Logger logger, String methodName, R requestPayload,
                                 long timeConsuming){
        String msg = logDetailCheck(methodName, timeConsuming);
        info(logger, msg, methodName, requestPayload, timeConsuming);
    }

    public static <R> void logDetailWarn(Logger logger, String methodName, R requestPayload,
                                         long timeConsuming){
        String msg = logDetailCheck(methodName, timeConsuming);
        warn(logger, msg, methodName, requestPayload, timeConsuming);
    }

    public static <R> void logDetailError(Logger logger, String methodName, R requestPayload,
                                         long timeConsuming){
        String msg = logDetailCheck(methodName, timeConsuming);
        error(logger, msg, methodName, requestPayload, timeConsuming);
    }

    private static String logDetailCheck(String methodName, long timeConsuming) {
        AssertUtils.assertTrue(timeConsuming >= 0, "日志耗时参数有误, 请检查");
        AssertUtils.assertNonEmpty(methodName, "日志请求方法参数有误, 请检查");
        return "调用"+squarePrefix+"{}"+squareSuffix+"方法, 请求参数:"+squarePrefix+"{}"+squareSuffix+", 耗时:"+squarePrefix+"{}"+squareSuffix+"ms.";
    }

    public static void info(Logger logger, String formatMsg, Object... objects){
        commLogExecute(logger, formatMsg, objects);
    }

    public static void warn(Logger logger, String formatMsg, Object... objects){
        commLogExecute(logger, formatMsg, objects);
    }

    public static void error(Logger logger, String formatMsg, Object... objects){
        commLogExecute(logger, formatMsg, objects);
    }

    private static void commLogExecute(Logger logger, String formatMsg, Object[] objects) {
        logCheck(logger, formatMsg, objects);
        ReflectExecuteLog(logger, formatMsg, objects);
    }

    private static void ReflectExecuteLog(Logger logger, String formatMsg, Object[] objects) {
        IfElseUtils.executeBothSide(ArrayUtils.isNotEmpty(objects), new IfElseUtils.ActionCallBack() {
            @Override
            public void actionTrue() {
                /* logger.info(buildWrapMessage(formatMsg), objects); */
                Method logMethodByName = getReflectLogMethod("执行actionTrue失败,日志方法未匹配到!{0}");
                try {
                    if(null != logMethodByName){
                        logMethodByName.invoke(logger, buildWrapMessage(formatMsg, logMethodByName.getName()), objects);
                    }
                } catch (IllegalAccessException | InvocationTargetException e) {
                    ExcpUtils.throwExp(MessageFormat.format("反射执行logger方法失败!{0}",e.getMessage()));
                }
            }

            @Override
            public void actionFalse() {
                /* logger.info(buildWrapMessage(formatMsg)); */
                Method logMethodByName = getReflectLogMethod("执行actionFalse失败,日志方法未匹配到!{0}");
                try {
                    if(null != logMethodByName){
                        logMethodByName.invoke(logger,buildWrapMessage(formatMsg, logMethodByName.getName()), null);
                    }
                } catch (IllegalAccessException | InvocationTargetException e) {
                    ExcpUtils.throwExp(MessageFormat.format("反射执行logger方法失败!{0}",e.getMessage()));
                }
            }

            private Method getReflectLogMethod(String msg) {
                Method logMethodByName = LogMethods.getLogMethodByName(Thread.currentThread().getStackTrace()[logStackHierarchy].getMethodName());
                AssertUtils.assertNonNull(logMethodByName, MessageFormat
                        .format(msg,
                                Thread.currentThread().getStackTrace()[logStackHierarchy].getMethodName()));
                return logMethodByName;
            }
        });
    }

    /* 日志方法映射枚举 */
    enum LogMethods implements LogConstant{

        /* 日志方法枚举映射 */
        INFO(LogConstant.INFO,
                handleEnumerationExp(LogConstant.INFO)),
        WARN(LogConstant.WARN,
                handleEnumerationExp(LogConstant.WARN)),
        ERROR(LogConstant.ERROR,
                handleEnumerationExp(LogConstant.ERROR));

        private String logMethodName;
        private Method logMethod;

        LogMethods(String logMethodName, Method logMethod){
            this.logMethodName = logMethodName;
            this.logMethod = logMethod;
            AssertUtils.assertTrue(null != logMethodName && logMethodName.equals(logMethod.getName()), "初始化日志名与日志方法失败!!不匹配.");
        }

        private static Method handleEnumerationExp(String logMethodName){
            Method method = null;
            try {
                method = Logger.class.getMethod(logMethodName, String.class, Object[].class);
            } catch (NoSuchMethodException e) {
                ExcpUtils.throwExp(MessageFormat.format("NoSuchMethod:日志枚举方法初始化异常!!!{0},日志枚举方法名称:{1}.",
                        e.getMessage(), logMethodName));
            } catch (Throwable tx){
                ExcpUtils.throwExp(MessageFormat.format("Throwable:日志枚举方法初始化未知异常!!!{0},日志枚举方法名称:{1}.",
                        tx.getMessage(), logMethodName));
            }
            return method;
        }

        public String getLogMethodName() {
            return logMethodName;
        }

        public void setLogMethodName(String logMethodName) {
            this.logMethodName = logMethodName;
        }

        public Method getLogMethod() {
            return logMethod;
        }

        public void setLogMethod(Method logMethod) {
            this.logMethod = logMethod;
        }

        public static Method getLogMethodByName(String logMethodName){
            if(!StringUtils.hasLength(logMethodName)){
                ExcpUtils.throwExp("请求日志枚举方法getLogMethodByName的logMethodName不能为空");
            }
            for (LogMethods value : LogMethods.values()) {
                if(value.getLogMethodName().equals(logMethodName)){
                    return value.getLogMethod();
                }
            }
            return null;
        }

    }

    interface LogConstant{
        /* 日志级别  */
        String INFO = "info";
        String WARN = "warn";
        String ERROR = "error";
    }

    @SuppressWarnings(value = "all")
    private static String buildWrapMessage(final String msg, final String methodName){
        /* 日志打印补充公共信息 */
        StringBuffer sb = new StringBuffer();
        final String concat = " " + horizontalLineConcat + " ";
        sb.append(squarePrefix);
        sb.append(DateUtils.formatDate(new Date(), logPattern));
        sb.append(concat).append(methodName);
        sb.append(squareSuffix).append(" ");
        sb.append(msg);
        return sb.toString();
    }

    private static void logCheck(Logger logger, String formatMsg, Object... objects){
        AssertUtils.assertNonNull(logger, "log should not be null");
        AssertUtils.assertNonEmpty(formatMsg, "log's format message should not be null or empty");
        if(matchCount(formatMsg, placeholderPrefix, formatMsg, placeholderSuffix)){
            if(StrUtils.getMatchCount(formatMsg, placeholderPrefix) > 0){
                /* slf4j占位符打印日志 */
                AssertUtils.assertTrue(
                        countAndObjLenMatch(StrUtils.getMatchCount(formatMsg, placeholderPrefix), objects),"log's objects should match placeholders.");
            }else{
                AssertUtils.assertTrue(ArrayUtils.isEmpty(objects), "when no placeholders, objects should be null or empty array.");
            }
        }else{
            ExcpUtils.throwExp("please check placeholder's left should match right.");
        }
    }

    private static boolean countAndObjLenMatch(int counts, Object... objects){
        AssertUtils.assertTrue(counts > 0, "countAndObjLenMatch's counts may be wrong.");
        return ArrayUtils.isNotEmpty(objects) && objects.length == counts;
    }

    private static boolean matchCount(final String str1, final String subStr1, final String str2, final String subStr2){
        AssertUtils.assertNonEmpty(str1, "matchCount's str1 should not be null or empty.");
        AssertUtils.assertNonEmpty(str1, "matchCount's subStr1 should not be null or empty.");
        AssertUtils.assertNonEmpty(str1, "matchCount's str2 should not be null or empty.");
        AssertUtils.assertNonEmpty(str1, "matchCount's subStr2 should not be null or empty.");
        return StrUtils.getMatchCount(str1, subStr1) == StrUtils.getMatchCount(str2, subStr2);
    }

    public static void main(String[] args) {
//        info(LoggerFactory.getLogger(LogUtils.class),"今天星期是","1","2");
//        info(LoggerFactory.getLogger(LogUtils.class),"今天星期是{}","1","2");
//        info(LoggerFactory.getLogger(LogUtils.class),"今天星期是{}{","1","2");
        info(LoggerFactory.getLogger(LogUtils.class),"今天星期是{}{}","1","2");
        info(LoggerFactory.getLogger(LogUtils.class),"今天星期是",  new Array[]{});
        warn(LoggerFactory.getLogger(LogUtils.class),"空指针异常!{}",  "please check");
        error(LoggerFactory.getLogger(LogUtils.class),"严重错误!!!{},{}",  "参数:","123");
        logDetailInfo(LoggerFactory.getLogger(LogUtils.class), "getYou", 123,2121);
        logDetailWarn(LoggerFactory.getLogger(LogUtils.class), "getYou", 123,2121);
        logDetailError(LoggerFactory.getLogger(LogUtils.class), "getYou", 123,2121);
        info(LoggerFactory.getLogger(LogUtils.class), "成功!!");
    }

}

PlaceholderConstant:

package com.xiaoxu.crawler.base.constant;

/**
 * @author xiaoxu
 * @date 2022-11-21 12:04
 * crawlerJ:com.xiaoxu.crawler.base.constant.PlaceholderConstant
 */
public interface PlaceholderConstant {

    String placeholderPrefix = "{";

    String placeholderSuffix = "}";

    String squarePrefix = "[";

    String squareSuffix = "]";

    String horizontalLineConcat = "-";

}

DateUtils:

package com.xiaoxu.crawler.utils;

import org.springframework.util.StringUtils;

import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

/**
 * @author xiaoxu
 * @date 2022-11-22 0:00
 * crawlerJ:com.xiaoxu.crawler.utils.DateUtils
 */
public class DateUtils {

    private static final Object lockObj = new Object();

    private static volatile Map<String, ThreadLocal<SimpleDateFormat>> timeLocalMaps = new HashMap<>();


    public static String formatDate(Date date, String pattern){
        AssertUtils.assertNonNull(date, "日期不能为null");
        String format = null;
        try {
            format = getSDF(pattern).format(date);
        } catch (IllegalArgumentException ie) {
            ExcpUtils.throwExp(MessageFormat.format("日期格式化出现无效参数异常!{0}",
                    ie.getMessage()));
        } catch (Throwable th) {
            ExcpUtils.throwExp(MessageFormat.format("日期格式化出现未知异常!{0}",
                    th.getMessage()));
        }
        return format;
    }

    public static Date getDateByStr(String dateStr, String pattern){
        AssertUtils.assertNonEmpty(dateStr, "dateStr should not be null or empty.");
        AssertUtils.assertNonEmpty(pattern, "pattern should not be null or empty.");
        AssertUtils.assertTrue(judgeStrLength(dateStr, pattern), "日期格式或者pattern格式有误!" );
        Date parse = null;
        try {
            parse = getSDF(pattern).parse(dateStr);
        } catch (ParseException th) {
            ExcpUtils.throwExp(MessageFormat.format("日期解析有误!{0}",
                    th.getMessage()));
        } catch (Throwable th) {
            ExcpUtils.throwExp(MessageFormat.format("日期解析未知异常!{0}",
                    th.getMessage()));
        }
        return parse;
    }

    private static boolean judgeStrLength(String str1, String str2){
        if(StringUtils.hasLength(str1) && StringUtils.hasLength(str2)){
            return str1.trim().length() > 0
                    && str1.trim().length() == str2.trim().length();
        }
        return false;
    }

    private static SimpleDateFormat getSDF(String pattern){
        AssertUtils.assertNonEmpty(pattern, "time pattern should not be null or empty.");
        ThreadLocal<SimpleDateFormat> local = timeLocalMaps.get(pattern);
        /* 双重检测锁,为timeLocalMaps增加volatile标识,
         * 禁止指令重排 */
        if(null == local){
            synchronized (lockObj){
                local = timeLocalMaps.get(pattern);
                if(null == local){
                    local = ThreadLocal.withInitial(()->new SimpleDateFormat(pattern));
                    timeLocalMaps.put(pattern, local);
                }
            }
        }
        return local.get();
    }

    private void testDate(){
        long start  = System.currentTimeMillis();
        CountDownLatch countDownLatch = new CountDownLatch(100000);
        for (int i = 0; i < 1000; i++) {
            for(int j = 0; j<100; j++) {
                new Thread(() -> {
                    try {
                        // 每个子线程单独创建sdf变量
                        SimpleDateFormat sdf = DateUtils.getSDF("yyyy-MM-dd");
                        Date parse = null;
                        try {
                            parse = sdf.parse("2022-11-10");
                        } catch (Exception e) {
                            System.out.println("错误!!!!");
                            throw new RuntimeException(e);
                        }
                        System.out.println("日期是" + parse);
                    } finally {
                        countDownLatch.countDown();
                    }
                }, "thread-" + j).start();
            }
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        System.out.println("总耗时:"+(end - start)+"ms");
    }

    public static void main(String[] args) {
        Date dateByStr = getDateByStr("2022-11-20", "yyyy-MM-dd");
        System.out.println(dateByStr);
        String s = formatDate(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
        System.out.println(s);
        String s1 = formatDate(new Date(), "yyyy-MM-dd HH:mm:ss.fff");
        System.out.println(s1);
    }
}

IfElseUtils:

package com.xiaoxu.crawler.utils;

/**
 * @author xiaoxu
 * @date 2022-11-24 0:51
 * crawlerJ:com.xiaoxu.crawler.utils.IfElseUtils
 */
public class IfElseUtils {

    /* if-else void-template */
    public static void executeBothSide(boolean flag, ActionCallBack callBack){
        if(flag){
            callBack.actionTrue();
        }else{
            callBack.actionFalse();
        }
    }

    /* if-else T-value-template */
    public static <T> T executeBothSideWithValue(boolean flag, ActionCallBackWithValue<T> callBack){
        if(flag){
            return callBack.actionTrue();
        }else{
            return callBack.actionFalse();
        }
    }

    public interface ActionCallBackWithValue<T>{
        /* 为真时 */
        T actionTrue();
        /* 为假时 */
        T actionFalse();
    }

    public interface ActionCallBack{
        /* 为真时 */
        void actionTrue();
        /* 为假时 */
        void actionFalse();
    }
}

ExcpUtils:

package com.xiaoxu.crawler.utils;

import com.xiaoxu.crawler.excp.AccessParamException;
import com.xiaoxu.crawler.excp.CrawlerForJException;
import org.springframework.util.StringUtils;

/**
 * @author xiaoxu
 * @date 2022-11-06 16:04
 * crawlerJ:com.xiaoxu.crawler.utils.ExcpUtils
 */
public class ExcpUtils {

    /* 不为true则抛出异常 */
    public static void throwExpIfFalse(boolean result,String msg){
        if(StringUtils.hasLength(msg)&&!result){
            throw new CrawlerForJException(msg);
        }else if(!StringUtils.hasLength(msg)){
            throw new AccessParamException(
                    String.format(
                            "调用throwExpIfFalse方法的msg不能为空:%s",msg));
        }
    }

    /* 抛出异常的工具方法 */
    public static void throwExp(String message){
        if(StringUtils.hasLength(message)){
            throw new CrawlerForJException(message);
        }else{
            throw new AccessParamException(
                    String.format("方法%s的参数不能为空:%s",
                            ExcpUtils.class.getSimpleName()
                                    +Thread.currentThread().getStackTrace()[1].getMethodName(),
                            message));
        }
    }
}

StrUtils:

package com.xiaoxu.crawler.utils;

import org.springframework.util.StringUtils;

import java.text.MessageFormat;

/**
 * @author xiaoxu
 * @date 2022-11-19 21:52
 * crawlerJ:com.xiaoxu.crawler.utils.StringUtils
 */
public class StrUtils {

    public static boolean equals(String a, String b){
        if(null == a){
            return null == b;
        }
        return a.equals(b);
    }

    public static int getMatchCount(String str, String subStr){
        if(!StringUtils.hasLength(str) || !StringUtils.hasLength(subStr)){
            ExcpUtils.throwExp(
                    MessageFormat.format("getMatchCount's str and subStr should not be null or empty:{0},{1}.",
                            str, subStr));
        }
        return StringUtils.countOccurrencesOf(str, subStr);
    }

}

AssertUtils:

package com.xiaoxu.crawler.utils;

import com.xiaoxu.crawler.excp.CrawlerForJException;
import org.apache.commons.lang3.ArrayUtils;

/**
 * @author xiaoxu
 * @date 2022-11-06 15:50
 * crawlerJ:com.xiaoxu.crawler.utils.AssertUtils
 */
public class AssertUtils {
    /* 校验为真 */
    public static void assertTrue(boolean res, String errorMsg){
        handlerError(res, errorMsg);
    }

    /* 校验非空 */
    public static <T> void assertNonNull(T obj, String errorMsg){
        handlerError(null != obj, errorMsg);
    }

    /* 校验非null非empty字符串 */
    public static <T> void assertNonEmpty(String str, String errorMsg){
        handlerError(null != str && !str.isEmpty(), errorMsg);
    }

    /* 校验非空Array */
    public static <T> void assertNonEmptyArray(T[] array, String errorMsg){
        handlerError(!ArrayUtils.isEmpty(array), errorMsg);
    }

    /* 统一异常处理 */
    private static void handlerError(boolean flag, String message){
        if(!flag){
            /* 使用公共异常处理 */
            throw new CrawlerForJException(message);
        }
    }
}

执行结果如下:

12:46:16.779 [main] INFO com.xiaoxu.crawler.utils.LogUtils - [2022-12-25 12:46:16.744 - info] 今天星期是12
12:46:16.782 [main] INFO com.xiaoxu.crawler.utils.LogUtils - [2022-12-25 12:46:16.782 - info] 今天星期是
12:46:16.782 [main] WARN com.xiaoxu.crawler.utils.LogUtils - [2022-12-25 12:46:16.782 - warn] 空指针异常!please check
12:46:16.782 [main] ERROR com.xiaoxu.crawler.utils.LogUtils - [2022-12-25 12:46:16.782 - error] 严重错误!!!参数:,123
12:46:16.782 [main] INFO com.xiaoxu.crawler.utils.LogUtils - [2022-12-25 12:46:16.782 - info] 调用[getYou]方法, 请求参数:[123], 耗时:[2121]ms.
12:46:16.782 [main] WARN com.xiaoxu.crawler.utils.LogUtils - [2022-12-25 12:46:16.782 - warn] 调用[getYou]方法, 请求参数:[123], 耗时:[2121]ms.
12:46:16.782 [main] ERROR com.xiaoxu.crawler.utils.LogUtils - [2022-12-25 12:46:16.782 - error] 调用[getYou]方法, 请求参数:[123], 耗时:[2121]ms.
12:46:16.782 [main] INFO com.xiaoxu.crawler.utils.LogUtils - [2022-12-25 12:46:16.782 - info] 成功!!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值