refactor(i18n): U22 日志中心组件 i18n 治理 — QuickQueryBar/types-v3/TimeRangePicker/LogRBAC 中文键清零

- QuickQueryBar.tsx: 23 处中文键 → monitor.logCenter.quickQuery.* 语义键
- types-v3.ts: 15 处中文键 → monitor.logCenter.quickQuery.* 语义键 (含 QUICK_TEMPLATES 3 引擎)
- TimeRangePicker.tsx: 13 处中文键 → monitor.timeRangePicker.* 语义键 + 修复 3 个复制粘贴 bug 键
  (i18n.t('开启后折叠行仅显示前 500 字预览') 应为 '选择时间范围'/'绝对时间'/'自定义时间范围')
  (i18n.t(' > 另存为 <ChevronDown className=') 应为 '相对时间')
- LogRBAC.tsx: 10 处中文键/硬编码 → monitor.logRbac.* 语义键 + 插值键 (scopeCount/deleteConfirm)
- zh/en 新增 68 键对齐 (7038→7098), tsc -b=0, npm build✓ (17.41s), ESLint=0/0/0, 17/17 测试通过
This commit is contained in:
attacker committed 2026-07-11 15:15:47 +08:00
1 parent fba5fc46b6
commit a8f6fd190b
6 files changed
+205 -81

No files matched your search

@@ -22,7 +22,7 @@ import i18n from '@/i18n';
const ROLE_LABEL: Record<string, string> = { const ROLE_LABEL: Record<string, string> = {
viewer: i18n.t('pipeline.viewer'), viewer: i18n.t('pipeline.viewer'),
analyst: i18n.t('分析者'), analyst: i18n.t('monitor.logRbac.analyst'),
}; };
const ENGINE_LABEL: Record<string, string> = { const ENGINE_LABEL: Record<string, string> = {
@@ -108,7 +108,7 @@ export default function LogRBAC() {
const handleCreate = async () => { const handleCreate = async () => {
if (!newName.trim() || !newCode.trim() || !newPattern.trim()) { if (!newName.trim() || !newCode.trim() || !newPattern.trim()) {
toast.error(i18n.t('请填写名称、标识与正则')); return; toast.error(i18n.t('monitor.logRbac.fillRequired')); return;
} }
setSaving(true); setSaving(true);
try { try {
@@ -147,8 +147,8 @@ export default function LogRBAC() {
const handleDelete = async (role: LogScopeRole) => { const handleDelete = async (role: LogScopeRole) => {
const ok = await _confirm({ const ok = await _confirm({
title: i18n.t('删除权限'), title: i18n.t('monitor.logRbac.deletePermission'),
description: `确定删除「${role.name}」?将影响 ${matchCount[role.id]?.length || 0} 个示例范围`, description: i18n.t('monitor.logRbac.deleteConfirm', { name: role.name, count: matchCount[role.id]?.length || 0 }),
variant: 'destructive', variant: 'destructive',
}); });
if (!ok) return; if (!ok) return;
@@ -165,7 +165,7 @@ export default function LogRBAC() {
}; };
const handleAssign = async () => { const handleAssign = async () => {
if (!selectedUserId || !assignRoleId) { toast.error(i18n.t('请选择用户和权限')); return; } if (!selectedUserId || !assignRoleId) { toast.error(i18n.t('monitor.logRbac.selectUserAndPermission')); return; }
setAssignLoading(true); setAssignLoading(true);
try { try {
await logRbacApi.assignRole(Number(selectedUserId), { await logRbacApi.assignRole(Number(selectedUserId), {
@@ -189,8 +189,8 @@ export default function LogRBAC() {
const selectedUser = users.find((u) => String(u.id) === selectedUserId); const selectedUser = users.find((u) => String(u.id) === selectedUserId);
const pageTabs = [ const pageTabs = [
{ id: 'roles', label: i18n.t('权限策略'), badge: roles.length || undefined }, { id: 'roles', label: i18n.t('monitor.logRbac.permissionPolicy'), badge: roles.length || undefined },
{ id: 'users', label: i18n.t('用户授权') }, { id: 'users', label: i18n.t('monitor.logRbac.userAssignment') },
{ id: 'preview', label: i18n.t('pipeline.matchPreview') }, { id: 'preview', label: i18n.t('pipeline.matchPreview') },
]; ];
@@ -206,7 +206,7 @@ export default function LogRBAC() {
onTabChange={setActiveTab} onTabChange={setActiveTab}
actions={( actions={(
<Button className="dg-btn-primary" onClick={() => setCreateOpen(true)}> <Button className="dg-btn-primary" onClick={() => setCreateOpen(true)}>
<Plus className="w-4 h-4 mr-1" />新建权限 <Plus className="w-4 h-4 mr-1" />{i18n.t('monitor.logRbac.createPermission')}
</Button> </Button>
)} )}
contentClassName="space-y-4" contentClassName="space-y-4"
@@ -215,8 +215,8 @@ export default function LogRBAC() {
roles.length === 0 ? ( roles.length === 0 ? (
<PageEmpty <PageEmpty
icon={<Shield className="h-10 w-10" />} icon={<Shield className="h-10 w-10" />}
title={i18n.t('暂无日志权限策略')} title={i18n.t('monitor.logRbac.noPolicies')}
description={i18n.t('创建策略并分配给用户后,将按正则限制可查询的索引与 Label 范围')} description={i18n.t('monitor.logRbac.noPoliciesDesc')}
action={<Button onClick={() => setCreateOpen(true)}><Plus className="h-4 w-4 mr-1" /> {i18n.t('monitor.logrbactsx.s6058845c')} </Button>} action={<Button onClick={() => setCreateOpen(true)}><Plus className="h-4 w-4 mr-1" /> {i18n.t('monitor.logrbactsx.s6058845c')} </Button>}
/> />
) : ( ) : (
@@ -243,7 +243,7 @@ export default function LogRBAC() {
className="text-[10px] cursor-pointer border-[var(--dg-border)]" className="text-[10px] cursor-pointer border-[var(--dg-border)]"
onClick={() => { setActiveTab('preview'); setPreviewRoleId(role.id); }} onClick={() => { setActiveTab('preview'); setPreviewRoleId(role.id); }}
> >
{matchCount[role.id]?.length || 0} 个范围 {i18n.t('monitor.logRbac.scopeCount', { count: matchCount[role.id]?.length || 0 })}
</Badge> </Badge>
</div> </div>
<p className="text-xs text-[var(--dg-text-muted)] mt-1 font-mono truncate"> <p className="text-xs text-[var(--dg-text-muted)] mt-1 font-mono truncate">
@@ -259,7 +259,7 @@ export default function LogRBAC() {
<SelectTrigger className="h-7 w-20 text-xs"><SelectValue /></SelectTrigger> <SelectTrigger className="h-7 w-20 text-xs"><SelectValue /></SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="viewer"> {i18n.t('pipeline.viewer')} </SelectItem> <SelectItem value="viewer"> {i18n.t('pipeline.viewer')} </SelectItem>
<SelectItem value="analyst"> {i18n.t('分析者')} </SelectItem> <SelectItem value="analyst"> {i18n.t('monitor.logRbac.analyst')} </SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<Button size="icon" variant="ghost" className="h-7 w-7" onClick={() => handleUpdate(role.id)}><Check className="w-3.5 h-3.5" /></Button> <Button size="icon" variant="ghost" className="h-7 w-7" onClick={() => handleUpdate(role.id)}><Check className="w-3.5 h-3.5" /></Button>
@@ -285,7 +285,7 @@ export default function LogRBAC() {
<Card className="dg-card"> <Card className="dg-card">
<CardContent className="p-4 space-y-4"> <CardContent className="p-4 space-y-4">
<p className="text-xs text-[var(--dg-text-muted)]"> <p className="text-xs text-[var(--dg-text-muted)]">
用户未分配任何策略时不拦截(向后兼容);分配后仅允许命中策略的查询。 {i18n.t('monitor.logRbac.userPolicyHint')}
</p> </p>
<div className="flex flex-wrap gap-3 items-end"> <div className="flex flex-wrap gap-3 items-end">
<div className="space-y-1 flex-1 min-w-[200px]"> <div className="space-y-1 flex-1 min-w-[200px]">
@@ -302,7 +302,7 @@ export default function LogRBAC() {
</Select> </Select>
</div> </div>
<div className="space-y-1 w-48"> <div className="space-y-1 w-48">
<Label className="text-xs"> {i18n.t('权限策略')} </Label> <Label className="text-xs"> {i18n.t('monitor.logRbac.permissionPolicy')} </Label>
<Select value={assignRoleId} onValueChange={setAssignRoleId}> <Select value={assignRoleId} onValueChange={setAssignRoleId}>
<SelectTrigger className="h-8 text-sm dg-input"><SelectValue placeholder={i18n.t('kubernetes.hpa.selectPolicy')} /></SelectTrigger> <SelectTrigger className="h-8 text-sm dg-input"><SelectValue placeholder={i18n.t('kubernetes.hpa.selectPolicy')} /></SelectTrigger>
<SelectContent> <SelectContent>
@@ -314,7 +314,7 @@ export default function LogRBAC() {
</div> </div>
<Button className="h-8" onClick={handleAssign} disabled={assignLoading || !selectedUserId || !assignRoleId}> <Button className="h-8" onClick={handleAssign} disabled={assignLoading || !selectedUserId || !assignRoleId}>
{assignLoading ? <Loader2 className="w-3.5 h-3.5 mr-1 animate-spin" /> : <UserPlus className="w-3.5 h-3.5 mr-1" />} {assignLoading ? <Loader2 className="w-3.5 h-3.5 mr-1 animate-spin" /> : <UserPlus className="w-3.5 h-3.5 mr-1" />}
分配 {i18n.t('monitor.logRbac.assign')}
</Button> </Button>
</div> </div>
{selectedUser ? ( {selectedUser ? (
@@ -351,7 +351,7 @@ export default function LogRBAC() {
<CardContent className="p-4 space-y-3"> <CardContent className="p-4 space-y-3">
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
<Button variant={previewRoleId === null ? 'secondary' : 'ghost'} size="sm" onClick={() => setPreviewRoleId(null)}> <Button variant={previewRoleId === null ? 'secondary' : 'ghost'} size="sm" onClick={() => setPreviewRoleId(null)}>
全部 ({SAMPLE_SCOPES.length}) {i18n.t('monitor.logRbac.all')} ({SAMPLE_SCOPES.length})
</Button> </Button>
{roles.map((r) => ( {roles.map((r) => (
<Button <Button
@@ -4,36 +4,36 @@ import i18n from '@/i18n';
const ALL_TEMPLATES: Record<'loki' | 'elasticsearch', { label: string; query: string }[]> = { const ALL_TEMPLATES: Record<'loki' | 'elasticsearch', { label: string; query: string }[]> = {
loki: [ loki: [
{ label: i18n.t('Nginx 错误'), query: '{job="nginx"} |= "error"' }, { label: i18n.t('monitor.logCenter.quickQuery.nginxError'), query: '{job="nginx"} |= "error"' },
{ label: i18n.t('JSON 过滤'), query: '{namespace="prod"} | json | level="warn"' }, { label: i18n.t('monitor.logCenter.quickQuery.jsonFilter'), query: '{namespace="prod"} | json | level="warn"' },
{ label: i18n.t('超时搜索'), query: '{app="api"} |= "timeout"' }, { label: i18n.t('monitor.logCenter.quickQuery.timeoutSearch'), query: '{app="api"} |= "timeout"' },
{ label: i18n.t('级别统计'), query: 'sum by(level) (count_over_time({job="app"}[5m]))' }, { label: i18n.t('monitor.logCenter.quickQuery.levelStats'), query: 'sum by(level) (count_over_time({job="app"}[5m]))' },
{ label: i18n.t('正则多级别'), query: '{container="frontend"} |~ "ERROR|WARN|FATAL"' }, { label: i18n.t('monitor.logCenter.quickQuery.regexMultiLevel'), query: '{container="frontend"} |~ "ERROR|WARN|FATAL"' },
{ label: i18n.t('慢请求>1s'), query: '{job="api"} | json | response_time > 1000' }, { label: i18n.t('monitor.logCenter.quickQuery.slowRequest1s'), query: '{job="api"} | json | response_time > 1000' },
{ label: i18n.t('错误率统计'), query: 'sum by(level) (count_over_time({app="cex-admin-api"} |= "error" [5m]))' }, { label: i18n.t('monitor.logCenter.quickQuery.errorRateStats'), query: 'sum by(level) (count_over_time({app="cex-admin-api"} |= "error" [5m]))' },
{ label: i18n.t('追踪ID查询'), query: '{app="cex-admin-api"} |= "trace_id" | json' }, { label: i18n.t('monitor.logCenter.quickQuery.traceIdQuery'), query: '{app="cex-admin-api"} |= "trace_id" | json' },
{ label: i18n.t('错误率统计'), query: 'sum by(service_name) (count_over_time({namespace="astralx"}[5m]))' }, { label: i18n.t('monitor.logCenter.quickQuery.errorRateStats'), query: 'sum by(service_name) (count_over_time({namespace="astralx"}[5m]))' },
{ label: i18n.t('高延迟筛选'), query: '{app="cex-admin-api"} | json | response_time > 500' }, { label: i18n.t('monitor.logCenter.quickQuery.highLatencyFilter'), query: '{app="cex-admin-api"} | json | response_time > 500' },
{ label: i18n.t('异常堆栈'), query: '{app="cex-admin-api"} |= "Exception"' }, { label: i18n.t('monitor.logCenter.quickQuery.exceptionStack'), query: '{app="cex-admin-api"} |= "Exception"' },
{ label: i18n.t('monitor.logTrend'), query: 'sum(count_over_time({app="cex-admin-api"}[1h]))' }, { label: i18n.t('monitor.logCenter.quickQuery.errorRateStats'), query: 'sum(count_over_time({app="cex-admin-api"}[1h]))' },
{ label: i18n.t('正则提取'), query: '{app="cex-admin-api"} | regexp "(?P<method>[A-Za-z0-9_]+) (?P<path>[^ ]+)"' }, { label: i18n.t('monitor.logCenter.quickQuery.regexExtract'), query: '{app="cex-admin-api"} | regexp "(?P<method>[A-Za-z0-9_]+) (?P<path>[^ ]+)"' },
{ label: i18n.t('格式输出'), query: '{app="cex-admin-api"} | json | line_format "{{.level}} {{.message}}"' }, { label: i18n.t('monitor.logCenter.quickQuery.formatOutput'), query: '{app="cex-admin-api"} | json | line_format "{{.level}} {{.message}}"' },
], ],
elasticsearch: [ elasticsearch: [
{ label: 'API 5xx', query: 'status:500 AND path:"/api/*"' }, { label: 'API 5xx', query: 'status:500 AND path:"/api/*"' },
{ label: i18n.t('错误超时'), query: '(level:error OR log.level:error) AND message:*timeout*' }, { label: i18n.t('monitor.logCenter.quickQuery.esErrorTimeout'), query: '(level:error OR log.level:error) AND message:*timeout*' },
{ label: i18n.t('排除调试'), query: 'host.name:server-* AND NOT level:debug' }, { label: i18n.t('monitor.logCenter.quickQuery.excludeDebug'), query: 'host.name:server-* AND NOT level:debug' },
{ label: i18n.t('慢请求'), query: 'response_time:>1000 AND method:POST' }, { label: i18n.t('monitor.logCenter.quickQuery.slowRequest'), query: 'response_time:>1000 AND method:POST' },
{ label: i18n.t('严重错误'), query: '@timestamp:[now-1h TO now] AND (level:critical OR log.level:critical)' }, { label: i18n.t('monitor.logCenter.quickQuery.criticalError'), query: '@timestamp:[now-1h TO now] AND (level:critical OR log.level:critical)' },
{ label: i18n.t('严重错误'), query: 'service:nginx AND (level:(error OR fatal) OR log.level:(error OR fatal))' }, { label: i18n.t('monitor.logCenter.quickQuery.criticalError'), query: 'service:nginx AND (level:(error OR fatal) OR log.level:(error OR fatal))' },
{ label: i18n.t('组合查询'), query: 'app:cex-admin-api AND (level:(error OR warn) OR log.level:(error OR warn))' }, { label: i18n.t('monitor.logCenter.quickQuery.combinedQuery'), query: 'app:cex-admin-api AND (level:(error OR warn) OR log.level:(error OR warn))' },
{ label: i18n.t('模糊匹配'), query: 'message:*timeout* AND app:cex-admin-api' }, { label: i18n.t('monitor.logCenter.quickQuery.fuzzyMatch'), query: 'message:*timeout* AND app:cex-admin-api' },
{ label: i18n.t('范围查询'), query: 'response_time:[1000 TO *] AND method:POST' }, { label: i18n.t('monitor.logCenter.quickQuery.rangeQuery'), query: 'response_time:[1000 TO *] AND method:POST' },
{ label: i18n.t('排除路径'), query: 'app:cex-admin-api AND NOT path:/health' }, { label: i18n.t('monitor.logCenter.quickQuery.excludePath'), query: 'app:cex-admin-api AND NOT path:/health' },
{ label: i18n.t('时间过滤'), query: '@timestamp:[now-1h TO now] AND level:error' }, { label: i18n.t('monitor.logCenter.quickQuery.timeFilter'), query: '@timestamp:[now-1h TO now] AND level:error' },
{ label: i18n.t('IP段过滤'), query: 'client_ip:172.36.* AND app:cex-admin-api' }, { label: i18n.t('monitor.logCenter.quickQuery.ipSegmentFilter'), query: 'client_ip:172.36.* AND app:cex-admin-api' },
{ label: i18n.t('多级别筛选'), query: 'app:cex-admin-api AND (level:(error OR warn OR info) OR log.level:(error OR warn OR info))' }, { label: i18n.t('monitor.logCenter.quickQuery.multiLevelFilter'), query: 'app:cex-admin-api AND (level:(error OR warn OR info) OR log.level:(error OR warn OR info))' },
{ label: i18n.t('嵌套字段'), query: 'labels.orgId:10001 AND app:cex-admin-api' }, { label: i18n.t('monitor.logCenter.quickQuery.nestedField'), query: 'labels.orgId:10001 AND app:cex-admin-api' },
], ],
}; };
@@ -46,7 +46,7 @@ export function QuickQueryBar({ engine, onSelect }: { engine: 'loki' | 'elastics
<div> <div>
<button onClick={() => setOpen(!open)} className="flex items-center gap-1 text-[11px] px-2 py-1 rounded transition-colors" style={{ color: 'var(--dg-blue)', background: open ? 'var(--dg-bg-tertiary)' : 'transparent' }}> <button onClick={() => setOpen(!open)} className="flex items-center gap-1 text-[11px] px-2 py-1 rounded transition-colors" style={{ color: 'var(--dg-blue)', background: open ? 'var(--dg-bg-tertiary)' : 'transparent' }}>
<span className="w-1.5 h-1.5 rounded-full" style={{ background: 'var(--dg-blue)' }} /> <span className="w-1.5 h-1.5 rounded-full" style={{ background: 'var(--dg-blue)' }} />
{labels[engine]} 快捷查询模板 {labels[engine]} {i18n.t('monitor.logCenter.quickQuery.quickTemplates')}
<ChevronDown className="h-3 w-3" style={{ transform: open ? 'rotate(0deg)' : 'rotate(-90deg)', transition: 'transform 0.2s' }} /> <ChevronDown className="h-3 w-3" style={{ transform: open ? 'rotate(0deg)' : 'rotate(-90deg)', transition: 'transform 0.2s' }} />
</button> </button>
{open && ( {open && (
@@ -6,27 +6,27 @@ import i18n from '@/i18n';
// 相对时间预设:最近N... 每次点击时计算 start = now - N // Logan.Li // 相对时间预设:最近N... 每次点击时计算 start = now - N // Logan.Li
const RELATIVE_PRESETS = [ const RELATIVE_PRESETS = [
{ label: i18n.t('最近1分钟'), value: '1m' }, { label: i18n.t('monitor.timeRangePicker.range1min'), value: '1m' },
{ label: i18n.t('monitor.logPanel.last5min'), value: '5m' }, { label: i18n.t('monitor.logPanel.last5min'), value: '5m' },
{ label: i18n.t('最近15分钟'), value: '15m' }, { label: i18n.t('monitor.timeRangePicker.range15min'), value: '15m' },
{ label: i18n.t('最近1小时'), value: '1h' }, { label: i18n.t('monitor.timeRangePicker.range1hour'), value: '1h' },
{ label: i18n.t('最近4小时'), value: '4h' }, { label: i18n.t('monitor.timeRangePicker.range4hours'), value: '4h' },
{ label: i18n.t('最近1天'), value: '24h' }, { label: i18n.t('monitor.timeRangePicker.range1day'), value: '24h' },
{ label: i18n.t('最近1周'), value: '7d' }, { label: i18n.t('monitor.timeRangePicker.range1week'), value: '7d' },
{ label: i18n.t('最近30天'), value: '30d' }, { label: i18n.t('monitor.timeRangePicker.range30days'), value: '30d' },
]; ];
// 绝对时间预设:固定的自然日/周/月/季/年 // Logan.Li // 绝对时间预设:固定的自然日/周/月/季/年 // Logan.Li
const ABSOLUTE_PRESETS = [ const ABSOLUTE_PRESETS = [
{ label: i18n.t('audit.terminal.presetToday'), value: 'today' }, { label: i18n.t('audit.terminal.presetToday'), value: 'today' },
{ label: i18n.t('common.yesterday'), value: 'yesterday' }, { label: i18n.t('common.yesterday'), value: 'yesterday' },
{ label: i18n.t('前天'), value: 'day_before_yesterday' }, { label: i18n.t('monitor.timeRangePicker.dayBeforeYesterday'), value: 'day_before_yesterday' },
{ label: i18n.t('common.thisWeek'), value: 'this_week' }, { label: i18n.t('common.thisWeek'), value: 'this_week' },
{ label: i18n.t('common.lastWeek'), value: 'last_week' }, { label: i18n.t('common.lastWeek'), value: 'last_week' },
{ label: i18n.t('cloud.thisMonth'), value: 'this_month' }, { label: i18n.t('cloud.thisMonth'), value: 'this_month' },
{ label: i18n.t('common.lastMonth'), value: 'last_month' }, { label: i18n.t('common.lastMonth'), value: 'last_month' },
{ label: i18n.t('本季度'), value: 'this_quarter' }, { label: i18n.t('monitor.timeRangePicker.thisQuarter'), value: 'this_quarter' },
{ label: i18n.t('本年度'), value: 'this_year' }, { label: i18n.t('monitor.timeRangePicker.thisYear'), value: 'this_year' },
]; ];
function getPresetRange(value: string): { start: Date; end: Date; label: string } { function getPresetRange(value: string): { start: Date; end: Date; label: string } {
@@ -136,7 +136,7 @@ export function TimeRangePicker({ value, onChange }: Props) {
{/* 头部 */} {/* 头部 */}
<div className="flex items-center gap-2 mb-3 pb-3" style={{ borderBottom: '1px solid var(--dg-border)' }}> <div className="flex items-center gap-2 mb-3 pb-3" style={{ borderBottom: '1px solid var(--dg-border)' }}>
<Calendar className="h-4 w-4" style={{ color: 'var(--dg-blue)' }} /> <Calendar className="h-4 w-4" style={{ color: 'var(--dg-blue)' }} />
<span className="text-sm font-medium" style={{ color: 'var(--dg-text-primary)' }}> {i18n.t('开启后折叠行仅显示前 500 字预览')} </span> <span className="text-sm font-medium" style={{ color: 'var(--dg-text-primary)' }}> {i18n.t('monitor.timeRangePicker.selectTimeRange')} </span>
<button <button
onClick={openCustom} onClick={openCustom}
className="text-xs ml-2 px-2 py-0.5 rounded transition-colors" className="text-xs ml-2 px-2 py-0.5 rounded transition-colors"
@@ -152,7 +152,7 @@ export function TimeRangePicker({ value, onChange }: Props) {
{mode === 'preset' ? ( {mode === 'preset' ? (
<> <>
{/* 相对时间 — 最近... */} {/* 相对时间 — 最近... */}
<p className="text-[10px] mb-2 font-medium uppercase tracking-wide" style={{ color: '#22c55e' }}> {i18n.t(' /> 另存为 <ChevronDown className=')} </p> <p className="text-[10px] mb-2 font-medium uppercase tracking-wide" style={{ color: '#22c55e' }}> {i18n.t('monitor.timeRangePicker.relativeTime')} </p>
<div className="grid grid-cols-4 gap-1.5 mb-3"> <div className="grid grid-cols-4 gap-1.5 mb-3">
{RELATIVE_PRESETS.map(p => ( {RELATIVE_PRESETS.map(p => (
<button <button
@@ -171,7 +171,7 @@ export function TimeRangePicker({ value, onChange }: Props) {
</div> </div>
{/* 绝对时间 — 固定日历范围 */} {/* 绝对时间 — 固定日历范围 */}
<p className="text-[10px] mb-2 font-medium uppercase tracking-wide" style={{ color: 'var(--dg-blue)' }}> {i18n.t('开启后折叠行仅显示前 500 字预览')} </p> <p className="text-[10px] mb-2 font-medium uppercase tracking-wide" style={{ color: 'var(--dg-blue)' }}> {i18n.t('monitor.timeRangePicker.absoluteTime')} </p>
<div className="grid grid-cols-5 gap-1.5"> <div className="grid grid-cols-5 gap-1.5">
{ABSOLUTE_PRESETS.map(p => ( {ABSOLUTE_PRESETS.map(p => (
<button <button
@@ -192,7 +192,7 @@ export function TimeRangePicker({ value, onChange }: Props) {
) : ( ) : (
/* 自定义日历模式 */ /* 自定义日历模式 */
<div className="space-y-3"> <div className="space-y-3">
<p className="text-[10px] font-medium uppercase" style={{ color: 'var(--dg-blue)' }}> {i18n.t('开启后折叠行仅显示前 500 字预览')} </p> <p className="text-[10px] font-medium uppercase" style={{ color: 'var(--dg-blue)' }}> {i18n.t('monitor.timeRangePicker.customTimeRange')} </p>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div> <div>
<label className="text-[10px] block mb-1" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('system.terminalStartTime')} </label> <label className="text-[10px] block mb-1" style={{ color: 'var(--dg-text-muted)' }}> {i18n.t('system.terminalStartTime')} </label>
@@ -134,7 +134,7 @@ export const ENGINE_META: Record<LogEngine, { name: string; color: string; query
color: '#f97316', color: '#f97316',
queryPlaceholder: 'keyword | SELECT * WHERE level = "info"', queryPlaceholder: 'keyword | SELECT * WHERE level = "info"',
statPlaceholder: '* | SELECT level, COUNT(*) as cnt GROUP BY level', statPlaceholder: '* | SELECT level, COUNT(*) as cnt GROUP BY level',
externalLink: i18n.t('SLS控制台'), externalLink: i18n.t('monitor.logCenter.quickQuery.slsConsole'),
}, },
}; };
@@ -250,27 +250,27 @@ export const savedQueryStorage = {
// 快捷查询模板 // Logan.Li // 快捷查询模板 // Logan.Li
export const QUICK_TEMPLATES: Record<LogEngine, { label: string; query: string }[]> = { export const QUICK_TEMPLATES: Record<LogEngine, { label: string; query: string }[]> = {
loki: [ loki: [
{ label: i18n.t('Nginx 错误日志'), query: '{job="nginx"} |= "error"' }, { label: i18n.t('monitor.logCenter.quickQuery.nginxErrorLog'), query: '{job="nginx"} |= "error"' },
{ label: i18n.t('JSON 解析过滤'), query: '{namespace="prod"} | json | level="warn"' }, { label: i18n.t('monitor.logCenter.quickQuery.jsonParseFilter'), query: '{namespace="prod"} | json | level="warn"' },
{ label: i18n.t('超时搜索'), query: '{app="api"} |= "timeout"' }, { label: i18n.t('monitor.logCenter.quickQuery.timeoutSearch'), query: '{app="api"} |= "timeout"' },
{ label: i18n.t('按级别统计'), query: 'sum by (level) (count_over_time({job="app"}[5m]))' }, { label: i18n.t('monitor.logCenter.quickQuery.statsByLevel'), query: 'sum by (level) (count_over_time({job="app"}[5m]))' },
{ label: i18n.t('正则匹配多级别'), query: '{container="frontend"} |~ "ERROR|WARN|FATAL"' }, { label: i18n.t('monitor.logCenter.quickQuery.regexMultiLevelMatch'), query: '{container="frontend"} |~ "ERROR|WARN|FATAL"' },
{ label: i18n.t('慢请求 >1s'), query: '{job="api"} | json | response_time > 1000' }, { label: i18n.t('monitor.logCenter.quickQuery.slowRequestGt1s'), query: '{job="api"} | json | response_time > 1000' },
], ],
elasticsearch: [ elasticsearch: [
{ label: i18n.t('API 5xx 错误'), query: 'status:500 AND path:"/api/*"' }, { label: i18n.t('monitor.logCenter.quickQuery.api5xxError'), query: 'status:500 AND path:"/api/*"' },
{ label: i18n.t('错误超时日志'), query: '(level:error OR log.level:error) AND message:*timeout*' }, { label: i18n.t('monitor.logCenter.quickQuery.errorTimeoutLog'), query: '(level:error OR log.level:error) AND message:*timeout*' },
{ label: i18n.t('排除调试日志'), query: 'host.name:server-* AND NOT level:debug AND NOT log.level:debug' }, { label: i18n.t('monitor.logCenter.quickQuery.excludeDebugLog'), query: 'host.name:server-* AND NOT level:debug AND NOT log.level:debug' },
{ label: i18n.t('慢请求检测'), query: 'response_time:>1000 AND method:POST' }, { label: i18n.t('monitor.logCenter.quickQuery.slowRequestDetect'), query: 'response_time:>1000 AND method:POST' },
{ label: i18n.t('最近1小时严重错误'), query: '@timestamp:[now-1h TO now] AND (level:critical OR log.level:critical)' }, { label: i18n.t('monitor.logCenter.quickQuery.lastHourCritical'), query: '@timestamp:[now-1h TO now] AND (level:critical OR log.level:critical)' },
{ label: i18n.t('最近1小时严重错误'), query: 'service:nginx AND (level:(error OR fatal) OR log.level:(error OR fatal))' }, { label: i18n.t('monitor.logCenter.quickQuery.lastHourCritical'), query: 'service:nginx AND (level:(error OR fatal) OR log.level:(error OR fatal))' },
], ],
sls: [ sls: [
{ label: i18n.t('错误日志筛选'), query: "* | select * where level = 'error'" }, { label: i18n.t('monitor.logCenter.quickQuery.errorLogFilter'), query: "* | select * where level = 'error'" },
{ label: i18n.t('按级别统计'), query: "* | select count(*) as cnt, level group by level" }, { label: i18n.t('monitor.logCenter.quickQuery.statsByLevel'), query: "* | select count(*) as cnt, level group by level" },
{ label: i18n.t('时间聚合'), query: "* | select date_trunc('minute', __time__) as t, count(*) group by t order by t" }, { label: i18n.t('monitor.logCenter.quickQuery.timeAggregate'), query: "* | select date_trunc('minute', __time__) as t, count(*) group by t order by t" },
{ label: i18n.t('状态码分布'), query: "* | select method, count(*) as cnt, avg(response_time) group by method order by cnt desc" }, { label: i18n.t('monitor.logCenter.quickQuery.statusCodeDist'), query: "* | select method, count(*) as cnt, avg(response_time) group by method order by cnt desc" },
{ label: i18n.t('慢请求分析'), query: "* | select * where response_time > 1000 order by response_time desc limit 100" }, { label: i18n.t('monitor.logCenter.quickQuery.slowRequestAnalysis'), query: "* | select * where response_time > 1000 order by response_time desc limit 100" },
{ label: i18n.t('状态码分布'), query: "* | select status_code, count(*) as cnt, count(*) * 100.0 / sum(count(*)) over() as pct group by status_code" }, { label: i18n.t('monitor.logCenter.quickQuery.statusCodeDist'), query: "* | select status_code, count(*) as cnt, count(*) * 100.0 / sum(count(*)) over() as pct group by status_code" },
], ],
}; };
+64 -2
View File
@@ -3263,7 +3263,49 @@
"alerts": "Log Alerts", "alerts": "Log Alerts",
"alertsDesc": "Log alert policies based on query conditions", "alertsDesc": "Log alert policies based on query conditions",
"rbac": "Log Permissions", "rbac": "Log Permissions",
"rbacDesc": "Restrict query scope by ES index / Loki Label regex" "rbacDesc": "Restrict query scope by ES index / Loki Label regex",
"quickQuery": {
"quickTemplates": "Quick Query Templates",
"slsConsole": "SLS Console",
"nginxError": "Nginx Errors",
"jsonFilter": "JSON Filter",
"timeoutSearch": "Timeout Search",
"levelStats": "Level Stats",
"regexMultiLevel": "Regex Multi-Level",
"slowRequest1s": "Slow Requests >1s",
"errorRateStats": "Error Rate Stats",
"traceIdQuery": "Trace ID Query",
"highLatencyFilter": "High Latency Filter",
"exceptionStack": "Exception Stack",
"regexExtract": "Regex Extract",
"formatOutput": "Format Output",
"esErrorTimeout": "Error & Timeout",
"excludeDebug": "Exclude Debug",
"slowRequest": "Slow Requests",
"criticalError": "Critical Errors",
"combinedQuery": "Combined Query",
"fuzzyMatch": "Fuzzy Match",
"rangeQuery": "Range Query",
"excludePath": "Exclude Paths",
"timeFilter": "Time Filter",
"ipSegmentFilter": "IP Segment Filter",
"multiLevelFilter": "Multi-Level Filter",
"nestedField": "Nested Field",
"nginxErrorLog": "Nginx Error Logs",
"jsonParseFilter": "JSON Parse Filter",
"statsByLevel": "Stats by Level",
"regexMultiLevelMatch": "Regex Multi-Level Match",
"slowRequestGt1s": "Slow Requests >1s",
"api5xxError": "API 5xx Errors",
"errorTimeoutLog": "Error Timeout Logs",
"excludeDebugLog": "Exclude Debug Logs",
"slowRequestDetect": "Slow Request Detection",
"lastHourCritical": "Last Hour Critical Errors",
"errorLogFilter": "Error Log Filter",
"timeAggregate": "Time Aggregation",
"statusCodeDist": "Status Code Distribution",
"slowRequestAnalysis": "Slow Request Analysis"
}
}, },
"alertEvents": { "alertEvents": {
"searchPlaceholder": "Search alert name or target...", "searchPlaceholder": "Search alert name or target...",
@@ -4388,7 +4430,11 @@
"thisMonth": "This month", "thisMonth": "This month",
"lastMonth": "Last month", "lastMonth": "Last month",
"thisQuarter": "This quarter", "thisQuarter": "This quarter",
"thisYear": "This year" "thisYear": "This year",
"selectTimeRange": "Select Time Range",
"relativeTime": "Relative Time",
"absoluteTime": "Absolute Time",
"customTimeRange": "Custom Time Range"
}, },
"channel": { "channel": {
"dingtalk": "DingTalk", "dingtalk": "DingTalk",
@@ -4430,6 +4476,22 @@
"perPage": "/page", "perPage": "/page",
"prevPage": "Prev", "prevPage": "Prev",
"nextPage": "Next" "nextPage": "Next"
},
"logRbac": {
"analyst": "Analyst",
"fillRequired": "Please fill in name, code and regex pattern",
"deletePermission": "Delete Permission",
"deleteConfirm": "Delete \"{{name}}\"? This will affect {{count}} sample scopes",
"selectUserAndPermission": "Please select user and permission",
"permissionPolicy": "Permission Policy",
"userAssignment": "User Assignment",
"createPermission": "New Permission",
"noPolicies": "No log permission policies",
"noPoliciesDesc": "After creating and assigning policies to users, queryable indices and labels will be restricted by regex patterns",
"scopeCount": "{{count}} scopes",
"userPolicyHint": "Users without any policy are not blocked (backward compatible); once assigned, only queries matching policies are allowed.",
"assign": "Assign",
"all": "All"
} }
}, },
"task": { "task": {
+64 -2
View File
@@ -3263,7 +3263,49 @@
"alerts": "日志告警", "alerts": "日志告警",
"alertsDesc": "基于查询条件的日志告警策略", "alertsDesc": "基于查询条件的日志告警策略",
"rbac": "日志权限", "rbac": "日志权限",
"rbacDesc": "按 ES 索引 / Loki Label 正则限制查询范围" "rbacDesc": "按 ES 索引 / Loki Label 正则限制查询范围",
"quickQuery": {
"quickTemplates": "快捷查询模板",
"slsConsole": "SLS控制台",
"nginxError": "Nginx 错误",
"jsonFilter": "JSON 过滤",
"timeoutSearch": "超时搜索",
"levelStats": "级别统计",
"regexMultiLevel": "正则多级别",
"slowRequest1s": "慢请求>1s",
"errorRateStats": "错误率统计",
"traceIdQuery": "追踪ID查询",
"highLatencyFilter": "高延迟筛选",
"exceptionStack": "异常堆栈",
"regexExtract": "正则提取",
"formatOutput": "格式输出",
"esErrorTimeout": "错误超时",
"excludeDebug": "排除调试",
"slowRequest": "慢请求",
"criticalError": "严重错误",
"combinedQuery": "组合查询",
"fuzzyMatch": "模糊匹配",
"rangeQuery": "范围查询",
"excludePath": "排除路径",
"timeFilter": "时间过滤",
"ipSegmentFilter": "IP段过滤",
"multiLevelFilter": "多级别筛选",
"nestedField": "嵌套字段",
"nginxErrorLog": "Nginx 错误日志",
"jsonParseFilter": "JSON 解析过滤",
"statsByLevel": "按级别统计",
"regexMultiLevelMatch": "正则匹配多级别",
"slowRequestGt1s": "慢请求 >1s",
"api5xxError": "API 5xx 错误",
"errorTimeoutLog": "错误超时日志",
"excludeDebugLog": "排除调试日志",
"slowRequestDetect": "慢请求检测",
"lastHourCritical": "最近1小时严重错误",
"errorLogFilter": "错误日志筛选",
"timeAggregate": "时间聚合",
"statusCodeDist": "状态码分布",
"slowRequestAnalysis": "慢请求分析"
}
}, },
"alertEvents": { "alertEvents": {
"searchPlaceholder": "搜索告警名称或目标...", "searchPlaceholder": "搜索告警名称或目标...",
@@ -4388,7 +4430,11 @@
"thisMonth": "本月", "thisMonth": "本月",
"lastMonth": "上月", "lastMonth": "上月",
"thisQuarter": "本季度", "thisQuarter": "本季度",
"thisYear": "本年度" "thisYear": "本年度",
"selectTimeRange": "选择时间范围",
"relativeTime": "相对时间",
"absoluteTime": "绝对时间",
"customTimeRange": "自定义时间范围"
}, },
"channel": { "channel": {
"dingtalk": "钉钉", "dingtalk": "钉钉",
@@ -4430,6 +4476,22 @@
"perPage": "/页", "perPage": "/页",
"prevPage": "上一页", "prevPage": "上一页",
"nextPage": "下一页" "nextPage": "下一页"
},
"logRbac": {
"analyst": "分析者",
"fillRequired": "请填写名称、标识与正则",
"deletePermission": "删除权限",
"deleteConfirm": "确定删除「{{name}}」?将影响 {{count}} 个示例范围",
"selectUserAndPermission": "请选择用户和权限",
"permissionPolicy": "权限策略",
"userAssignment": "用户授权",
"createPermission": "新建权限",
"noPolicies": "暂无日志权限策略",
"noPoliciesDesc": "创建策略并分配给用户后,将按正则限制可查询的索引与 Label 范围",
"scopeCount": "{{count}} 个范围",
"userPolicyHint": "用户未分配任何策略时不拦截(向后兼容);分配后仅允许命中策略的查询。",
"assign": "分配",
"all": "全部"
} }
}, },
"task": { "task": {