Log工具类-在命令行输出提示信息

2026年01月21日 06:17

描述

在命令行输出提示信息

标签

Android Java
Log工具类_在命令行输出提示信息.txt 14,531 字符 | 372 行
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 复制