Skip to content

Commit 999c3bd

Browse files
committed
fixed: 目录下重名问题(#2)
1 parent d40d823 commit 999c3bd

10 files changed

Lines changed: 353 additions & 22 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ data/
33
dist/
44
config.toml
55
config.test.toml
6+
.env.local
67

78
backend/target/
89
frontend/node_modules/

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ LogSearch是一个低内存、高性能、渐进式检索工具。
1616

1717
## 它适合这些场景:
1818

19+
- 开发测试环境没有条件上 elk,又不想命令检索。
1920
- 在多份应用日志里快速找关键字。
2021
- 搜索错误、链路 ID、订单号、用户 ID、类名、接口名。
2122
-`AND` / `OR` 组合条件缩小范围。
@@ -89,6 +90,7 @@ path = "/var/log/my-app/worker.log"
8990
id = "my-app"
9091
path = "/var/log/my-app"
9192
include = ["*.log", "*.gz", "*.zst", "*.bz2", "*.xz"]
93+
exclude = []
9294
recursive = false
9395
```
9496

@@ -98,10 +100,25 @@ recursive = false
98100
- `path` 是真实日志路径。
99101
- 可以配置多个 `[[files]]`
100102
- 也可以配置 `[[directories]]`,目录下匹配 `include` 的日志会自动发现并加入搜索。
103+
- `path` 必须是真实目录,不支持 `./logs/**` 这类通配符;需要筛选子目录时,把通配符写到 `include`
104+
- `include` 可以匹配文件名,例如 `*.log`;也可以在 `recursive = true` 时匹配相对路径,例如 `2026-05-*/*.log`
105+
- `exclude` 可以排除相对路径,例如 `**/debug.log`
101106
- `.gz``.zst``.bz2``.xz` 文件会按对应压缩格式自动解压搜索,并在页面里显示压缩类型。
102107
- `recursive = false` 表示只扫描目录第一层。
108+
- 如果日志按日期放在子目录里,可以设置 `recursive = true`,例如 `/var/log/my-app/2026-05-02/error.log``/var/log/my-app/2026-05-03/error.log` 会分别显示为不同来源。
103109
- 页面里的 `File` 下拉框可以选择全部文件或某一个文件。
104110

111+
例如只搜索 2026 年 5 月的日志:
112+
113+
```toml
114+
[[directories]]
115+
id = "my-app-2026-05"
116+
path = "/var/log/my-app"
117+
include = ["2026-05-*/*.log"]
118+
exclude = ["**/debug.log"]
119+
recursive = true
120+
```
121+
105122
## 怎么搜索
106123

107124
### 搜普通关键字

backend/src/config.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub struct LogDirectoryConfig {
4141
#[serde(default = "default_directory_include")]
4242
pub include: Vec<String>,
4343
#[serde(default)]
44+
pub exclude: Vec<String>,
45+
#[serde(default)]
4446
pub recursive: bool,
4547
}
4648

@@ -81,6 +83,7 @@ impl AppConfig {
8183
}
8284

8385
for directory in &mut cfg.directories {
86+
validate_directory_path(&directory.path)?;
8487
if directory.path.is_relative() {
8588
directory.path = base.join(&directory.path);
8689
}
@@ -91,6 +94,17 @@ impl AppConfig {
9194
}
9295
}
9396

97+
fn validate_directory_path(path: &Path) -> anyhow::Result<()> {
98+
let source = path.to_string_lossy();
99+
if source.contains('*') || source.contains('?') || source.contains('[') {
100+
anyhow::bail!(
101+
"directories.path must be a real directory, put patterns in include instead: path = {:?}",
102+
path
103+
);
104+
}
105+
Ok(())
106+
}
107+
94108
fn absolutize(path: &Path) -> anyhow::Result<PathBuf> {
95109
if path.exists() {
96110
return Ok(path.canonicalize()?);
@@ -153,6 +167,28 @@ path = "./logs"
153167
config.directories[0].include,
154168
vec!["*.log", "*.gz", "*.zst", "*.bz2", "*.xz"]
155169
);
170+
assert!(config.directories[0].exclude.is_empty());
156171
assert!(!config.directories[0].recursive);
157172
}
173+
174+
#[test]
175+
fn rejects_glob_patterns_in_directory_path() {
176+
let dir = tempfile::tempdir().unwrap();
177+
let config_path = dir.path().join("config.toml");
178+
std::fs::write(
179+
&config_path,
180+
r#"
181+
[[directories]]
182+
id = "release"
183+
path = "./logs/**"
184+
include = ["*.log"]
185+
"#,
186+
)
187+
.unwrap();
188+
189+
let err = AppConfig::load(&config_path).unwrap_err();
190+
191+
assert!(err.to_string().contains("directories.path must be a real directory"));
192+
assert!(err.to_string().contains("put patterns in include instead"));
193+
}
158194
}

0 commit comments

Comments
 (0)