- POST /logs/metrics:LogQL 指标查询代理,[$step] 占位符按跨度自适应步长,返回多序列(httptest 假 Loki 单测) - 快捷查询:选中 App/Pod 即写入输入框并立即查询,面板保持打开支持 Pod 级联;命名空间仅级联筛选不插入,切换时清理失效标签 - 删除冗余的「查询该范围日志」「指定副本日志」按钮;Ingress 按钮仅在选择 ingress 命名空间/app 时显示 - /logs 支持 ?q= 预填自动执行(表格「查看日志」跳转入口) - 清理无引用死组件 LogResults/LogHistogram
71 lines
2.7 KiB
TypeScript
71 lines
2.7 KiB
TypeScript
// 日志查询纯工具(与组件分离)
|
|
|
|
/** 从 LogQL 提取 {...} 选择器部分 */
|
|
export function extractSelector(query: string): string {
|
|
const i = query.indexOf('{')
|
|
const j = query.indexOf('}', i)
|
|
return i >= 0 && j > i ? query.slice(i, j + 1) : ''
|
|
}
|
|
|
|
/**
|
|
* 替换 LogQL 的 {...} 选择器,保留其后的管道过滤(|= "x" 等);
|
|
* 原查询没有选择器时把新选择器前置到最前面
|
|
*/
|
|
export function replaceSelector(query: string, nextSelector: string): string {
|
|
const current = extractSelector(query)
|
|
if (!current) return nextSelector + query.trim().replace(/^\{[^}]*\}/, '')
|
|
const i = query.indexOf(current)
|
|
return query.slice(0, i) + nextSelector + query.slice(i + current.length)
|
|
}
|
|
|
|
/** 从选择器里删除指定标签的所有匹配条件(=、!=、=~、!~) */
|
|
export function removeLabelFilter(query: string, key: string): string {
|
|
const current = extractSelector(query)
|
|
if (!current) return query
|
|
const inner = current.slice(1, -1).trim()
|
|
if (!inner) return query
|
|
const pattern = new RegExp(`^${key}(=|!=|=~|!~)`)
|
|
const kept = inner.split(',').map((s) => s.trim()).filter(Boolean).filter((p) => !pattern.test(p))
|
|
const next = kept.length ? `{${kept.join(', ')}}` : '{}'
|
|
return replaceSelector(query, next)
|
|
}
|
|
|
|
/** 选择器里是否已包含指定标签的匹配条件 */
|
|
export function hasLabelFilter(query: string, key: string): boolean {
|
|
const current = extractSelector(query)
|
|
if (!current) return false
|
|
return current
|
|
.slice(1, -1)
|
|
.split(',')
|
|
.some((part) => new RegExp(`^\\s*${key}(=|!=|=~|!~)`).test(part))
|
|
}
|
|
|
|
/**
|
|
* 把 key="value" 合并进 LogQL 的 {...} 选择器(同名标签替换旧值);exclude=true 用 != 排除
|
|
*/
|
|
export function applyLabelFilter(query: string, key: string, value: string, exclude = false): string {
|
|
const op = exclude ? '!=' : '='
|
|
const frag = `${key}${op}"${value}"`
|
|
const i = query.indexOf('{')
|
|
if (i < 0) return `{${frag}}`
|
|
const j = query.indexOf('}', i)
|
|
if (j < 0) return query
|
|
const inner = query.slice(i + 1, j).trim()
|
|
// 同名标签(=、!=、=~、!~)替换,其余保留
|
|
const parts = inner ? inner.split(',').map((s) => s.trim()).filter(Boolean) : []
|
|
const kept = parts.filter((p) => !new RegExp(`^${key}(=|!=|=~|!~)`).test(p))
|
|
kept.push(frag)
|
|
return query.slice(0, i + 1) + kept.join(', ') + query.slice(j)
|
|
}
|
|
|
|
/** 提取 LogQL 管道中的字面过滤词(|= "x" 与 != "y"),用于结果行高亮 */
|
|
export function literalFilters(query: string): string[] {
|
|
const terms: string[] = []
|
|
const re = /\|=\s*"((?:[^"\\]|\\.)*)"/g
|
|
let m: RegExpExecArray | null
|
|
while ((m = re.exec(query)) !== null) {
|
|
if (m[1]) terms.push(m[1].replace(/\\"/g, '"'))
|
|
}
|
|
return terms
|
|
}
|