truncate string value, polish object renderer

This commit is contained in:
gongdewei 2021-07-21 16:53:05 +08:00
parent 226bdce059
commit c91c42aaba
4 changed files with 152 additions and 61 deletions

View File

@ -22,13 +22,13 @@ public class ObjectVO {
//map entry key
protected ObjectVO key;
// string, collection/array
//collection/array size
private Integer size;
//complex object's fields
protected List<ObjectVO> fields;
//exceed number limit
// whether exceed number limit
private Boolean exceedLimit;
private Integer objectNumberLimit;

View File

@ -22,23 +22,20 @@ public class ObjectInspector {
public static final int DEFAULT_OBJECT_NUMBER_LIMIT = 500;
public static final int DEFAULT_ARRAY_LEN_LIMIT = 100;
public static final int DEFAULT_STRING_LEN_LIMIT = 4096;
private int objectNumberLimit;
private int arrayLenLimit;
private int objectNumberLimit = DEFAULT_OBJECT_NUMBER_LIMIT;
private int arrayLenLimit = DEFAULT_ARRAY_LEN_LIMIT;
private int stringLenLimit = DEFAULT_STRING_LEN_LIMIT;
//子对象数量
private int objectCount;
public ObjectInspector() {
this(DEFAULT_OBJECT_NUMBER_LIMIT, DEFAULT_ARRAY_LEN_LIMIT);
}
public ObjectInspector(int objectNumberLimit) {
this(objectNumberLimit, DEFAULT_ARRAY_LEN_LIMIT);
}
public ObjectInspector(int objectNumberLimit, int arrayLenLimit) {
this.setObjectNumberLimit(objectNumberLimit);
this.setArrayLenLimit(arrayLenLimit);
this.objectNumberLimit = objectNumberLimit;
}
public ObjectVO inspect(Object object, int expand) {
@ -51,11 +48,24 @@ public class ObjectInspector {
return objectVO;
} catch (ObjectTooLargeException e) {
// unreachable statement
return new ObjectVO(object != null ? object.getClass().getSimpleName() : "", "...");
return new ObjectVO(object != null ? object.getClass().getSimpleName() : "", e.getMessage());
}
}
private ObjectVO inspectObject(Object obj, int deep, int expand) throws ObjectTooLargeException {
ObjectVO objectVO = this.inspectObject0(obj, deep, expand);
if (objectVO != null && objectVO.getValue() != null && objectVO.getValue() instanceof String) {
String stringValue = (String) objectVO.getValue();
if (stringValue.length() > stringLenLimit) {
// truncate string value
objectVO.setValue(stringValue.substring(0, stringLenLimit) + "...(truncated " +
(stringValue.length() - stringLenLimit) + " chars)");
}
}
return objectVO;
}
private ObjectVO inspectObject0(Object obj, int deep, int expand) throws ObjectTooLargeException {
checkObjectAmount();
if (null == obj) {
@ -588,8 +598,15 @@ public class ObjectInspector {
this.arrayLenLimit = arrayLenLimit < 10 ? 10 :arrayLenLimit;
}
public int getStringLenLimit() {
return stringLenLimit;
}
// --------------- static methods --------------------//
public void setStringLenLimit(int stringLenLimit) {
this.stringLenLimit = stringLenLimit < 100 ? 100 : stringLenLimit;
}
// --------------- static methods --------------------//
private static Object[] toArray(int[] arrays, int limit) {
limit = Math.min(arrays.length, limit);

View File

@ -1,6 +1,7 @@
package com.taobao.arthas.core.util.object;
import com.taobao.arthas.core.command.model.ObjectVO;
import com.taobao.arthas.core.util.StringUtils;
import java.util.Collection;
@ -29,30 +30,25 @@ public abstract class ObjectRenderer {
return;
}
if (vo.getKey() != null) {
//kv entry
render(vo.getKey(), deep, sb);
sb.append(" : ");
render((ObjectVO) vo.getValue(), deep, sb);
renderKeyValueEntry(vo, deep, sb);
return;
}
if (vo.getName() != null) {
// object field name
sb.append(vo.getName()).append('=');
}
if (vo.getType() != null) {
//object
//object type
sb.append('@').append(vo.getType()).append('[');
}
// object value start
int nextDeep = deep+1;
if (vo.getFields() != null) {
//fields
sb.append('\n');
for (ObjectVO field : vo.getFields()) {
renderTab(sb, nextDeep);
render(field, nextDeep, sb);
sb.append(",\n");
}
// complex object: fields is not null
renderComplexObject(vo, nextDeep, sb);
} else {
//value
if (vo.getSize() != null) {
@ -68,6 +64,8 @@ public abstract class ObjectRenderer {
renderValue(vo, deep, sb);
}
}
// object value end
if (vo.getType() != null) {
if (sb.charAt(sb.length() - 1) == '\n') {
renderTab(sb, deep);
@ -76,6 +74,25 @@ public abstract class ObjectRenderer {
}
}
private static void renderComplexObject(ObjectVO vo, int deep, StringBuffer sb) {
sb.append('\n');
for (ObjectVO field : vo.getFields()) {
if (StringUtils.isEmpty(field.getName())) {
throw new IllegalArgumentException("Complex object's field name is empty: " + sb + "... ");
}
renderTab(sb, deep);
render(field, deep, sb);
sb.append(",\n");
}
}
private static void renderKeyValueEntry(ObjectVO vo, int deep, StringBuffer sb) {
//kv entry
render(vo.getKey(), deep, sb);
sb.append(" : ");
render((ObjectVO) vo.getValue(), deep, sb);
}
private static StringBuffer renderTab(StringBuffer sb, int deep) {
for (int i = 0; i < deep; i++) {
sb.append(TAB);
@ -91,47 +108,40 @@ public abstract class ObjectRenderer {
sb.append('\n');
Collection collection = (Collection) value;
for (Object e : collection) {
if (e instanceof ObjectVO) {
ObjectVO objectVO = (ObjectVO) e;
renderTab(sb, nextDeep);
render(objectVO, nextDeep, sb);
sb.append(",\n");
} else {
renderTab(sb, nextDeep);
sb.append(e).append(",\n");
}
}
//如果没有完全显示所有元素则添加省略提示
int count = collection.size();
if (vo.getSize() > count) {
String msg = count + " out of " + vo.getSize() + " displayed, " + (vo.getSize() - count) + " remaining.\n";
renderTab(sb, nextDeep);
sb.append(msg);
renderElement(e, nextDeep, sb);
}
renderSize(vo, collection.size(), nextDeep, sb);
} else if (value instanceof Object[]) {
sb.append('\n');
Object[] objs = (Object[]) value;
for (int i = 0; i < objs.length; i++) {
Object obj = objs[i];
if (obj instanceof ObjectVO) {
ObjectVO objectVO = (ObjectVO) obj;
renderTab(sb, nextDeep);
render(objectVO, nextDeep, sb);
sb.append(",\n");
} else {
renderTab(sb, nextDeep);
sb.append(obj).append(",\n");
}
}
//如果没有完全显示所有元素则添加省略提示
int count = objs.length;
if (vo.getSize() > count) {
String msg = count + " out of " + vo.getSize() + " displayed, " + (vo.getSize() - count) + " remaining.\n";
renderTab(sb, nextDeep).append(msg);
renderElement(objs[i], nextDeep, sb);
}
renderSize(vo, objs.length, nextDeep, sb);
} else {
sb.append(value);
}
}
private static void renderElement(Object obj, int deep, StringBuffer sb) {
if (obj instanceof ObjectVO) {
ObjectVO objectVO = (ObjectVO) obj;
renderTab(sb, deep);
render(objectVO, deep, sb);
sb.append(",\n");
} else {
renderTab(sb, deep);
sb.append(obj).append(",\n");
}
}
private static void renderSize(ObjectVO vo, int elemCount, int deep, StringBuffer sb) {
//如果没有完全显示所有元素则添加省略提示
if (vo.getSize() > elemCount) {
String msg = elemCount + " out of " + vo.getSize() + " displayed, " + (vo.getSize() - elemCount) + " remaining.\n";
renderTab(sb, deep);
sb.append(msg);
}
}
}

View File

@ -14,6 +14,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -26,6 +27,7 @@ public class ObjectRenderTest {
static int arrayLenLimit = 15;
static int objectNumLimit = 30;
static int stringLenLimit = 100;
@Test
public void testSimpleObjects() {
@ -357,7 +359,8 @@ public class ObjectRenderTest {
@Test
public void testObjectTooLargeException() {
NestedClass nestedClass = new NestedClass(100);
ObjectInspector objectInspector = new ObjectInspector(10, arrayLenLimit);
ObjectInspector objectInspector = new ObjectInspector(10);
objectInspector.setArrayLenLimit(arrayLenLimit);
ObjectVO objectVO = objectInspector.inspect(nestedClass, 4);
printObject(objectVO);
@ -380,6 +383,64 @@ public class ObjectRenderTest {
"] Number of objects exceeds limit: 10"));
}
@Test
public void testTruncateStringValue() {
String str = "This is a very long string.";
StringBuilder sb = new StringBuilder(str.length()*10);
for (int i = 0; i < 10; i++) {
sb.append(str);
}
String longString = sb.toString();
// string
ObjectVO objectVO = inspectObject(longString, 1);
printObject(objectVO);
Assert.assertTrue(ObjectRenderer.render(objectVO).contains("(truncated 170 chars)"));
// array
Object[] objects = new Object[]{longString};
objectVO = inspectObject(objects, 2);
printObject(objectVO);
Assert.assertTrue(ObjectRenderer.render(objectVO).contains("(truncated 170 chars)"));
// collection
List list = new ArrayList();
list.add(longString);
objectVO = inspectObject(objects, 2);
printObject(objectVO);
Assert.assertTrue(ObjectRenderer.render(objectVO).contains("(truncated 170 chars)"));
// map
Map map = new HashMap();
map.put("longString", longString);
objectVO = inspectObject(map, 2);
printObject(objectVO);
Assert.assertTrue(ObjectRenderer.render(objectVO).contains("(truncated 170 chars)"));
// complex object
SonBean sonBean = new SonBean();
sonBean.setJ(longString);
objectVO = inspectObject(sonBean, 2);
printObject(objectVO);
Assert.assertTrue(ObjectRenderer.render(objectVO).contains("(truncated 170 chars)"));
// nest map
Map nestMap = new HashMap();
nestMap.put("sonBean", sonBean);
objectVO = inspectObject(nestMap, 3);
printObject(objectVO);
Assert.assertTrue(ObjectRenderer.render(objectVO).contains("(truncated 170 chars)"));
// nest list
List nestList = new ArrayList();
nestList.add(nestMap);
objectVO = inspectObject(nestList, 3);
printObject(objectVO);
Assert.assertTrue(ObjectRenderer.render(objectVO).contains("(truncated 170 chars)"));
}
/**
* 显示基类属性值
*/
@ -419,12 +480,15 @@ public class ObjectRenderTest {
}
}
private ObjectInspector newInspector() {
return new ObjectInspector(objectNumLimit, arrayLenLimit);
private ObjectInspector newInspector(int objectNumLimit, int arrayLenLimit, int stringLenLimit) {
ObjectInspector objectInspector = new ObjectInspector(objectNumLimit);
objectInspector.setArrayLenLimit(arrayLenLimit);
objectInspector.setStringLenLimit(stringLenLimit);
return objectInspector;
}
private ObjectVO inspectObject(Object object, int expand) {
return new ObjectInspector(objectNumLimit, arrayLenLimit).inspect(object, expand);
return newInspector(objectNumLimit, arrayLenLimit, stringLenLimit).inspect(object, expand);
}
private void testSimpleArray(Object array, String testJson, String testString) {