规范日志打印,统一日志模板。
/**
* @description 日志类型枚举类
*/
@Getter
public enum LogEnum {
//全局请求日志
GLOBAL,
// 系统报错日志
SYS_ERR,
// 用户信息
SYS_USER
;
/**
* 判断日志类型不能为空
*
* @param logType 日志类型
* @return boolean 返回类型
*/
public static boolean isLogType(LogEnum logType) {
return logType != null;
}
}
level
public final class Level implements Serializable {
private static final long serialVersionUID = -814092767334282137L;
public static final int OFF_INT = Integer.MAX_VALUE;
public static final int ERROR_INT = 40000;
public static final int WARN_INT = 30000;
public static final int INFO_INT = 20000;
public static final int DEBUG_INT = 10000;
public static final int TRACE_INT = 5000;
public static final int ALL_INT = Integer.MIN_VALUE;
public static final Integer OFF_INTEGER = Integer.MAX_VALUE;
public static final Integer ERROR_INTEGER = 40000;
public static final Integer WARN_INTEGER = 30000;
public static final Integer INFO_INTEGER = 20000;
public static final Integer DEBUG_INTEGER = 10000;
public static final Integer TRACE_INTEGER = 5000;
public static final Integer ALL_INTEGER = Integer.MIN_VALUE;
public static final Level OFF = new Level(Integer.MAX_VALUE, "OFF");
public static final Level ERROR = new Level(40000, "ERROR");
public static final Level WARN = new Level(30000, "WARN");
public static final Level INFO = new Level(20000, "INFO");
public static final Level DEBUG = new Level(10000, "DEBUG");
public static final Level TRACE = new Level(5000, "TRACE");
public static final Level ALL = new Level(Integer.MIN_VALUE, "ALL");
public final int levelInt;
public final String levelStr;
private Level(int levelInt, String levelStr) {
this.levelInt = levelInt;
this.levelStr = levelStr;
}
public String toString() {
return this.levelStr;
}
public int toInt() {
return this.levelInt;
}
public Integer toInteger() {
switch (this.levelInt) {
case Integer.MIN_VALUE:
return ALL_INTEGER;
case 5000:
return TRACE_INTEGER;
case 10000:
return DEBUG_INTEGER;
case 20000:
return INFO_INTEGER;
case 30000:
return WARN_INTEGER;
case 40000:
return ERROR_INTEGER;
case Integer.MAX_VALUE:
return OFF_INTEGER;
default:
throw new IllegalStateException("Level " + this.levelStr + ", " + this.levelInt + " is unknown.");
}
}
public boolean isGreaterOrEqual(Level r) {
return this.levelInt >= r.levelInt;
}
public static Level toLevel(String sArg) {
return toLevel(sArg, DEBUG);
}
public static Level valueOf(String sArg) {
return toLevel(sArg, DEBUG);
}
public static Level toLevel(int val) {
return toLevel(val, DEBUG);
}
public static Level toLevel(int val, Level defaultLevel) {
switch (val) {
case Integer.MIN_VALUE:
return ALL;
case 5000:
return TRACE;
case 10000:
return DEBUG;
case 20000:
return INFO;
case 30000:
return WARN;
case 40000:
return ERROR;
case Integer.MAX_VALUE:
return OFF;
default:
return defaultLevel;
}
}
public static Level toLevel(String sArg, Level defaultLevel) {
if (sArg == null) {
return defaultLevel;
} else if (sArg.equalsIgnoreCase("ALL")) {
return ALL;
} else if (sArg.equalsIgnoreCase("TRACE")) {
return TRACE;
} else if (sArg.equalsIgnoreCase("DEBUG")) {
return DEBUG;
} else if (sArg.equalsIgnoreCase("INFO")) {
return INFO;
} else if (sArg.equalsIgnoreCase("WARN")) {
return WARN;
} else if (sArg.equalsIgnoreCase("ERROR")) {
return ERROR;
} else {
return sArg.equalsIgnoreCase("OFF") ? OFF : defaultLevel;
}
}
private Object readResolve() {
return toLevel(this.levelInt);
}
public static Level fromLocationAwareLoggerInteger(int levelInt) {
Level level;
switch (levelInt) {
case 0:
level = TRACE;
break;
case 10:
level = DEBUG;
break;
case 20:
level = INFO;
break;
case 30:
level = WARN;
break;
case 40:
level = ERROR;
break;
default:
throw new IllegalArgumentException(levelInt + " not a valid level value");
}
return level;
}
public static int toLocationAwareLoggerInteger(Level level) {
if (level == null) {
throw new IllegalArgumentException("null level parameter is not admitted");
} else {
switch (level.toInt()) {
case 5000:
return 0;
case 10000:
return 10;
case 20000:
return 20;
case 30000:
return 30;
case 40000:
return 40;
default:
throw new IllegalArgumentException(level + " not a valid level value");
}
}
}
}
LogInfo
@Data
@Accessors(chain = true)
public class LogInfo implements Serializable {
private static final long serialVersionUID = 5250395474667395607L;
@JSONField(ordinal = MagicNumConstant.ONE)
private String traceId;
@JSONField(ordinal = MagicNumConstant.TWO)
private String type;
@JSONField(ordinal = MagicNumConstant.THREE)
private String level;
@JSONField(ordinal = MagicNumConstant.FOUR)
private String location;
@JSONField(ordinal = MagicNumConstant.SIX)
private Object info;
public void setInfo(Object info) {
this.info = info;
}
@Override
public String toString() {
String result="";
if(StringUtils.isNotEmpty(type)){
result+="[" + type +"]";
}
if(StringUtils.isNotEmpty(location)){
result+="[" + location +"]";
}
if(info!=null && StringUtils.isNotEmpty(info.toString())){
result+="[" + info.toString() +"]";
}
return result;
}
}
LogUtil
@Slf4j
public class LogUtil {
private static final String TRACE_TYPE = "TRACE_TYPE";
public static final String SCHEDULE_LEVEL = "SCHEDULE";
public static final String K8S_CALLBACK_LEVEL = "K8S_CALLBACK";
private static final String GLOBAL_REQUEST_LEVEL = "GLOBAL";
private static final String TRACE_LEVEL = "TRACE";
private static final String DEBUG_LEVEL = "DEBUG";
private static final String INFO_LEVEL = "INFO";
private static final String WARN_LEVEL = "WARN";
private static final String ERROR_LEVEL = "ERROR";
public static void startScheduleTrace() {
MDC.put(TRACE_TYPE, SCHEDULE_LEVEL);
}
public static void cleanTrace() {
MDC.clear();
}
/**
* info级别的日志
*
* @param logType 日志类型
* @param object 打印的日志参数
*/
public static void info(LogEnum logType, Object... object) {
logHandle(logType, Level.INFO, object);
}
/**
* debug级别的日志
*
* @param logType 日志类型
* @param object 打印的日志参数
*/
public static void debug(LogEnum logType, Object... object) {
logHandle(logType, Level.DEBUG, object);
}
/**
* error级别的日志
*
* @param logType 日志类型
* @param object 打印的日志参数
*/
public static void error(LogEnum logType, Object... object) {
errorObjectHandle(object);
logHandle(logType, Level.ERROR, object);
}
/**
* warn级别的日志
*
* @param logType 日志类型
* @param object 打印的日志参数
*/
public static void warn(LogEnum logType, Object... object) {
logHandle(logType, Level.WARN, object);
}
/**
* trace级别的日志
*
* @param logType 日志类型
* @param object 打印的日志参数
*/
public static void trace(LogEnum logType, Object... object) {
logHandle(logType, Level.TRACE, object);
}
/**
* 日志处理
*
* @param logType 日志类型
* @param level 日志级别
* @param object 打印的日志参数
*/
private static void logHandle(LogEnum logType, Level level, Object[] object) {
LogInfo logInfo = generateLogInfo(logType, level, object);
switch (logInfo.getLevel()) {
case TRACE_LEVEL:
log.trace(MarkerFactory.getMarker(TRACE_LEVEL), logJsonStringLengthLimit(logInfo));
break;
case DEBUG_LEVEL:
log.debug(MarkerFactory.getMarker(DEBUG_LEVEL), logJsonStringLengthLimit(logInfo));
break;
case GLOBAL_REQUEST_LEVEL:
logInfo.setLevel(null);
logInfo.setType(null);
logInfo.setLocation(null);
log.info(MarkerFactory.getMarker(GLOBAL_REQUEST_LEVEL), logJsonStringLengthLimit(logInfo));
break;
case SCHEDULE_LEVEL:
log.info(MarkerFactory.getMarker(SCHEDULE_LEVEL), logJsonStringLengthLimit(logInfo));
break;
case K8S_CALLBACK_LEVEL:
log.info(MarkerFactory.getMarker(K8S_CALLBACK_LEVEL), logJsonStringLengthLimit(logInfo));
break;
case INFO_LEVEL:
log.info(MarkerFactory.getMarker(INFO_LEVEL), logJsonStringLengthLimit(logInfo));
break;
case WARN_LEVEL:
log.warn(MarkerFactory.getMarker(WARN_LEVEL), logJsonStringLengthLimit(logInfo));
break;
case ERROR_LEVEL:
log.error(MarkerFactory.getMarker(ERROR_LEVEL), logJsonStringLengthLimit(logInfo));
break;
default:
}
}
/**
* 日志信息组装的内部方法
*
* @param logType 日志类型
* @param level 日志级别
* @param object 打印的日志参数
* @return LogInfo
*/
private static LogInfo generateLogInfo(LogEnum logType, Level level, Object[] object) {
LogInfo logInfo = new LogInfo();
// 日志类型检测
if (!LogEnum.isLogType(logType)) {
level = Level.ERROR;
object = new Object[MagicNumConstant.ONE];
object[MagicNumConstant.ZERO] = "日志类型【".concat(LogEnum.SYS_ERR.name()).concat("】不正确!");
logType = LogEnum.SYS_ERR;
}
// 获取trace_id
if (StringUtils.isEmpty(MDC.get(StringConstant.LOG_TRACE_ID))) {
MDC.put(StringConstant.LOG_TRACE_ID, UUID.randomUUID().toString());
}
// 设置logInfo的level,type,traceId属性
logInfo.setLevel(level.levelStr)
.setType(logType.toString())
.setTraceId(MDC.get(StringConstant.LOG_TRACE_ID));
//自定义日志级别
//LogEnum、 MDC中的 TRACE_TYPE 做日志分流标识
if (Level.INFO.toInt() == level.toInt()) {
if (LogEnum.GLOBAL.equals(logType)) {
//info全局请求
logInfo.setLevel(GLOBAL_REQUEST_LEVEL);
} else if (LogEnum.K8S.equals(logType)) {
logInfo.setLevel(K8S_CALLBACK_LEVEL);
} else {
//schedule定时等 链路记录
String traceType = MDC.get(TRACE_TYPE);
if (StringUtils.isNotBlank(traceType)) {
logInfo.setLevel(traceType);
}
}
}
// 设置logInfo的堆栈信息
setLogStackInfo(logInfo);
// 设置logInfo的info信息
setLogInfo(logInfo, object);
// 截取logInfo的长度并转换成json字符串
return logInfo;
}
/**
* 设置loginfo的堆栈信息
*
* @param logInfo 日志对象
*/
private static void setLogStackInfo(LogInfo logInfo) {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
if (elements.length >= MagicNumConstant.SIX) {
StackTraceElement element = elements[MagicNumConstant.FIVE];
logInfo.setLocation(String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
}
}
/**
* 限制log日志的长度并转换成json
*
* @param logInfo 日志对象
* @return String
*/
private static String logJsonStringLengthLimit(LogInfo logInfo) {
try {
if(logInfo==null){
return "";
}
String jsonString = logInfo.toString();
if (StringUtils.isBlank(jsonString)) {
return "";
}
if (jsonString.length() > MagicNumConstant.TEN_THOUSAND) {
String trunk = logInfo.getInfo().toString().substring(MagicNumConstant.ZERO, MagicNumConstant.NINE_THOUSAND);
logInfo.setInfo(trunk);
jsonString = JSON.toJSONString(logInfo);
}
return jsonString;
} catch (Exception e) {
logInfo.setLevel(Level.ERROR.levelStr).setType(LogEnum.SYS_ERR.toString())
.setInfo("cannot serialize exception: " + ExceptionUtils.getStackTrace(e));
return logInfo.toString();
}
}
/**
* 设置日志对象的info信息
*
* @param logInfo 日志对象
* @param object 打印的日志参数
*/
private static void setLogInfo(LogInfo logInfo, Object[] object) {
if (object.length > MagicNumConstant.ONE) {
logInfo.setInfo(MessageFormatter.arrayFormat(object[MagicNumConstant.ZERO].toString(),
Arrays.copyOfRange(object, MagicNumConstant.ONE, object.length)).getMessage());
} else if (object.length == MagicNumConstant.ONE && object[MagicNumConstant.ZERO] instanceof Exception) {
logInfo.setInfo((ExceptionUtils.getStackTrace((Exception) object[MagicNumConstant.ZERO])));
log.error((ExceptionUtils.getStackTrace((Exception) object[MagicNumConstant.ZERO])));
} else if (object.length == MagicNumConstant.ONE) {
logInfo.setInfo(object[MagicNumConstant.ZERO] == null ? "" : object[MagicNumConstant.ZERO]);
} else {
logInfo.setInfo("");
}
}
/**
* 处理Exception的情况
*
* @param object 打印的日志参数
*/
private static void errorObjectHandle(Object[] object) {
if (object.length == MagicNumConstant.TWO && object[MagicNumConstant.ONE] instanceof Exception) {
log.error(String.valueOf(object[MagicNumConstant.ZERO]), (Exception) object[MagicNumConstant.ONE]);
object[MagicNumConstant.ONE] = ExceptionUtils.getStackTrace((Exception) object[MagicNumConstant.ONE]);
} else if (object.length >= MagicNumConstant.THREE) {
log.error(String.valueOf(object[MagicNumConstant.ZERO]),
Arrays.copyOfRange(object, MagicNumConstant.ONE, object.length));
for (int i = 0; i < object.length; i++) {
if (object[i] instanceof Exception) {
object[i] = ExceptionUtils.getStackTrace((Exception) object[i]);
}
}
}
}
}