mdxui

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>

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>

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 layout

Mobile (under 768px)

┌────────────────────────────┐
│  Logs             [Export] │
├────────────────────────────┤
│  [Search]                  │
├────────────────────────────┤
│  ┌────────────────────┐    │
│  │ GET /api/v1        │    │
│  │ 200 • 145ms        │    │
│  │ 08:15              │    │
│  └────────────────────┘    │
└────────────────────────────┘
Compact card layout

Accessibility

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>

On this page