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:
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" },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
Reference in new issue
Block a user