我们在开发过程中经常需要在控制台打印日志来进行调试,但是会发现日志内容过长的情况下,就无法完整的输出全部内容,这是因为 Log 日志最大长度为 4096 字节,超过这个长度的部分就无法显示。下面给出几个解决方案:
方式一:
private static final int MAX_LOG_BYTES = 4096;
public static void largeLog(String tag, String content) {
if (content.length() > MAX_LOG_BYTES) {
Log.d(tag, content.substring(0, MAX_LOG_BYTES));
largeLog(tag, content.substring(MAX_LOG_BYTES));
} else {
Log.d(tag, content);
}
}
以上代码初看起来,简洁优雅,但实际上存在问题,Log 日志的最大长度 4096 是指的字节长度,如果输出的日志内容都是 ASCII 编码的字符(单字节的字符),这样写是可以的。但是如果日志内容包括 UTF8 编码的中文字符(1-4个字节),所以上面代码中的:content.substring(0, MAX_LOG_BYTES),“4096 长度的字符串”包括的字节数会超出 4096 字节(日志最大字节数),仍然会导致分段输出的日志可能不完整。
根据上面的分析,如果输出的日志(字符串)不仅仅包括单字节字符(包括中文字符),可以考虑将上面的 MAX_LOG_BYTES 的值修改的低一些,例如1000,2000等。这样处理虽然比较简单,但略显粗糙,因此我们可以考虑用更精准的 “方式二” 与 “方式三” 来实现。
方式二:
# 由于日志的tag, priority(assert, debug, ...), etc. 会占用一些字节,这里4096是一个理想值。
# 建议这里设置一个小于4096 的值,例如一个比较保守的值 4000
# private static final int MAX_LOG_BYTES = 4096;
private static final int MAX_LOG_BYTES = 4000;
public static void largeLog(int priority, String tag, @NonNull String content) {
int size = content.getBytes(StandardCharsets.UTF_8).length;
if (size > MAX_LOG_BYTES) {
String text = trim(content, MAX_LOG_BYTES);
Log.println(priority, tag, text);
largeLog(priority, tag, content.substring(text.length()));
} else {
Log.println(priority, tag, content);
}
}
public static String trim(String text, int size) {
byte[] inputBytes = text.getBytes(StandardCharsets.UTF_8);
byte[] outputBytes = new byte[size];
System.arraycopy(inputBytes, 0, outputBytes, 0, size);
String result = new String(outputBytes, StandardCharsets.UTF_8);
// check if last character is truncated
int lastIndex = result.length() - 1;
if (lastIndex > 0 && result.charAt(lastIndex) != text.charAt(lastIndex)) {
// last character is truncated so remove the last character
return result.substring(0, lastIndex);
}
return result;
}
方式三:
# 由于日志的tag, priority(assert, debug, ...), etc. 会占用一些字节,这里4096是一个理想值。
# 建议这里设置一个小于4096 的值,例如一个比较保守的值 4000
# private static final int MAX_LOG_BYTES = 4096;
private static final int MAX_LOG_BYTES = 4000;
public static void largeLog(String tag, String str) {
String[] logs = SplitStringByByteLength(str, "UTF-8", MAX_LOG_BYTES );
for(String log : logs) {
Log.i(tag, log);
}
}
public static String[] SplitStringByByteLength(String src, String encoding, int maxsize) {
Charset cs = Charset.forName(encoding);
CharsetEncoder coder = cs.newEncoder();
ByteBuffer out = ByteBuffer.allocate(maxsize); // output buffer of required size
CharBuffer in = CharBuffer.wrap(src);
List<String> ss = new ArrayList<>(); // a list to store the chunks
int pos = 0;
while(true) {
CoderResult cr = coder.encode(in, out, true); // try to encode as much as possible
int newpos = src.length() - in.length();
String s = src.substring(pos, newpos);
ss.add(s); // add what has been encoded to the list
pos = newpos; // store new input position
out.rewind(); // and rewind output buffer
if (! cr.isOverflow()) {
break; // everything has been encoded
}
}
return ss.toArray(new String[0]);
}
参考: