Using the Log Viewer
Browse, search, filter, and export webhook processing logs through a built-in web UI so you can debug failed webhooks, monitor PR workflows, and audit event history without SSH-ing into your server.
Prerequisites
- A running github-webhook-server instance (see Getting Started)
- The
ENABLE_LOG_SERVERenvironment variable set totrue
Quick Start
Add ENABLE_LOG_SERVER=true to your environment and open /logs in a browser:
# docker-compose.yaml (environment section)
environment:
- ENABLE_LOG_SERVER=true
For a non-Docker setup:
ENABLE_LOG_SERVER=true WEBHOOK_SERVER_DATA_DIR=/path/to/data uv run entrypoint.py
Then navigate to http://your-server:5000/logs.
Warning: The log viewer endpoints are unauthenticated. Deploy only on trusted networks (VPN, internal) or behind a reverse proxy with authentication. Never expose
/logsto the public internet.
Enabling the Log Viewer
Set ENABLE_LOG_SERVER=true as an environment variable before starting the server. The value must be the literal string true — any other value (including True, 1, or yes) leaves the log viewer disabled.
When the log viewer is disabled, all log viewer paths (/logs, /logs/api/*, /logs/ws) return a 404 response.
The viewer reads log files from ${WEBHOOK_SERVER_DATA_DIR}/logs. If you have not set WEBHOOK_SERVER_DATA_DIR, the default is /home/podman/data, so logs are read from /home/podman/data/logs.
See Environment Variables for the full list of environment variables.
Configuring Log Files and Masking
The log viewer uses the same logging configuration as the rest of the server. Add these keys to your config.yaml:
log-level: INFO
log-file: webhook-server.log
logs-server-log-file: logs_server.log
mask-sensitive-data: true
| Key | What it controls | Default |
|---|---|---|
log-level |
Verbosity of logs (INFO or DEBUG) |
INFO |
log-file |
Main log file name | webhook-server.log |
logs-server-log-file |
Separate log file for the log viewer itself | logs_server.log |
mask-sensitive-data |
Redact tokens, passwords, and secrets from log output | true |
Tip: You can override
mask-sensitive-dataper repository inconfig.yamlfor debugging a specific repo without exposing secrets across all repositories.
See Configuration Reference for all available options.
Browsing Logs in the Web UI
Open http://your-server:5000/logs to see the log viewer interface. The page loads the most recent log entries automatically.
Filtering
Use the filter bar at the top of the page to narrow down results:
| Filter | Description | Example |
|---|---|---|
| Search | Free-text search across log messages (case-insensitive) | container build failed |
| Hook ID | GitHub webhook delivery ID (X-GitHub-Delivery header) |
f4b3c2d1-a9b8-... |
| PR # | Pull request number | 42 |
| Repository | Repository in owner/repo format |
myorg/myrepo |
| User | GitHub username who triggered the event | octocat |
| Level | Log severity level | ERROR, WARNING, INFO, DEBUG |
| Start Time / End Time | Restrict results to a time range | Datetime picker |
| Results Limit | Maximum entries to return | 100, 500, 1000, 5000, 10000 |
Filters are applied as you type (with a 300ms debounce). The server re-queries with each filter change to provide accurate, backend-filtered results.
Click Clear Filters to reset all filters at once.
Reading Log Entries
Each log entry shows three columns:
- Timestamp — when the event occurred (displayed in your local timezone)
- Level — severity badge (
INFO,WARNING,ERROR,SUCCESS,STEP,DEBUG,COMPLETED) - Message — the log message followed by clickable metadata tags for Hook ID, PR number, repository, and user
Statistics Bar
Below the filters, three counters help you understand the dataset:
- Shown — number of entries currently displayed
- Total — estimated total log entries across all log files
- Scanned — entries the server examined for the last query (a
+suffix and "(partial scan)" label indicate more logs exist beyond what was scanned)
Real-Time Log Streaming
Click Start Real-time to open a WebSocket connection that streams new log entries as they arrive. The current filter settings are applied to the stream — you only see entries matching your active filters.
Click Stop Real-time to disconnect. The connection status indicator at the top of the page shows whether streaming is active.
Note: Enable Auto-scroll (toggle in the controls) to keep the newest entries visible as they arrive. Disable it when you need to read through older entries without being scrolled away.
Viewing Webhook Flow Timelines
Click any Hook ID link in a log entry to open the Webhook Flow Timeline modal. This shows:
- Flow Overview — hook ID, total steps, processing duration, token spend (API calls made), and repository
- Step-by-step timeline — each workflow step with its status icon (✓ success, ✗ error, ◷ in-progress), relative timing, and duration
- Final status — whether the flow completed successfully, with errors, or is still running
Steps are grouped by task ID. Click a group header to expand and see individual steps. Click any step to view its detailed execution metadata and associated log entries.
Viewing All Events for a PR
Click any PR number link in a log entry to open the PR Workflow modal. This lists every unique webhook delivery ID associated with that PR. Click any event in the list to jump to its flow timeline.
Exporting Logs
Click Export JSON to download the currently filtered logs as a JSON file. The export respects all active filters and the results limit.
The downloaded file includes:
- Export metadata — timestamp, applied filters, and entry count
- Log entries — the full array of matching log entries
The file is named webhook_logs_YYYYMMDD_HHMMSS.json.
Tip: Increase the Results Limit before exporting if you need more than the default number of entries.
Advanced Usage
Switching Themes
Click the theme toggle button (🌙/☀️) in the top-right corner to switch between light and dark mode. Your preference is saved in the browser.
Collapsing the Filter Panel
Click the ▼ button next to "Filters & Controls" to collapse the filter panel and give more screen space to log entries. The collapsed state is remembered across page loads.
Drilling Into Step Logs
In the flow timeline modal, clicking a step fetches the actual log entries that occurred during that step's execution window. This is useful for understanding exactly what happened during a specific operation — for example, why a container build failed or which GitHub API call hit a rate limit.
Each step detail view shows:
- Status badge and duration
- Error details (if the step failed)
- Execution metadata from the structured log
- Time-correlated log entries from the text logs
Using the REST API Directly
The log viewer is backed by a REST API that you can call programmatically. See Log Viewer API Reference for the complete endpoint documentation, including:
GET /logs/api/entries— query and filter log entriesGET /logs/api/export— export filtered logs as JSONGET /logs/api/workflow-steps/{hook_id}— get the step timeline for a webhook deliveryGET /logs/api/step-logs/{hook_id}/{step_name}— get log entries for a specific stepGET /logs/api/pr-flow/{hook_id}— get PR flow visualization dataWebSocket /logs/ws— real-time log streaming with server-side filtering
Troubleshooting
Log viewer page returns 404
- Verify that
ENABLE_LOG_SERVER=trueis set (literal stringtrue). Restart the server after changing environment variables.
No log entries appear
- Check that
WEBHOOK_SERVER_DATA_DIRpoints to a directory containing alogs/subdirectory with.logorwebhooks_*.jsonfiles. - Ensure the server process has read permissions on the log files.
WebSocket disconnects immediately
- The log viewer checks
ENABLE_LOG_SERVERon WebSocket connect. If the environment variable was changed after startup, restart the server.
Sensitive data visible in logs
- Set
mask-sensitive-data: truein yourconfig.yaml(this is the default). See Configuring Repositories for per-repository overrides.
"(partial scan)" shown in statistics
- The server caps how many entries it scans per query for performance. Add more specific filters (hook ID, repository, time range) to narrow the scan, or increase the results limit.