https://modrinth.com/mod/log-filter1
一个轻量级日志过滤模组,旨在减少控制台和日志文件中的垃圾信息,让开发者和玩家能更专注于关键错误,并节省硬盘空间。
- 正则表达式过滤:支持强大的正则表达式匹配,精准过滤特定内容的日志。
- 完全匹配过滤:可以精准过滤掉指定的整条文本信息。
- 按日志记录器过滤:支持按模组或类名(Logger Name)过滤整个模组的日志。
- 按日志等级过滤:一键屏蔽
TRACE或DEBUG等低级日志。屏蔽
INFO日志?当然可以!这样你只会看到报错和警告,适用于调试。 - 白名单模式:设置排除规则,确保重要日志(如严重错误)不会被误删。
- 高效缓存:内置去重缓存机制,避免重复处理相同的日志。
- 下载模组的 JAR 文件。
- 将 JAR 文件放入你的 Minecraft 安装目录下的
mods文件夹中。 - 启动游戏一次。模组会自动生成默认配置文件。
- 根据需要修改配置文件,保存后配置自动热重载生效(无需重启游戏)。非法正则会被告警跳过,详见下文配置说明。
或手动新建
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. Matching is PARTIAL (regex find): a rule hits when it matches any part of the message, so wrapping with '.*' is unnecessary.
#用于过滤日志消息的正则表达式。匹配方式为包含匹配(find): 规则命中消息任意片段即生效, 无需用 '.*' 包裹。
#REGEX ESCAPING: metacharacters ()[]{}*+?.^$|\ are special; escape them to match literally, e.g. literal parentheses must be written as \( \).
#正则转义: 元字符 ()[]{}*+?.^$|\ 有特殊含义, 按字面匹配必须转义, 例如字面圆括号必须写成 \( \)。
#TOML ESCAPING: in double-quoted strings TOML consumes one level of backslashes, so write "some \\(thing\\)"; in single-quoted literal strings write 'some \(thing\)' directly.
#TOML 转义: 双引号字符串中 TOML 会消耗一层反斜杠, 因此需写 "some \\(thing\\)"; 单引号字面字符串直接写 'some \(thing\)' 即可。
#An invalid regex (e.g. unbalanced parentheses) is safely skipped with a warning (log WARN + in-game chat message); other rules keep working.
#非法正则(如括号不闭合)会被安全跳过并告警(日志 WARN + 进入世界时聊天框提示), 其余规则照常生效。
filterRules = []
#Exact message strings to filter (case-sensitive)
#精确匹配并过滤的消息字符串(区分大小写)
exactMatches = []
#Logger names to completely filter (e.g., 'net.minecraft.server.MinecraftServer')
#完全过滤的 Logger 名称(例如 'net.minecraft.server.MinecraftServer')
loggerNames = []
#Log levels to filter: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
#要过滤的日志级别: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
logLevels = ["TRACE", "DEBUG"]
#Regex patterns that will EXCLUDE logs from filtering (whitelist)
#Regex/TOML escaping rules are the same as filterRules (see its comment above).
#将日志排除在过滤之外的正则表达式(白名单)
#正则与 TOML 的转义规则与 filterRules 相同(见其上方注释)。
excludePatterns = []
[async]
#Enable async log output: wraps all root appenders into a Log4j2 AsyncAppender so console/file I/O runs on a background thread instead of the logging thread
#启用异步日志输出: 将 root logger 的全部 appender 包装进 Log4j2 AsyncAppender, 控制台/文件 I/O 转移到后台线程执行, 打日志的线程只付入队成本
enableAsyncLogging = true
#Capacity of the async queue (number of buffered log events)
#异步队列容量(缓冲的日志事件数量)
#Range: 128 ~ 65536
asyncQueueSize = 4096
#When queue is full: true = briefly block the logging thread (no log loss), false = drop the log event
#队列满时: true = 短暂阻塞打日志线程(不丢日志), false = 丢弃该条日志事件
asyncBlockingWhenFull = true
#Milliseconds to wait for the queue to drain before discarding remaining events (on pipeline rebuild and JVM shutdown)
#等待队列排干的毫秒数, 超时后丢弃剩余事件(在管线重建与 JVM 关闭时生效)
#Range: 0 ~ 600000
asyncShutdownTimeoutMs = 5000
#Capture caller location (class/method/line). Costs a stack walk per event; Minecraft's default log pattern does not need it
#捕获调用方位置(类/方法/行号)。每条日志付出一次栈遍历成本; Minecraft 默认日志格式用不到它, 保持 false 即可
asyncIncludeLocation = false
#Fully qualified class name of a org.apache.logging.log4j.core.async.BlockingQueueFactory implementation (empty = default ArrayBlockingQueue). Upgrade hook: a Disruptor-based factory can be injected later via JarJar without code changes
#BlockingQueueFactory 实现类的全限定名(留空 = 默认 ArrayBlockingQueue)。升级钩子: 后续可通过 JarJar 注入 Disruptor 队列工厂而无需改代码
asyncQueueFactoryClass = ""
| 选项 | 默认值 | 说明 |
|---|---|---|
enableFilter |
true |
是否启用日志过滤功能。 |
debugMode |
false |
启用调试模式,会在控制台打印被过滤掉的日志,方便测试规则。 |
maxCacheSize |
1000 |
过滤日志的缓存大小,用于去重。 |
使用正则表达式匹配要过滤的日志。
filterRules = [
".*Exception.*", # 过滤掉所有包含 "Exception" 的日志
".*stack trace.*", # 过滤掉堆栈跟踪
".*Could not pass event.*"# 过滤掉特定事件报错
]⚠ 常见坑:正则元字符与 TOML 双层转义
- 正则层:
()[]{}*+?.^$|\是正则元字符,按字面匹配必须转义。例:日志some (thing)的圆括号必须写成\(thing\),否则括号被解析为捕获组,规则静默失效(正则本身合法,无任何报错)。- TOML 层:双引号字符串中 TOML 会先消耗一层反斜杠,双引号写法需再翻倍:
"some \\(thing\\)";推荐用单引号字面字符串,免 TOML 转义:'some \(thing\)'。- 匹配语义:
filterRules为包含匹配(find),规则命中消息任意片段即生效,无需用.*包裹。- 非法正则(如括号不闭合)会被安全跳过:日志输出 WARN(含规则原文),玩家进入世界时聊天框会收到黄字警告,其余规则照常生效,不会导致启动崩溃。写规则前建议先在 regex101.com(选 Java flavor)验证。
只有当日志内容完全一致时才会被过滤(区分大小写)。
exactMatches = [
"This message will be completely filtered out"
]根据日志的来源(类名或模组包名)进行过滤。支持层级过滤。
loggerNames = [
"net.minecraft.server.MinecraftServer", # 过滤掉主服务器的日志
"com.some.noisy.mod" # 过滤掉某个吵闹的模组的所有日志
]根据日志级别进行过滤。可选值:TRACE, DEBUG, INFO, WARN, ERROR, FATAL。
logLevels = [
"TRACE",
"DEBUG"
]非常重要。此处的规则匹配的日志将不会被过滤,即使其他规则匹配到了它。用于保留关键错误。
excludePatterns = [
".*CRITICAL ERROR.*", # 即使匹配了 Exception,但如果是 CRITICAL ERROR,则保留
".*IMPORTANT.*"
]如果你想要自己编译此模组:
./gradlew build编译后的 JAR 文件位于 build/libs/ 目录。
这个模组的核心思想是拦截。它在 Minecraft 的日志输出到达控制台或文件之前,提前“劫持”了这些数据,决定是放行还是丢弃。
Minecraft 1.20.1 使用的是 Log4j 2 日志框架。
- 日志流向: 游戏代码产生日志事件 (
LogEvent) -> 传递给 Log4j 的LoggerContext-> 传递给Appender(如控制台输出器、文件写入器) -> 最终显示在屏幕上。 - Filter 机制: Log4j 允许在 Logger 或 Appender 上挂载“过滤器 (
Filter)”。 - 异步输出管线: root logger 的全部 Appender 会被包装进 Log4j2
AsyncAppender(默认开启,可配置):控制台/文件 I/O 转移到后台线程执行,产生日志的线程只付入队成本,队列容量与满载策略均可配置。
我们的模组通过 LogFilterManager.java 在游戏启动时 (FMLCommonSetupEvent) 执行以下操作:
-
获取上下文:
LoggerContext loggerContext = LoggerContext.getContext(false);
我们拿到了 Minecraft 当前的日志环境上下文。
-
获取根配置:
Configuration configuration = loggerContext.getConfiguration(); LoggerConfig rootLoggerConfig = configuration.getRootLogger();
我们拿到了“根 Logger”的配置。因为几乎所有日志最终都会流向根 Logger,所以在这里挂载过滤器可以捕获全局日志。
-
注册自定义过滤器:
log4jFilter = new FilteringLogFilter(); // 我们自定义的过滤器类 log4jFilter.start(); rootLoggerConfig.addFilter(log4jFilter);
我们将自定义的
FilteringLogFilter插入到了日志处理链的最前端。 -
拦截与决策 (
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。
- 步骤 A:提取关键信息。直接从 Log4j 的
-
最终裁决:
- 如果上述任何拦截条件匹配,我们返回
Result.DENY。Log4j 收到DENY后,会立即丢弃该日志,控制台和文件都不会显示。 - 如果都没匹配,我们返回
Result.NEUTRAL。Log4j 认为过滤器“没意见”,继续将日志传给下一个处理器,最终正常显示。
- 如果上述任何拦截条件匹配,我们返回
-
配置热重载: 修改并保存
logfilter-common.toml后,Forge 的配置文件监听立即触发重载事件:异步管线按参数签名幂等重建(参数未变则跳过),过滤规则重新编译,非法正则被跳过并告警——全程无需重启游戏。
让我们来看看这条日志:
[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.
我们需要把这条日志拆解成几个关键部分:
| 日志片段 | 含义 | 对应配置项 | 备注 |
|---|---|---|---|
WARN |
日志等级 | logLevels |
表示这是一条警告,不是错误也不是信息。 |
net.minecraft.client.renderer.ShaderInstance |
Logger 名称 (记录器名称) | loggerNames |
发出这条日志的 Java 类的全限定名。 |
Shader ... program. |
日志消息 | filterRules, exactMatches |
具体的报错内容。 |
- 如果你想屏蔽某个类/模组发出的所有消息(例如这个 Shader 类的所有报错我都懒得看),你应该用
loggerNames。 - 如果你想屏蔽某种级别的所有消息(例如我不想看任何 WARN 级别的消息),你应该用
logLevels。 - 如果你想精准屏蔽这一句话(哪怕它只出现一次),你应该用
exactMatches。 - 如果你想模糊屏蔽这类报错(例如不管它找不到的是 Sampler1 还是 Sampler2,或者是哪个 Shader,只要是“找不到采样器”就屏蔽),你应该用
filterRules。
针对这条具体的 Shader 报错,我们有四种过滤策略,请根据你的需求选择:
这条日志的核心特征是“找不到 sampler”。我们可以写一个正则表达式,涵盖所有找不到 sampler 的情况,而不影响其他日志。
适用场景:你认为所有关于找不到 Shader 采样器的警告都是无关紧要的,想全部屏蔽。
配置代码:
# 解释:.* 表示任意字符,匹配包含 "could not find sampler named" 的所有日志,不管它的前后有什么
filterRules = [".*could not find sampler named.*"]如果你只想屏蔽 Sampler2 这个特定找不到的警告,但保留其他 Shader 警告。
适用场景:极其精准的打击,不误伤友军。
配置代码:
# 必须和日志里的文字一模一样(通常不包括时间戳和线程名,只包括冒号后面的内容)
exactMatches = ["Shader rendertype_entity_translucent_emissive could not find sampler named Sampler2 in the specified shader program."]如果 ShaderInstance 这个类非常吵,你根本不想看它输出的任何东西。
适用场景:激进过滤。警告:如果这个类后续输出了严重的错误日志,你也看不到了。
配置代码:
# 只要日志来源是 net.minecraft.client.renderer.ShaderInstance,全部屏蔽
loggerNames = ["net.minecraft.client.renderer.ShaderInstance"]适用场景:不推荐。这会屏蔽所有的警告信息,可能导致你错过真正需要关注的潜在问题。
配置代码:
logLevels = ["WARN"]