mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-02 06:56:36 +08:00
feat(access-logs): 上报并展示边缘缓存状态 cache_status
OpenResty 日志输出 $upstream_cache_status;Agent/协议/ClickHouse 贯通入库。 明细列表与详情按 HIT/MISS 等推导命中、回源、未缓存三态标签。
This commit is contained in:
@@ -46,9 +46,11 @@ import { formatBytes, formatCompactNumber } from '@/lib/utils/metrics';
|
||||
|
||||
import { buildIPGroupPayloadFromGroup } from '../../waf/components/helpers';
|
||||
import {
|
||||
cacheOutcomeLabel,
|
||||
formatOverviewRangeHint,
|
||||
formatOverviewTrendLabel,
|
||||
OVERVIEW_RANGE_OPTIONS,
|
||||
resolveCacheOutcome,
|
||||
type OverviewRangeHours,
|
||||
} from './access-log-utils';
|
||||
|
||||
@@ -463,6 +465,21 @@ export function AccessLogDetailDialog({
|
||||
</Badge>
|
||||
}
|
||||
/>
|
||||
<DetailField
|
||||
label='缓存'
|
||||
value={
|
||||
<div className='flex flex-wrap items-center gap-2'>
|
||||
<Badge variant='outline' className='text-[10px]'>
|
||||
{cacheOutcomeLabel(
|
||||
resolveCacheOutcome(activeItem.cache_status),
|
||||
)}
|
||||
</Badge>
|
||||
<span className='font-mono text-xs text-muted-foreground'>
|
||||
{activeItem.cache_status || '—'}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<DetailField
|
||||
label='路径'
|
||||
value={activeItem.path || '—'}
|
||||
|
||||
@@ -64,3 +64,34 @@ export function formatOverviewTrendLabel(value: string, hours: number) {
|
||||
}
|
||||
return `${month}/${day} ${hour}:00`;
|
||||
}
|
||||
|
||||
export type CacheOutcome = 'hit' | 'origin' | 'uncached';
|
||||
|
||||
export function resolveCacheOutcome(
|
||||
cacheStatus: string | undefined | null,
|
||||
): CacheOutcome {
|
||||
const status = (cacheStatus ?? '').trim().toUpperCase();
|
||||
if (
|
||||
status === 'HIT' ||
|
||||
status === 'STALE' ||
|
||||
status === 'REVALIDATED' ||
|
||||
status === 'UPDATING'
|
||||
) {
|
||||
return 'hit';
|
||||
}
|
||||
if (status === 'MISS' || status === 'EXPIRED') {
|
||||
return 'origin';
|
||||
}
|
||||
return 'uncached';
|
||||
}
|
||||
|
||||
export function cacheOutcomeLabel(outcome: CacheOutcome) {
|
||||
switch (outcome) {
|
||||
case 'hit':
|
||||
return '命中';
|
||||
case 'origin':
|
||||
return '回源';
|
||||
default:
|
||||
return '未缓存';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,25 @@ import type { AccessLogItem, AccessLogList } from '@/lib/services/openflare';
|
||||
import { formatDateTime } from '@/lib/utils';
|
||||
|
||||
import { AccessLogDetailDialog } from './access-log-detail-dialog';
|
||||
import { DETAIL_SORT_OPTIONS } from './access-log-utils';
|
||||
import {
|
||||
cacheOutcomeLabel,
|
||||
DETAIL_SORT_OPTIONS,
|
||||
resolveCacheOutcome,
|
||||
type CacheOutcome,
|
||||
} from './access-log-utils';
|
||||
|
||||
function cacheOutcomeVariant(
|
||||
outcome: CacheOutcome,
|
||||
): 'default' | 'secondary' | 'outline' | 'destructive' {
|
||||
switch (outcome) {
|
||||
case 'hit':
|
||||
return 'default';
|
||||
case 'origin':
|
||||
return 'secondary';
|
||||
default:
|
||||
return 'outline';
|
||||
}
|
||||
}
|
||||
|
||||
function PaginationBar({
|
||||
page,
|
||||
@@ -136,53 +154,66 @@ export function DetailTab({
|
||||
<TableHead className='text-xs'>IP</TableHead>
|
||||
<TableHead className='text-xs'>域名</TableHead>
|
||||
<TableHead className='text-xs'>路径</TableHead>
|
||||
<TableHead className='text-xs'>缓存</TableHead>
|
||||
<TableHead className='text-xs'>状态码</TableHead>
|
||||
<TableHead className='w-12 text-center text-xs' />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{(data?.items ?? []).map((item) => (
|
||||
<TableRow key={item.id} className='border-dashed'>
|
||||
<TableCell className='text-xs'>
|
||||
{formatDateTime(item.logged_at)}
|
||||
</TableCell>
|
||||
<TableCell className='text-xs'>
|
||||
{item.node_name || item.node_id}
|
||||
</TableCell>
|
||||
<TableCell className='text-xs font-mono'>
|
||||
{item.remote_addr}
|
||||
</TableCell>
|
||||
<TableCell className='text-xs'>{item.host}</TableCell>
|
||||
<TableCell className='text-xs max-w-48 truncate'>
|
||||
{item.path}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant='outline' className='text-[10px]'>
|
||||
{item.status_code}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className='text-center'>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant='ghost'
|
||||
size='icon'
|
||||
className='h-6 w-6 text-muted-foreground hover:text-foreground'
|
||||
onClick={() => {
|
||||
setSelected(item);
|
||||
setDetailOpen(true);
|
||||
}}
|
||||
>
|
||||
<Eye className='size-3' />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side='top' className='text-xs'>
|
||||
查看详情
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{(data?.items ?? []).map((item) => {
|
||||
const outcome = resolveCacheOutcome(item.cache_status);
|
||||
return (
|
||||
<TableRow key={item.id} className='border-dashed'>
|
||||
<TableCell className='text-xs'>
|
||||
{formatDateTime(item.logged_at)}
|
||||
</TableCell>
|
||||
<TableCell className='text-xs'>
|
||||
{item.node_name || item.node_id}
|
||||
</TableCell>
|
||||
<TableCell className='text-xs font-mono'>
|
||||
{item.remote_addr}
|
||||
</TableCell>
|
||||
<TableCell className='text-xs'>{item.host}</TableCell>
|
||||
<TableCell className='text-xs max-w-48 truncate'>
|
||||
{item.path}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant={cacheOutcomeVariant(outcome)}
|
||||
className='text-[10px]'
|
||||
title={item.cache_status || undefined}
|
||||
>
|
||||
{cacheOutcomeLabel(outcome)}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant='outline' className='text-[10px]'>
|
||||
{item.status_code}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className='text-center'>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant='ghost'
|
||||
size='icon'
|
||||
className='h-6 w-6 text-muted-foreground hover:text-foreground'
|
||||
onClick={() => {
|
||||
setSelected(item);
|
||||
setDetailOpen(true);
|
||||
}}
|
||||
>
|
||||
<Eye className='size-3' />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side='top' className='text-xs'>
|
||||
查看详情
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
|
||||
@@ -544,6 +544,7 @@ export interface AccessLogItem {
|
||||
host: string;
|
||||
path: string;
|
||||
user_agent: string;
|
||||
cache_status: string;
|
||||
status_code: number;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user