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("") || // 未知文件 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); } }