2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Kürzlich habe ich darüber nachgedacht, wie ich die Protokolle klarer gestalten kann, um das Auffinden von Problemen zu erleichtern, ohne in jeder Klasse ein Logger-Objekt zu erstellen und unterschiedliche Farben zu verwenden, um sie zu unterscheiden. Verwenden Sie die Richtung des Stapels, um das Klassenproblem des Protokolls zu ermitteln. Um den Effekt zu sehen, laden Sie einfach den Code hoch.
>>>>>>>> java 版本代码 : import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogUtil { public static void trace(String msg) { getLogger().trace(msg); } public static void debug(String msg) { getLogger().debug("u001B[34m" + msg + "u001B[0m"); } public static void info(String msg) { getLogger().info("u001B[32m" + msg + "u001B[0m"); } public static void warning(String msg) { getLogger().warn("u001B[33m" + msg + "u001B[0m"); } public static void error(String msg) { getLogger().error("u001B[31m" + msg + "u001B[0m"); } public static void error(String msg, Throwable t) { getLogger().error("u001B[31m" + msg + "u001B[0m", t); } private static Logger getLogger() { return LoggerFactory.getLogger(findCaller()); } /** * 查找调用者的方法。 * 通过分析当前线程的堆栈跟踪,找到第一个不是LogUtil类的方法作为调用者。 * 这个方法主要用于在日志记录时,确定日志的来源,以便更好地定位问题。 * * @return 调用者的类路径和方法名。 */ private static String findCaller() { // 获取当前线程的堆栈跟踪元素 // 获取堆栈信息 StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); // 初始化调用者堆栈元素为null // 最原始被调用的堆栈信息 StackTraceElement caller = null; // 获取LogUtil类的全限定名 // 日志类名称 String logClassName = LogUtil.class.getName(); // 遍历堆栈跟踪元素,找到第一个不是LogUtil类的元素 // 循环遍历到日志类标识 int i = 0; for (int len = callStack.length; i < len; i++) { if (logClassName.equals(callStack[i].getClassName())) { break; } } // 调用者位于LogUtil类元素之后的第三个元素(考虑到findCaller自身和getStackTrace的调用) caller = callStack[i + 3]; // 获取调用者的类路径和方法名 String fullPath = getPath(caller); // 返回调用者的类路径和方法名 return fullPath; } /** * 获取调用者堆栈跟踪元素的详细路径。 * <p> * 本方法用于生成调用者方法的详细路径,包括类名、方法名和行号。这样生成的路径可以帮助定位代码中调用此方法的位置, * 对于调试和跟踪代码执行流程非常有用。 * * @param caller 调用者堆栈跟踪元素,通过StackTraceElement获取。 * @return 返回一个字符串,包含调用者类名、方法名和行号的详细路径。 */ @NotNull private static String getPath(StackTraceElement caller) { // 格式化类名,去除包名,只保留类名部分。 String formatClassName = caller.getClassName(); // 获取调用者方法名。 String methodName = caller.getMethodName(); // 获取调用者方法所在的行号。 int lineNumber = caller.getLineNumber(); // 拼接类名、方法名和行号,生成详细的路径。 String fullPath = formatClassName + "." + methodName + "." + lineNumber; return fullPath; } /** * 格式化类名。 * 将带有包名的类名转换为首字母大写的形式,去除包名并连接类名。 * 如果类名本身不包含包名,直接返回原类名。 * @param name 完整的类名,包括包名。 * @return 格式化后的类名,不包含包名。 */ private static String simpleClassName(String name) { // 使用正则表达式按点号分割类名和包名 String[] split = name.split("\."); // 使用 StringBuffer 来构建最终的类名,以确保线程安全和效率 StringBuffer buf = new StringBuffer(); for (int i = 0; i < split.length; i++) { // 如果当前元素不是最后一个元素,说明它是包名的一部分 // 取出每个包名部分的第二个字符(即首字母)并连接到缓冲区 // 如果是最后一个元素,直接添加到缓冲区,以保留类名的完整性 if (i != split.length - 1) { buf.append(split[i].charAt(1)); buf.append("."); } else { buf.append(split[i]); } } // 将缓冲区转换为字符串并返回 return buf.toString(); } }
>>>>>>>> Scala-Versionscode:
import org.apache.log4j.Logger import org.apache.log4j.LogManager import scala.util.control.Breaks.{break, breakable} class LOGGER() {} /** * Created by wqj on 16/6/30. * 日志记录接口,跟spark本身的日志分开记录 */ object LOGGER extends Serializable { /** * 输出debug日志 * * @param message ,日志消息对象 * */ def debug(message: Object): Unit = { getLogger.debug("u001B[34m" + message + "u001B[0m") } /** * 输出info日志 * * @param message ,日志消息对象 * */ def file(message: Object): Unit = { getLogger.info(message) } /** * 输出warn日志 * * @param message ,日志消息对象 * */ def warn(message: Object): Unit = { getLogger.warn("u001B[33m" + message + "u001B[0m") } /** * 输出error日志 * * @param message ,日志消息对象 * */ def error(message: Object): Unit = { getLogger.error("u001B[31m" + message + "u001B[0m") } /** * 输出info日志, 由于本处打印的日志无法与spark日志分开, 故使用error等级输出属于info范畴的日志. 此处使用logInfo间接调用error, 为 * 以后可能实现日志分开做准备 * * @param message ,日志消息对象 * */ def info(message: Object): Unit = { getLogger.info("u001B[32m" + message + "u001B[0m") } /** * 获取日志记录器 * * @return 日志记录器 * */ def getLogger(): Logger = { LogManager.getLogger(findCaller) } /** * 查找调用者的方法。 * 通过分析当前线程的堆栈跟踪,找到第一个不是LogUtil类的方法作为调用者。 * 这个方法主要用于在日志记录时,确定日志的来源,以便更好地定位问题。 * * @return 调用者的类路径和方法名。 */ private def findCaller(): String = { // 获取当前线程的堆栈跟踪元素 // 获取堆栈信息 val callStack = Thread.currentThread.getStackTrace // 初始化调用者堆栈元素为null // 最原始被调用的堆栈信息 var caller: StackTraceElement = null // 获取LogUtil类的全限定名 // 日志类名称 val logClassName = LOGGER.getClass.getName // 遍历堆栈跟踪元素,找到第一个不是LogUtil类的元素 // 循环遍历到日志类标识 var i = 0 val len = callStack.length breakable { while (i < len) { if (logClassName == callStack(i).getClassName) break i += 1 } } val index = i + 3 // 调用者位于LogUtil类元素之后的第三个元素(考虑到findCaller自身和getStackTrace的调用) caller = callStack(index) // 获取调用者的类路径和方法名 val fullPath = getFullPath(caller) // 返回调用者的类路径和方法名 fullPath } /** * 获取调用者堆栈跟踪元素的详细路径。 * <p> * 本方法用于生成调用者方法的详细路径,包括类名、方法名和行号。这样生成的路径可以帮助定位代码中调用此方法的位置, * 对于调试和跟踪代码执行流程非常有用。 * * @param caller 调用者堆栈跟踪元素,通过StackTraceElement获取。 * @return 返回一个字符串,包含调用者类名、方法名和行号的详细路径。 */ private def getFullPath(caller: StackTraceElement): String = { // 格式化类名,去除包名,只保留类名部分。 val formatClassName = caller.getClassName // 获取调用者方法名。 val methodName = caller.getMethodName // 获取调用者方法所在的行号。 val lineNumber = caller.getLineNumber // 拼接类名、方法名和行号,生成详细的路径。 formatClassName + "." + methodName + "." + lineNumber } }
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="ture" scanPeriod="60 seconds" debug="false"> <!-- 定义日志的根目录 --> <property name="LOG_HOME" value="./logs"/> <!-- 定义日志文件名称 --> <property name="APP_NAME" value="myApp"/> <!-- 控制台输出 --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <encoder> <charset>UTF-8</charset> <!-- <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %green(%logger{50}) - %msg%n</pattern> </encoder> </appender> <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 --> <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 指定日志文件的名称 --> <file>${LOG_HOME}/${APP_NAME}.log</file> <!-- 当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 --> <fileNamePattern>${LOG_HOME}/${APP_NAME}-%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 只保存最近30天的文件 --> <MaxHistory>30</MaxHistory> <!--用来指定日志文件的上限大小 --> <totalSizeCap>10GB</totalSizeCap>--> </rollingPolicy> <!-- 日志输出编码格式化 --> <encoder> <charset>UTF-8</charset> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%line - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="console"/> <appender-ref ref="appLogAppender"/> </root> </configuration>
log4j.rootLogger=DEBUG, stdout, R log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c - %m%n log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=ai.log log4j.appender.R.MaxFileSize=10240KB log4j.appender.R.MaxBackupIndex=10 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c - %m%n log4j.logger.org.springframework=WARN log4j.logger.com.lagooo.as=INFO log4j.logger.org.apache=INFO log4j.logger.org.apache.log4j.Logger=error