Logs
API request logs view component for DeveloperApp
Logs
The Logs view provides a comprehensive interface for viewing, filtering, and analyzing API request logs. Debug issues, monitor API activity, and analyze request/response patterns with detailed logging and search capabilities.
Type Signature
interface LogsViewProps {
/** Page title */
title?: string
/** Page description */
description?: string
/** View layout mode */
layout?: 'single' | 'column' | 'canvas' | 'vibe'
/** Log entries */
logs?: Array<{
id: string
timestamp: string
method: string // HTTP method
path: string // Request path
status: number // HTTP status code
duration: number // Response time in ms
apiKey?: string // API key used
ip?: string // Client IP address
userAgent?: string // User agent
error?: string // Error message if failed
}>
/** Filter options */
filters?: {
method?: string[]
status?: number[]
dateRange?: {
from: string
to: string
}
search?: string
}
/** Show real-time logs */
realtime?: boolean
/** Enable log export */
allowExport?: boolean
/** Page-level action buttons */
actions?: Array<{
id: string
label: string
icon?: string
onClick?: () => void
variant?: 'default' | 'primary' | 'destructive'
}>
/** Child content */
children?: React.ReactNode
}Usage
<App name="Developer Portal">
<Shell>
<LogsView
title="API Logs"
description="View and analyze API request logs"
realtime={false}
allowExport={true}
/>
</Shell>
</App>Props
title
The page title displayed in the header. Defaults to "Logs".
<LogsView title="Request Logs">description
Optional page description or subtitle.
<LogsView
title="API Logs"
description="Monitor and debug API requests in real-time"
>layout
Layout mode for the view content. Defaults to 'single'.
// Single column layout (default)
<LogsView layout="single">
// Multi-column layout
<LogsView layout="column">logs
Array of API request log entries.
<LogsView
logs={[
{
id: 'log_1',
timestamp: '2024-12-19T08:15:23.456Z',
method: 'GET',
path: '/api/v1/users',
status: 200,
duration: 145,
apiKey: 'sk_live_••••4a3f',
ip: '192.168.1.1',
userAgent: 'curl/7.68.0'
},
{
id: 'log_2',
timestamp: '2024-12-19T08:15:18.234Z',
method: 'POST',
path: '/api/v1/users',
status: 500,
duration: 2340,
apiKey: 'sk_test_••••8b2e',
ip: '192.168.1.2',
error: 'Internal server error'
}
]}
>filters
Active filters for log entries.
<LogsView
filters={{
method: ['GET', 'POST'],
status: [200, 201],
dateRange: {
from: '2024-12-18T00:00:00Z',
to: '2024-12-19T23:59:59Z'
},
search: 'users'
}}
>realtime
Enable real-time log streaming. Defaults to false.
<LogsView realtime={true}>
{/* Logs update automatically as new requests arrive */}
</LogsView>allowExport
Enable log export functionality. Defaults to true.
<LogsView allowExport={true}>
{/* Shows export button in header */}
</LogsView>actions
Page-level action buttons displayed in the header.
<LogsView
actions={[
{
id: 'export',
label: 'Export',
icon: 'Download',
onClick: () => exportLogs()
},
{
id: 'clear',
label: 'Clear Filters',
onClick: () => clearFilters()
}
]}
>Log Display
Table View
Display logs in a table format:
<LogsView>
<Table>
<TableHeader>
<TableRow>
<TableHead>Time</TableHead>
<TableHead>Method</TableHead>
<TableHead>Path</TableHead>
<TableHead>Status</TableHead>
<TableHead>Duration</TableHead>
<TableHead>API Key</TableHead>
<TableHead>Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{logs.map(log => (
<TableRow
key={log.id}
className={log.status >= 400 ? 'bg-destructive/10' : ''}
>
<TableCell>
<time>{formatTime(log.timestamp)}</time>
</TableCell>
<TableCell>
<Badge variant={getMethodVariant(log.method)}>
{log.method}
</Badge>
</TableCell>
<TableCell>
<Code>{log.path}</Code>
</TableCell>
<TableCell>
<Badge variant={getStatusVariant(log.status)}>
{log.status}
</Badge>
</TableCell>
<TableCell>{log.duration}ms</TableCell>
<TableCell>{log.apiKey}</TableCell>
<TableCell>
<Button
size="sm"
variant="ghost"
onClick={() => viewDetails(log.id)}
>
Details
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</LogsView>List View
Display logs in a list format with more detail:
<LogsView>
<div className="space-y-2">
{logs.map(log => (
<Card
key={log.id}
className={log.status >= 400 ? 'border-destructive' : ''}
>
<CardContent className="py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<Badge variant={getMethodVariant(log.method)}>
{log.method}
</Badge>
<Code>{log.path}</Code>
<Badge variant={getStatusVariant(log.status)}>
{log.status}
</Badge>
<span className="text-sm text-muted-foreground">
{log.duration}ms
</span>
</div>
<div className="text-sm text-muted-foreground">
{formatTime(log.timestamp)}
</div>
</div>
{log.error && (
<Alert variant="destructive" className="mt-2">
<AlertDescription>{log.error}</AlertDescription>
</Alert>
)}
</CardContent>
</Card>
))}
</div>
</LogsView>Filtering and Search
Method Filter
Filter by HTTP method:
<LogsView>
<div className="flex gap-2 mb-4">
<Label>Method:</Label>
<CheckboxGroup
value={filters.method}
onValueChange={(methods) => setFilters({ ...filters, method: methods })}
options={[
{ value: 'GET', label: 'GET' },
{ value: 'POST', label: 'POST' },
{ value: 'PUT', label: 'PUT' },
{ value: 'PATCH', label: 'PATCH' },
{ value: 'DELETE', label: 'DELETE' }
]}
/>
</div>
</LogsView>Status Code Filter
Filter by response status:
<LogsView>
<Select
value={filters.status}
onValueChange={(status) => setFilters({ ...filters, status })}
>
<SelectTrigger>
<SelectValue placeholder="All statuses" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All statuses</SelectItem>
<SelectItem value="2xx">2xx Success</SelectItem>
<SelectItem value="4xx">4xx Client Error</SelectItem>
<SelectItem value="5xx">5xx Server Error</SelectItem>
</SelectContent>
</Select>
</LogsView>Date Range Filter
Filter by time period:
<LogsView>
<DateRangePicker
value={filters.dateRange}
onValueChange={(range) => setFilters({ ...filters, dateRange: range })}
/>
</LogsView>Text Search
Search log entries:
<LogsView>
<Input
type="search"
placeholder="Search logs..."
value={filters.search}
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
icon="Search"
/>
</LogsView>Log Details
View detailed information about a specific request:
<LogDetailsDialog log={selectedLog}>
<Tabs defaultValue="overview">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="request">Request</TabsTrigger>
<TabsTrigger value="response">Response</TabsTrigger>
<TabsTrigger value="headers">Headers</TabsTrigger>
</TabsList>
<TabsContent value="overview">
<dl className="grid grid-cols-2 gap-4">
<div>
<dt className="text-sm text-muted-foreground">Timestamp</dt>
<dd>{formatDateTime(log.timestamp)}</dd>
</div>
<div>
<dt className="text-sm text-muted-foreground">Duration</dt>
<dd>{log.duration}ms</dd>
</div>
<div>
<dt className="text-sm text-muted-foreground">Method</dt>
<dd><Badge>{log.method}</Badge></dd>
</div>
<div>
<dt className="text-sm text-muted-foreground">Status</dt>
<dd><Badge variant={getStatusVariant(log.status)}>{log.status}</Badge></dd>
</div>
<div>
<dt className="text-sm text-muted-foreground">API Key</dt>
<dd><Code>{log.apiKey}</Code></dd>
</div>
<div>
<dt className="text-sm text-muted-foreground">IP Address</dt>
<dd>{log.ip}</dd>
</div>
</dl>
</TabsContent>
<TabsContent value="request">
<CodeBlock
language="json"
code={JSON.stringify(log.request, null, 2)}
/>
</TabsContent>
<TabsContent value="response">
<CodeBlock
language="json"
code={JSON.stringify(log.response, null, 2)}
/>
</TabsContent>
<TabsContent value="headers">
<Table>
<TableBody>
{Object.entries(log.headers).map(([key, value]) => (
<TableRow key={key}>
<TableCell className="font-mono">{key}</TableCell>
<TableCell>{value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TabsContent>
</Tabs>
</LogDetailsDialog>Real-Time Streaming
Stream logs in real-time:
<LogsView realtime={true}>
<div className="mb-4 flex items-center justify-between">
<div className="flex items-center gap-2">
<Badge variant="success">
<span className="animate-pulse">●</span> Live
</Badge>
<span className="text-sm text-muted-foreground">
{liveLogCount} new logs
</span>
</div>
<Button
variant="ghost"
onClick={toggleRealtime}
>
{realtime ? 'Pause' : 'Resume'}
</Button>
</div>
<LogsList
logs={logs}
autoScroll={realtime}
/>
</LogsView>Error Highlighting
Highlight failed requests:
<LogsView>
{logs.map(log => (
<LogCard
key={log.id}
log={log}
className={cn(
log.status >= 500 && 'border-destructive bg-destructive/10',
log.status >= 400 && log.status < 500 && 'border-warning bg-warning/10'
)}
>
{log.error && (
<Alert variant="destructive">
<AlertTitle>Error</AlertTitle>
<AlertDescription>{log.error}</AlertDescription>
</Alert>
)}
</LogCard>
))}
</LogsView>Performance Metrics
Show request duration trends:
<LogsView>
<Card>
<CardHeader>
<CardTitle>Response Time Distribution</CardTitle>
</CardHeader>
<CardContent>
<BarChart
data={durationBuckets}
xAxis={{ dataKey: 'range' }}
yAxis={{ label: 'Requests' }}
bars={[
{ dataKey: 'count', fill: '#3b82f6' }
]}
/>
</CardContent>
</Card>
</LogsView>Export Functionality
Export logs in various formats:
const handleExport = async (format: 'csv' | 'json' | 'txt') => {
const data = await exportLogs({
format,
filters,
limit: 10000
})
downloadFile(data, `logs-${Date.now()}.${format}`)
toast.success(`Logs exported as ${format.toUpperCase()}`)
}
<LogsView
allowExport={true}
actions={[
{
id: 'export',
label: 'Export',
icon: 'Download',
onClick: () => showExportDialog()
}
]}
>
<ExportDialog
formats={['csv', 'json', 'txt']}
onExport={handleExport}
/>
</LogsView>Pagination
Handle large log volumes:
<LogsView>
<LogsList logs={logs} />
<Pagination
currentPage={page}
totalPages={totalPages}
onPageChange={setPage}
pageSize={pageSize}
onPageSizeChange={setPageSize}
/>
</LogsView>Empty State
Handle cases with no logs:
<LogsView>
{logs.length === 0 ? (
<EmptyState
icon="FileText"
title="No logs found"
description={
hasFilters
? "No logs match your filters. Try adjusting your search criteria."
: "No API requests logged yet. Make your first API request to see logs."
}
action={
hasFilters
? {
label: 'Clear Filters',
onClick: clearFilters
}
: {
label: 'View API Docs',
onClick: () => navigateToDocs()
}
}
/>
) : (
<LogsList logs={logs} />
)}
</LogsView>Integration with Payload CMS
Auto-wire to Payload logs collection:
// Auto-fetches from Payload 'logs' collection
<LogsView collection="logs">
{/* Logs loaded automatically */}
</LogsView>DeveloperApp Integration
Part of the DeveloperApp component suite:
<DeveloperDashboard
config={{
showAPIKeys: true,
showWebhooks: true,
showUsage: true,
showLogs: true
}}
>
<APIKeysView />
<WebhooksView />
<UsageView />
<LogsView />
</DeveloperDashboard>Responsive Behavior
The view adapts to screen size:
Desktop (≥1024px)
┌────────────────────────────────────────┐
│ Logs [Export] │
├────────────────────────────────────────┤
│ [Filters: Method Status Date Search] │
├────────────────────────────────────────┤
│ Time Method Path Status ms │
│ 08:15 GET /api/v1 200 145 │
│ 08:14 POST /api/v1 500 234 │
│ ... │
└────────────────────────────────────────┘
Full table layoutMobile (under 768px)
┌────────────────────────────┐
│ Logs [Export] │
├────────────────────────────┤
│ [Search] │
├────────────────────────────┤
│ ┌────────────────────┐ │
│ │ GET /api/v1 │ │
│ │ 200 • 145ms │ │
│ │ 08:15 │ │
│ └────────────────────┘ │
└────────────────────────────┘
Compact card layoutAccessibility
The Logs view implements accessibility features:
- Keyboard navigation - Tab through logs and filters
- Screen reader labels - Descriptive labels for log entries
- Status colors - Color-blind friendly status indicators
- Focus management - Focus on selected log
<LogsView aria-label="API request logs">
<Table aria-label="API request log entries">
<TableRow
aria-label={`${log.method} request to ${log.path}, status ${log.status}, ${log.duration} milliseconds`}
>
{/* ... */}
</TableRow>
</Table>
</LogsView>