在命令行输出提示信息
import android.util.Log;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.StringUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
public final class KentLog {
// ========== 可配置部分 ==========
private static final String BASE_TAG = "KGlobalLog";
private static final String DELIMITER = " | ";
private static final int STACK_TRACE_INDEX = 4;
// 不同日志级别的TAG后缀
private static final String TAG_DEBUG = BASE_TAG + "-D";
private static final String TAG_INFO = BASE_TAG + "-I";
private static final String TAG_ERROR = BASE_TAG + "-E";
private static final String TAG_FILE = BASE_TAG + "-F";
// ========== debug ==========
public static void debug(String msg) {
logWithLocation(msg, Log::i, TAG_DEBUG);
}
public static void debug(Object msg) {
debug(stringify(msg));
}
public static void debug(String... messages) {
debug(buildMessage(messages));
}
public static void debug(Object... messages) {
debug(buildMessageGeneric(messages));
}
// 原始类型数组重载 - 明确的方法签名避免警告
public static void debug(int[] array) { debug(formatPrimitiveArray(array)); }
public static void debug(long[] array) { debug(formatPrimitiveArray(array)); }
public static void debug(float[] array) { debug(formatPrimitiveArray(array)); }
public static void debug(double[] array) { debug(formatPrimitiveArray(array)); }
public static void debug(boolean[] array) { debug(formatPrimitiveArray(array)); }
public static void debug(char[] array) { debug(formatPrimitiveArray(array)); }
public static void debug(byte[] array) { debug(formatPrimitiveArray(array)); }
public static void debug(short[] array) { debug(formatPrimitiveArray(array)); }
// ========== error ==========
public static void error(String msg) {
logWithLocation(msg, LogUtils::e, TAG_ERROR);
onlyFile(msg);
}
public static void error(Throwable tr) {
String msg = stringify(tr);
logWithLocation(msg, LogUtils::e, TAG_ERROR);
onlyFile(msg);
}
public static void error(String... messages) {
error(buildMessage(messages));
}
public static void error(Object... messages) {
error(buildMessageGeneric(messages));
}
// 原始类型数组重载
public static void error(int[] array) { error(formatPrimitiveArray(array)); }
public static void error(long[] array) { error(formatPrimitiveArray(array)); }
public static void error(float[] array) { error(formatPrimitiveArray(array)); }
public static void error(double[] array) { error(formatPrimitiveArray(array)); }
public static void error(boolean[] array) { error(formatPrimitiveArray(array)); }
public static void error(char[] array) { error(formatPrimitiveArray(array)); }
public static void error(byte[] array) { error(formatPrimitiveArray(array)); }
public static void error(short[] array) { error(formatPrimitiveArray(array)); }
// ========== info ==========
public static void info(String msg) {
logWithLocation(msg, Log::i, TAG_INFO);
onlyFile(msg);
}
public static void info(String... messages) {
info(buildMessage(messages));
}
public static void info(Object... messages) {
info(buildMessageGeneric(messages));
}
// ========== onlyFile ==========
public static void onlyFile(String msg) {
LogUtils.file(addLocationToFileLog(msg));
}
public static void onlyFile(String... messages) {
onlyFile(buildMessage(messages));
}
public static void onlyFile(Object... messages) {
onlyFile(buildMessageGeneric(messages));
}
public static void onlyFile(Throwable tr) {
onlyFile(stringify(tr));
}
// 原始类型数组重载
public static void onlyFile(int[] array) { onlyFile(formatPrimitiveArray(array)); }
public static void onlyFile(long[] array) { onlyFile(formatPrimitiveArray(array)); }
public static void onlyFile(float[] array) { onlyFile(formatPrimitiveArray(array)); }
public static void onlyFile(double[] array) { onlyFile(formatPrimitiveArray(array)); }
public static void onlyFile(boolean[] array) { onlyFile(formatPrimitiveArray(array)); }
public static void onlyFile(char[] array) { onlyFile(formatPrimitiveArray(array)); }
public static void onlyFile(byte[] array) { onlyFile(formatPrimitiveArray(array)); }
public static void onlyFile(short[] array) { onlyFile(formatPrimitiveArray(array)); }
// ========== 内部工具 ==========
/** 带调用位置的日志输出 */
private static void logWithLocation(String msg, LogMethod lm, String tag) {
String locationInfo = getLocationInfo();
String fullMsg = StringUtils.isEmpty(locationInfo) ?
msg : locationInfo + DELIMITER + msg;
lm.log(tag, StringUtils.isEmpty(fullMsg) ? KentNull.nullNullString() : fullMsg);
}
/** 为文件日志添加位置信息 */
private static String addLocationToFileLog(String msg) {
String locationInfo = getLocationInfo();
return StringUtils.isEmpty(locationInfo) ?
msg : locationInfo + DELIMITER + msg;
}
/** 获取调用位置信息(穿透到实际调用者) */
private static String getLocationInfo() {
try {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// 动态查找第一个非KentLog类的栈帧
for (int i = 1; i < stackTrace.length; i++) {
StackTraceElement element = stackTrace[i];
String className = element.getClassName();
// 1. 基础Java类
if (className.equals(Thread.class.getName()) ||
className.equals(Runnable.class.getName()) ||
className.equals(KentLog.class.getName())) {
continue;
}
// 2. Lambda表达式和方法引用相关的类
if (className.contains("$$Lambda$") || // Lambda表达式
className.contains("/EnclosingMethod") || // 匿名内部类
className.contains("$anonfun$") || // Scala lambda
className.contains("$lambda$") || // Kotlin lambda
className.startsWith("java.") || // Java核心库
className.startsWith("javax.") || // Java扩展库
className.startsWith("sun.") || // Sun/Oracle内部类
className.startsWith("com.sun.") || // Sun/Oracle组件
className.startsWith("oracle.")) { // Oracle类库
continue;
}
// 3. Android系统类
if (className.startsWith("android.") ||
className.startsWith("androidx.") ||
className.startsWith("com.android.")) {
continue;
}
// 4. 常见的反射和代理类
if (className.startsWith("java.lang.reflect.") ||
className.startsWith("java.lang.invoke.") ||
className.contains("$$Proxy") || // 动态代理
className.contains("$Proxy")) { // 代理类
continue;
}
// 5. 函数式接口和回调
if (className.startsWith("kotlin.jvm.functions.") || // Kotlin函数
className.startsWith("kotlinx.coroutines.")) { // Kotlin协程
continue;
}
// 6. 空白和匿名类标识
if (element.getFileName() == null || // 无源码文件(通常是合成类)
element.getFileName().equals("<unknown>") || // 未知文件
element.getMethodName().contains("lambda$") || // Lambda方法
element.getMethodName().matches(".*\\$\\d+")) { // 匿名类方法(如access$100)
continue;
}
// 7. 特定的工具类和框架(根据你的项目添加)
if (className.startsWith("com.blankj.utilcode.") || // blankj工具库
className.startsWith("com.google.") || // Google库
className.startsWith("org.jetbrains.")) { // JetBrains库
continue;
}
String fileName = element.getFileName();
String methodName = element.getMethodName();
int lineNumber = element.getLineNumber();
// 简化类名显示
String simpleClassName = getSimpleClassName(className);
return String.format("[%s.%s(%s:%d)]",
simpleClassName, methodName, fileName, lineNumber);
}
// 如果没找到,回退到固定索引
if (stackTrace.length > 4) {
StackTraceElement element = stackTrace[4];
return formatStackTraceElement(element);
}
} catch (Exception e) {
// 忽略异常,不影响正常日志输出
}
return "";
}
/** 格式化单个栈跟踪元素 */
private static String formatStackTraceElement(StackTraceElement element) {
String fileName = element.getFileName();
String className = element.getClassName();
String methodName = element.getMethodName();
int lineNumber = element.getLineNumber();
String simpleClassName = getSimpleClassName(className);
return String.format("[%s.%s(%s:%d)]",
simpleClassName, methodName, fileName, lineNumber);
}
/** 获取简化的类名 */
private static String getSimpleClassName(String className) {
if (StringUtils.isEmpty(className)) return "";
// 移除包名,只保留类名
int lastDotIndex = className.lastIndexOf('.');
if (lastDotIndex >= 0 && lastDotIndex < className.length() - 1) {
return className.substring(lastDotIndex + 1);
}
return className;
}
/** String... 专用,无警告,末尾无 DELIMITER */
private static String buildMessage(String... messages) {
if (messages == null || messages.length == 0) {
return KentNull.nullNullString();
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < messages.length; i++) {
sb.append(messages[i] == null ? KentNull.nullNullString() : messages[i]);
if (i < messages.length - 1) {
sb.append(DELIMITER);
}
}
return sb.toString();
}
/** Object... 通用版本,避免原始类型数组警告 */
private static String buildMessageGeneric(Object... messages) {
if (messages == null || messages.length == 0) {
return KentNull.nullNullString();
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < messages.length; i++) {
sb.append(stringify(messages[i]));
if (i < messages.length - 1) {
sb.append(DELIMITER);
}
}
return sb.toString();
}
/** 格式化原始类型数组的通用方法 */
private static String formatPrimitiveArray(Object array) {
if (array == null) {
return KentNull.nullNullString();
}
if (array instanceof int[]) {
return Arrays.toString((int[]) array);
} else if (array instanceof long[]) {
return Arrays.toString((long[]) array);
} else if (array instanceof float[]) {
return Arrays.toString((float[]) array);
} else if (array instanceof double[]) {
return Arrays.toString((double[]) array);
} else if (array instanceof boolean[]) {
return Arrays.toString((boolean[]) array);
} else if (array instanceof char[]) {
return Arrays.toString((char[]) array);
} else if (array instanceof byte[]) {
return Arrays.toString((byte[]) array);
} else if (array instanceof short[]) {
return Arrays.toString((short[]) array);
}
return String.valueOf(array);
}
/** 通用数组/集合/Map/对象格式化 */
private static String stringify(Object obj) {
if (obj == null) {
return KentNull.nullNullString();
}
if (obj instanceof Throwable) {
return stringifyThrowable((Throwable) obj);
}
// 检查是否为数组
Class<?> clazz = obj.getClass();
if (clazz.isArray()) {
if (clazz == int[].class) {
return Arrays.toString((int[]) obj);
} else if (clazz == long[].class) {
return Arrays.toString((long[]) obj);
} else if (clazz == float[].class) {
return Arrays.toString((float[]) obj);
} else if (clazz == double[].class) {
return Arrays.toString((double[]) obj);
} else if (clazz == boolean[].class) {
return Arrays.toString((boolean[]) obj);
} else if (clazz == char[].class) {
return Arrays.toString((char[]) obj);
} else if (clazz == byte[].class) {
return Arrays.toString((byte[]) obj);
} else if (clazz == short[].class) {
return Arrays.toString((short[]) obj);
} else {
// 对象数组
return Arrays.deepToString((Object[]) obj);
}
}
// 检查是否为集合或Map
if (obj instanceof Collection) {
return String.format("[Collection: size=%d, %s]",
((Collection<?>) obj).size(), obj.toString());
}
if (obj instanceof Map) {
return String.format("[Map: size=%d, %s]",
((Map<?, ?>) obj).size(), obj.toString());
}
return obj.toString();
}
private static String stringifyThrowable(Throwable tr) {
StringWriter sw = new StringWriter();
tr.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
/** 日志方法接口 */
@FunctionalInterface
private interface LogMethod {
void log(String tag, String msg);
}
}
支持 Ctrl+A 全选,Ctrl+C 复制