项目概述
本项目实现日志检索服务:把结构化日志导入按天切分的索引,提供限定时间范围的过滤与聚合查询,并按保留策略滚动与删除旧索引。日志写起来廉价、查起来昂贵,若保留没有被设计就会无限增长。
架构与关键决策
导入端点按映射校验文档后批量写入别名之下的当日索引,使查询路径无需知道哪个具体索引持有近期数据;查询端点要求时间范围并在此内过滤,只触达与范围重叠的索引。保留以索引生命周期表达:按容量或时间滚动,把旧索引迁移到更廉价的层,超过窗口后删除。刷新间隔按查询 SLA 取舍,短则新日志可见更快而导入更贵,长则相反。
实施与测试
导入固定文档集断言过滤查询计数与聚合桶与预期一致;断言超过保留窗口的索引被选中删除;断言缺少时间范围的查询被拒;通过索引一条覆盖各字段类型的文档并回查验证映射。
限制与后续
- 仅接收结构化日志,自由文本解析留给生产方。
- 超出保留窗口无跨集群检索或冷存储归档。
- 聚合无物化视图,宽聚合每次渲染都重算。
代码示例
pnpm init
pnpm add @opensearch-project/opensearch express
pnpm add -D typescript tsx vitest
log-search/
├── src/ingest.ts
├── src/search.ts
├── src/rollover.ts
├── src/retention.ts
├── tests/search.test.ts
└── package.json
PUT _index_template/logs {
"index_patterns": ["logs-*"],
"template": { "settings": { "number_of_shards": 2 }, "mappings": { "properties": { "@timestamp": {"type":"date"}, "level": {"type":"keyword"} } } }
}
await client.bulk({ refresh: false, body: docs.flatMap((d) => [{ index: { _index: 'logs-write' } }, d]) });
{ query: { bool: { filter: [ { range: { '@timestamp': { gte: from, lte: to } } }, { term: { level: 'error' } } ] } } }
"aggs": { "per_minute": { "date_histogram": { "field": "@timestamp", "fixed_interval": "1m" } } }
await client.indices.delete({ index: 'logs-' + cutoffDate, ignore_unavailable: true });
pnpm vitest run