日志过滤器

日志过滤器

一个轻量级日志过滤模组,旨在减少控制台和日志文件中的垃圾信息,让开发者和玩家能够专注于关键错误并节省磁盘空间。

日志过滤器

一个轻量级的日志过滤模组,旨在减少控制台和日志文件中刷屏的信息,让开发者和玩家专注于关键错误并节省磁盘空间。

功能特性

  • 正则过滤:支持强大的正则匹配,精确过滤特定日志内容。
  • 精确匹配过滤:可以精确过滤掉指定的整行文本。
  • 按记录器过滤:支持过滤某个模组或类名(Logger Name)的所有日志。
  • 按日志等级过滤:一键屏蔽 TRACE、DEBUG 等低级日志。

    屏蔽 INFO 日志?当然可以!这样你只会看到错误和警告,适合调试使用。

  • 白名单模式:设置排除规则,确保重要日志(如关键错误)不会被误删。
  • 高效缓存:内置去重缓存机制,避免重复处理相同日志。

安装

  1. 下载模组的 JAR 文件。
  2. 将 JAR 文件放入你的 Minecraft 安装目录下的 mods 文件夹中。
  3. 启动一次游戏。模组会自动生成默认配置文件。
  4. 根据需要修改配置文件,然后重启游戏使配置生效。

或者手动创建 logfilter-common.toml 文件,根据需要修改配置,然后启动游戏。

配置简要说明

配置文件位于:config/logfilter-common.toml 默认配置:

[General]
	#Enable or disable log filtering
	enableFilter = true
	#Enable debug mode to see which logs are being filtered
	debugMode = false
	#Maximum size of filtered log cache (for duplicate detection)
	#Range: 100 ~ 10000
	maxCacheSize = 1000
[FilterRules]
	#Regex patterns to filter log messages
	filterRules = []
	#Exact message strings to filter (case-sensitive)
	exactMatches = []
	#Logger names to completely filter (e.g., 'net.minecraft.server.MinecraftServer')
	loggerNames = []
	#Log levels to filter: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
	logLevels = ["TRACE", "DEBUG"]
	#Regex patterns that will EXCLUDE logs from filtering (whitelist)
	excludePatterns = []

General(常规设置)

选项 默认值 描述
enableFilter true 是否启用日志过滤功能。
debugMode false 启用调试模式,将过滤的日志打印到控制台,便于测试规则。
maxCacheSize 1000 已过滤日志的缓存大小,用于去重。

FilterRules(过滤规则)

1. filterRules(正则表达式)

使用正则匹配需要过滤的日志。

filterRules = [
    ".*Exception.*",          # 过滤掉所有包含 "Exception" 的日志
    ".*stack trace.*",        # 过滤掉堆栈跟踪
    ".*Could not pass event.*"# 过滤掉特定事件错误
]

2. exactMatches(精确匹配)

只有当日志内容完全一致(区分大小写)时才会被过滤。

exactMatches = [
    "This message will be completely filtered out"
]

3. loggerNames(记录器名称)

根据日志的来源(类名或模组包名)进行过滤。支持层级过滤。

loggerNames = [
    "net.minecraft.server.MinecraftServer",  # 过滤掉主服务器的日志
    "com.some.noisy.mod"                     # 过滤掉某个刷屏模组的所有日志
]

4. logLevels(日志等级)

根据日志等级进行过滤。可用值:TRACE、DEBUG、INFO、WARN、ERROR、FATAL。

logLevels = [
    "TRACE",
    "DEBUG"
]

5. excludePatterns(白名单/排除规则)

非常重要。匹配此处规则的日志将不会被过滤,即使其他规则也匹配了它们。用于保留关键错误。

excludePatterns = [
    ".*CRITICAL ERROR.*",  # 即使匹配 Exception,如果是 CRITICAL ERROR,也保留
    ".*IMPORTANT.*"
]

🔨 构建

如果你想自行编译此模组:

./gradlew build

编译后的 JAR 文件位于 build/libs/ 目录中。


深入解析

第一部分:模组代码层面的工作原理

此模组的核心思想是拦截。它在 Minecraft 的日志输出到达控制台或文件之前“劫持”数据,决定是放行还是丢弃。

1. 日志框架基础:Log4j 2

Minecraft 1.20.1 使用 Log4j 2 日志框架。

  • 日志流程:游戏代码生成日志事件(LogEvent)-> 传递给 Log4j 的 LoggerContext -> 传递给 Appender(如控制台 appender、文件写入器)-> 最终显示在屏幕上。
  • 过滤器机制:Log4j 允许在 Logger 或 Appender 上挂载“过滤器(Filter)”。

2. 代码执行流程

我们的模组在游戏启动时(FMLCommonSetupEvent)通过 LogFilterManager.java 执行以下操作:

  1. 获取上下文:

    LoggerContext loggerContext = LoggerContext.getContext(false);
    

    我们获取了 Minecraft 当前的日志环境上下文。

  2. 获取根配置:

    Configuration configuration = loggerContext.getConfiguration();
    LoggerConfig rootLoggerConfig = configuration.getRootLogger();
    

    我们获取了“根 Logger”的配置。由于几乎所有日志最终都会流向根 Logger,因此在此处挂载过滤器可以捕获全局日志。

  3. 注册自定义过滤器:

    log4jFilter = new FilteringLogFilter(); // 我们的自定义过滤器类
    log4jFilter.start();
    rootLoggerConfig.addFilter(log4jFilter);
    

    我们将自定义的 FilteringLogFilter 插入到日志处理链的最前端。

  4. 拦截与决策(FilteringLogFilter.filter 方法): 每当 Minecraft 尝试输出一行日志时,Log4j 都会调用我们的 filter(LogEvent event) 方法。为避免日志过滤导致游戏卡顿,我们采用“快速失败”设计,逻辑优先级如下:

    • 步骤 A:提取关键信息。 直接从 Log4j 的 LogEvent 中提取 Logger 名称和等级等关键细节。
    • 步骤 B:快速检查(等级与 Logger)。 优先检查日志等级(logLevels)和 Logger 名称(loggerNames)。这是开销极低的操作。若匹配到,日志会被立即丢弃,完全跳过后续开销昂贵的消息格式化和正则匹配,显著降低主线程开销。
    • 步骤 C:消息格式化。 只有当日志通过快速检查且未被拦截时,才会调用 getFormattedMessage() 生成消息文本。
    • 步骤 D:缓存检查。 计算日志内容的哈希值,检查是否存在于缓存中。若存在,立即返回 DENY,避免重复计算。
    • 步骤 E:白名单检查(excludePatterns)。 若日志匹配白名单正则,返回 NEUTRAL。白名单优先级最高,用于保护重要日志。
    • 步骤 F:精确匹配检查(exactMatches)。 若日志消息与配置的字符串完全一致,返回 DENY。
    • 步骤 G:正则规则检查(filterRules)。 若日志消息匹配配置的正则表达式,返回 DENY。
  5. 最终判定:

    • 若上述任一拦截条件匹配,我们返回 Result.DENY。收到 DENY 后,Log4j 会立即丢弃该日志,它将不会出现在控制台或文件中。
    • 若均不匹配,我们返回 Result.NEUTRAL。Log4j 认为过滤器“无意见”,继续将日志传递给下一个处理器,最终正常显示。

第二部分:实际案例分析及配置

让我们看这条日志:

[241... 00:32:22.574] [Render thread/WARN] [net.minecraft.client.renderer.ShaderInstance/]: Shader rendertype_entity_translucent_emissive could not find sampler named Sampler2 in the specified shader program.

1. 日志条目剖析

我们需要将这条日志拆解为几个关键部分:

日志片段 含义 对应配置项 备注
WARN 日志等级 logLevels 表示这是警告,而非错误或信息。
net.minecraft.client.renderer.ShaderInstance 记录器名称 loggerNames 发出此日志的 Java 类的完全限定名。
Shader ... program. 日志消息 filterRules、exactMatches 具体的错误内容。

2. 如何确定它属于哪个配置项?

  • 如果你想屏蔽某个类/模组发出的所有消息(例如,我厌倦了看到这个 Shader 类的所有错误),应使用 loggerNames。
  • 如果你想屏蔽某个等级的所有消息(例如,我不想看到任何 WARN 级别的消息),应使用 logLevels。
  • 如果你想精确屏蔽这一句话(即使它只出现一次),应使用 exactMatches。
  • 如果你想模糊屏蔽这类错误(例如,无论它找不到 Sampler1 还是 Sampler2,或者无论哪个 Shader,只要“找不到 sampler”就屏蔽),应使用 filterRules。

3. 实用配置方案

针对这个特定的 Shader 错误,我们有四种过滤策略。请根据你的需求选择:

方案 A:精确过滤此错误(推荐 - 使用 filterRules)

这条日志的核心特征是“找不到 sampler”。我们可以编写一个正则表达式,涵盖所有找不到 sampler 的情况,而不影响其他日志。 适用场景:你认为所有关于着色器 sampler 缺失的警告都无关紧要,想要全部屏蔽。
配置代码:

# 说明:.* 表示任意字符,匹配所有包含 "could not find sampler named" 的日志,无论其前后是什么内容。
	filterRules = [".*could not find sampler named.*"]
方案 B:只屏蔽这条特定的长句(使用 exactMatches)

如果你只想屏蔽关于 Sampler2 未找到的特定警告,但保留其他 Shader 警告。 适用场景:极其精确的打击,无附带损伤。
配置代码:

# 必须与日志中的文本完全一致(通常不包括时间戳和线程名,只包括冒号之后的内容)
	exactMatches = ["Shader rendertype_entity_translucent_emissive could not find sampler named Sampler2 in the specified shader program."]
方案 C:屏蔽 ShaderInstance 类的所有日志(使用 loggerNames)

如果 ShaderInstance 类非常吵,你完全不想看到它输出的任何内容。 适用场景:激进过滤。警告:如果此类后续输出了严重错误日志,你也将看不到。
配置代码:

# 只要日志来源是 net.minecraft.client.renderer.ShaderInstance,就全部屏蔽。
	loggerNames = ["net.minecraft.client.renderer.ShaderInstance"]
方案 D:屏蔽所有 WARN 级别日志(使用 logLevels)

适用场景:不推荐。这会屏蔽所有警告消息,可能导致你错过真正需要关注的潜在问题。
配置代码:

	logLevels = ["WARN"]