1 前言
使用Slf4j时,针对日志打印进行工具类封装使用。
依赖如下:
org.springframework.boot 2.5.4 spring-boot-starter-parent
org.springframework.boot spring-boot-starter-web org.slf4j slf4j-api 1.7.32 ch.qos.logback logback-classic 1.2.5 org.springframework.boot spring-boot-configuration-processor true org.apache.commons commons-lang3
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 void logDetailInfo(Logger logger, String methodName, R requestPayload,long timeConsuming){String msg = logDetailCheck(methodName, timeConsuming);info(logger, msg, methodName, requestPayload, timeConsuming);}public static void logDetailWarn(Logger logger, String methodName, R requestPayload,long timeConsuming){String msg = logDetailCheck(methodName, timeConsuming);warn(logger, msg, methodName, requestPayload, timeConsuming);}public static 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() {@Overridepublic 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()));}}@Overridepublic 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> 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 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 executeBothSideWithValue(boolean flag, ActionCallBackWithValue callBack){if(flag){return callBack.actionTrue();}else{return callBack.actionFalse();}}public interface ActionCallBackWithValue{/* 为真时 */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 void assertNonNull(T obj, String errorMsg){handlerError(null != obj, errorMsg);}/* 校验非null非empty字符串 */public static void assertNonEmpty(String str, String errorMsg){handlerError(null != str && !str.isEmpty(), errorMsg);}/* 校验非空Array */public static 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] 成功!!
上一篇:蓝桥杯备赛Day4——多维数组
下一篇:C++必须掌握的知识点