项目中使用log4j打印日志调用toString方法,排查问题很不方便,此工具类可将日志转化为JSON。若对象某个字段的值包含“=” “,” “(” “)”等特殊字符则需特殊处理。都是大佬,不多说直接贴代码。
import com.alibaba.fastjson.JSON;
import javafx.util.Pair;
import org.apache.commons.lang.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
public class LogToJson {
private static final String NULL = "<null>";
private static final String NULL_2 = "null";
//CST日期匹配正则
private static Pattern cstDatePattern = Pattern.compile("^[a-zA-Z]{3} [a-zA-Z]{3} [0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} CST ((19|20)\\d{2})$");
//UTC日期匹配正则
private static Pattern utcDatePattern = Pattern.compile("^[0-9]{4}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}$]");
//数字类型匹配正则
private static Pattern numPattern = Pattern.compile("^-?[0-9]+\\.?[0-9]*$");
//集合类型
private static Pattern listPattern = Pattern.compile("^\\[.*\\]$");
//map类型
private static Pattern mapPattern = Pattern.compile("^\\{.*\\}$");
//对象类型
private static Pattern objectPattern = Pattern.compile("^[a-zA-Z0-9\\.]+\\(.+\\)$");
//super
private static Pattern superPattern = Pattern.compile("^super=[a-zA-Z0-9\\.]+\\(.+\\)$");
public static String toJsonString(String str) throws ParseException {
return JSON.toJSONString(strToMap(str));
}
public static <T> T toObject(String str, Class<T> clazz) throws ParseException {
return JSON.parseObject(toJsonString(str), clazz);
}
private static Map<String, Object> strToMap(String str) throws ParseException {
//空校验
str = StringUtils.trim(str);
if (StringUtils.isEmpty(str)) {
return str == null ? null : new HashMap<>();
}
//获取str主要内容 第一个"()"
str = StringUtils.substringAfter(str, "(").trim();
str = StringUtils.substringBeforeLast(str, ")").trim();
//开始构造
String token;
Map<String, Object> map = new HashMap<>();
while (StringUtils.isNotEmpty(str) && StringUtils.isNotEmpty(token = ToStringTokenUtils.splitToken(str))) {
str = StringUtils.removeStart(str, token).trim();
str = StringUtils.removeStart(str, ",").trim();
// 如果带"super="(lombok的@ToString(callSuper=true), 按照当前层继续处理
if (superPattern.matcher(token).matches()) {
token = token.substring(token.indexOf("(") + 1, token.length() - 1);
str = String.format("%s, %s", token, str);
continue;
}
Pair<String, String> pair = ToStringTokenUtils.parseToken(token);
map.put(pair.getKey(), buildTypeValue(pair.getKey(), pair.getValue()));
}
return map;
}
private static Object buildTypeValue(String key, String value) throws ParseException {
// value为null的情况
if (StringUtils.isEmpty(value)) {
return null;
} else if (NULL.equalsIgnoreCase(value)) {
return null;
}else if (NULL_2.equalsIgnoreCase(value)){
return null;
}
// CST日期类型
if (cstDatePattern.matcher(value).matches()) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date us = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzzz yyyy", Locale.ENGLISH).parse(value);
return format.format(us);
}
// UTC日期类型
if (utcDatePattern.matcher(value).matches()) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date us = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH).parse(value);
return format.format(us);
}
// 数字类型
if (numPattern.matcher(value).matches()) {
return value;
}
// 集合类型
if (listPattern.matcher(value).matches()) {
return buildListValue(value);
}
// map类型
if (mapPattern.matcher(value).matches()) {
return buildMapValue(value);
}
// 对象类型
if (objectPattern.matcher(value).matches()) {
return strToMap(value);
}
//其余一律当做str处理
return value;
}
private static Object buildListValue(String value) throws ParseException {
List<Object> result = new ArrayList<>();
// 去除左右括号
value = value.substring(1, value.length() - 1).trim();
String token = null;
while (StringUtils.isNotBlank(value) && StringUtils.isNotBlank(token = ToStringTokenUtils.splitToken(value))) {
result.add(buildTypeValue(null, token));
value = StringUtils.removeStart(value, token).trim();
value = StringUtils.removeStart(value, ",").trim();
}
return result;
}
private static Object buildMapValue(String value) throws ParseException {
Map<Object, Object> result = new HashMap<>();
value = value.substring(1, value.length() - 1).trim();
if (StringUtils.isEmpty(value)) {
return result;
}
String token = null;
while (StringUtils.isNotEmpty(token = ToStringTokenUtils.splitToken(value))) {
Pair<String, String> pair = ToStringTokenUtils.parseToken(token);
result.put(buildTypeValue(pair.getKey(), pair.getKey()), buildTypeValue(pair.getKey(), pair.getValue()));
value = StringUtils.removeStart(value, token).trim();
value = StringUtils.removeStart(value, ",").trim();
}
return result;
}
public static class ToStringTokenUtils {
private static List<Character> TOKEN_LEFT = Arrays.asList('(', '[', '{');
private static List<Character> TOKEN_RIGHT = Arrays.asList(')', ']', '}');
static String splitToken(String str) {
if (StringUtils.isBlank(str)) {
return str;
}
int index = indexOfSplitToken(str, ',');
return str.substring(0, index);
}
static Pair<String, String> parseToken(String token) {
int index = indexOfSplitToken(token, '=');
return new Pair<>(token.substring(0, index), token.substring(index + 1));
}
private static int indexOfSplitToken(String token, char split) {
Deque<Character> stack = new LinkedList<>();
int length = token.length();
for (int i = 0; i < length; i++) {
char c = token.charAt(i);
if (TOKEN_LEFT.contains(c)) {
stack.push(c);
} else if (TOKEN_RIGHT.contains(c)) {
//括号一定要匹配
if (TOKEN_LEFT.indexOf(stack.peek()) != TOKEN_RIGHT.indexOf(c)) {
throw new RuntimeException("括号匹配异常, stack=" + stack + ",token=" + token);
}
stack.pop();
} else if (c == split && stack.isEmpty()) {
if (',' == split ){
if (i < length && token.charAt(i+1) == ' ')return i;
}else {
return i;
}
}
}
if (stack.isEmpty()) {
return token.length();
}
//匹配结束都没匹配到,匹配异常
throw new RuntimeException("获取分割下标异常, stack=" + stack + ",token=" + token);
}
}
}