mirror of
https://gitee.com/arthas/arthas.git
synced 2024-11-29 18:58:37 +08:00
truncate string value, polish object renderer
This commit is contained in:
parent
226bdce059
commit
c91c42aaba
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user