Unlucky Launcher v0.0.1-alpha
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s

Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent),
MCP server 接入外部 Agent 工具,GUI 开发中。
This commit is contained in:
AskaEth committed 2026-10-31 21:54:10 +08:00
commit bc41dfcf7d
517 files changed
+92844

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
//! 日志级别猜测与“关键行”判定。
//!
//! 平移自 `analyse/02` §6.4(`Log4jLevel.guessLevel`):先认 log4j 注入的
//! `[HH:mm:ss] [thread/LEVEL]` 格式,再退到 JUL 的 `[INFO]` 形态;`stderr` 一律当
//! ERROR(`[authlib-injector]` 例外)。
//!
//! 目的有两个:给事件流打 `level`,以及让环形缓冲能“关键行永久保留”
//! (`analyse/02` §7.10-3:2000 行截断会把根因行挤掉)。
/// 日志级别(认知顺序:Error 最严重)。
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level {
/// 错误。
Error,
/// 警告。
Warn,
/// 信息。
Info,
/// 调试。
Debug,
/// 无法判断。
Unknown,
}
impl Level {
/// 事件流里用的字符串形式。
pub fn as_str(self) -> &'static str {
match self {
Self::Error => "error",
Self::Warn => "warn",
Self::Info => "info",
Self::Debug => "debug",
Self::Unknown => "unknown",
}
}
/// 是否错误级(退出分析筛行用)。
pub fn is_error(self) -> bool {
self == Self::Error
}
}
/// 猜测一行日志的级别。
pub fn guess(is_stderr: bool, line: &str) -> Level {
let trimmed = line.trim_start();
if is_stderr {
// authlib-injector 往 stderr 打的都是正常信息(`analyse/02` §6.4)。
if trimmed.starts_with("[authlib-injector]") {
return Level::Info;
}
return Level::Error;
}
if let Some(level) = log4j_level(trimmed) {
return level;
}
jul_level(trimmed).unwrap_or(Level::Unknown)
}
/// 是否“关键行”:命中这些特征的行不随环形淘汰(`Exception|Error|Caused by|OutOfMemory`)。
pub fn is_key_line(line: &str) -> bool {
let lower = line.to_ascii_lowercase();
lower.contains("exception")
|| lower.contains("error")
|| lower.contains("caused by")
|| lower.contains("outofmemory")
|| lower.contains("fatal")
}
/// 从 `[12:34:56] [Render thread/INFO]: ...` 里取级别。
///
/// 逐段扫描方括号内容,取第一段形如 `<线程名>/<LEVEL>` 的级别,最多看 3 段。
fn log4j_level(line: &str) -> Option<Level> {
let mut rest = line;
for _ in 0..3 {
let open = rest.find('[')?;
let tail = &rest[open + 1..];
let close = tail.find(']')?;
let segment = &tail[..close];
if let Some(slash) = segment.rfind('/') {
if let Some(level) = level_from_name(&segment[slash + 1..]) {
return Some(level);
}
}
rest = &tail[close + 1..];
}
None
}
fn level_from_name(name: &str) -> Option<Level> {
match name.trim() {
"ERROR" | "FATAL" => Some(Level::Error),
"WARN" | "WARNING" => Some(Level::Warn),
"INFO" | "NOTICE" => Some(Level::Info),
"DEBUG" | "TRACE" | "FINE" | "FINER" | "FINEST" => Some(Level::Debug),
_ => None,
}
}
/// JUL/裸标记形态:`[INFO]`、`[SEVERE]`、`[WARNING]`。
fn jul_level(line: &str) -> Option<Level> {
const MARKERS: &[(&str, Level)] = &[
("[SEVERE]", Level::Error),
("[ERROR]", Level::Error),
("[WARNING]", Level::Warn),
("[WARN]", Level::Warn),
("[INFO]", Level::Info),
("[DEBUG]", Level::Debug),
("[FINE]", Level::Debug),
];
MARKERS
.iter()
.find(|(marker, _)| line.starts_with(marker))
.map(|(_, level)| *level)
}