feat: Add get_proxy_host_logs tool for reading nginx proxy host logs

Reads nginx access/error logs directly from a mounted NPM log directory,
enabling agents to debug proxy issues without SSH access. Requires mounting
NPM's /data/logs volume and setting NPM_LOG_DIR.

Also includes a feature request PRD for proposing a native log API upstream.
This commit is contained in:
2026-05-22 14:27:37 +00:00
parent c3227c3a5f
commit 5e89176806
9 changed files with 628 additions and 2 deletions

View File

@@ -0,0 +1,176 @@
# Feature Request: Per-Host Log Retrieval API
**Project:** [NginxProxyManager/nginx-proxy-manager](https://github.com/NginxProxyManager/nginx-proxy-manager)
**Type:** Feature Request
**Status:** Draft PRD
## Problem Statement
Nginx Proxy Manager writes per-host access and error logs to predictable paths on disk
(`/data/logs/proxy-host-{id}_access.log`, `/data/logs/proxy-host-{id}_error.log`), but
provides no API to read them. The only log-related API is the audit log (`GET /api/audit-log`),
which tracks admin configuration changes — not HTTP traffic.
This means operators and automation tools have no programmatic way to:
- Retrieve recent access log entries for a specific proxy host
- Check error logs when debugging upstream connectivity issues
- Monitor traffic patterns or detect anomalies through the existing API surface
The only workarounds today are direct filesystem access (requiring volume mounts or
`docker exec`) or external log aggregation pipelines, both of which add significant
operational complexity for a task that should be simple.
## Proposed Solution
Add REST API endpoints to retrieve nginx access and error logs for individual proxy hosts.
### New Endpoints
#### `GET /api/nginx/proxy-hosts/{id}/logs`
Retrieve log entries for a specific proxy host.
**Query Parameters:**
| Parameter | Type | Default | Description |
|------------|--------|------------|-------------------------------------------------------|
| `type` | string | `"access"` | Log type: `"access"` or `"error"` |
| `lines` | int | `100` | Number of most recent lines to return (max: `1000`) |
| `search` | string | - | Filter lines containing this substring |
| `since` | string | - | ISO 8601 timestamp — only return lines after this time|
**Response (200):**
```json
{
"host_id": 5,
"log_type": "access",
"file": "proxy-host-5_access.log",
"total_lines": 4821,
"returned_lines": 100,
"lines": [
"[01/Jun/2025:14:22:31 +0000] HIT 200 200 - GET https app.example.com \"/api/data\" [Client 10.0.0.1] [Length 1542] [Gzip -] [Sent-to 192.168.1.50] \"Mozilla/5.0\" \"https://app.example.com/\"",
"..."
]
}
```
**Error Responses:**
| Status | Condition |
|--------|-------------------------------------|
| 404 | Proxy host not found |
| 404 | Log file does not exist |
| 403 | User lacks permission for this host |
#### `GET /api/nginx/proxy-hosts/{id}/logs/summary`
Return a statistical summary of recent traffic for a proxy host.
**Response (200):**
```json
{
"host_id": 5,
"period": "last_1000_lines",
"status_codes": {"200": 812, "301": 45, "404": 23, "500": 3},
"top_paths": ["/api/data", "/", "/login"],
"top_clients": ["10.0.0.1", "10.0.0.5"],
"cache_hit_rate": 0.42,
"access_log_size_bytes": 524288,
"error_log_size_bytes": 8192
}
```
### Permissions
| Permission | Description |
|--------------------|--------------------------------------------|
| `proxy-hosts:logs` | Read logs for proxy hosts the user can view |
Admin users can read logs for any host. Non-admin users can only read logs
for hosts they own, consistent with existing proxy host permissions.
### Backend Implementation Notes
The implementation is straightforward because log paths are already deterministic
and hardcoded in nginx templates:
```javascript
// backend/templates/proxy_host.conf
access_log /data/logs/proxy-host-{{ id }}_access.log proxy;
error_log /data/logs/proxy-host-{{ id }}_error.log warn;
```
A minimal implementation would:
1. Add a new route in `backend/routes/` (e.g., `proxy-host-logs.js`)
2. Verify the proxy host exists and the user has access
3. Read the last N lines from the log file using a reverse-reader (or `tail`-like approach)
4. Optionally filter lines by substring or timestamp
5. Return as JSON
**Reference files for implementation:**
- `backend/routes/nginx/proxy_hosts.js` — existing proxy host routes and permission model
- `backend/templates/proxy_host.conf` — log path template confirming the naming convention
- `backend/lib/access/` — permission definition files
- `docker/rootfs/etc/logrotate.d/nginx-proxy-manager` — log rotation config (rotated logs
have `.1`, `.2.gz` suffixes)
### Log Rotation Consideration
NPM rotates logs weekly (access: 4 rotations, error: 10 rotations). The API should
read only the current (unrotated) log file. Rotated archives (`.1`, `.2.gz`) could be
supported in a future iteration but are not required for the initial implementation.
## Motivation
### Use Cases
1. **MCP/AI Agent Integration** — MCP servers wrapping the NPM API (like
[nginx-proxy-manager-mcp](https://github.com/b3nw/nginx-proxy-manager-mcp)) can
expose log retrieval to AI assistants for debugging proxy issues conversationally.
2. **Quick Debugging** — When a reverse proxy returns errors, operators need to quickly
check the access and error logs for that specific host. Today this requires SSH/exec
access to the container.
3. **Monitoring Dashboards** — Custom dashboards can poll the log endpoint for
traffic summaries without deploying a full log aggregation stack.
4. **Automation** — CI/CD pipelines and health-check scripts can verify that traffic
is flowing correctly to newly deployed services behind NPM.
### Why Not External Log Aggregation?
External solutions (Loki, ELK, Fluentd) are powerful but heavy. Many NPM users run
single-host homelab setups where a full log pipeline is disproportionate to the need.
A built-in API covers 80% of use cases with zero additional infrastructure.
## Alternatives Considered
| Approach | Pros | Cons |
|---------------------------|-----------------------------|-------------------------------------------------|
| **API endpoint (proposed)** | Native, zero extra infra | Requires upstream PR |
| Volume mount + file read | Works today, no NPM changes | Tight coupling, no access control, not portable |
| Docker exec | Works today | Requires Docker socket, security risk |
| Sidecar log server | Decoupled | Extra container, extra config, extra maintenance |
## Scope
### In Scope (v1)
- `GET /api/nginx/proxy-hosts/{id}/logs` with `type`, `lines`, `search` params
- Permission checks consistent with existing proxy host access model
- Current (unrotated) log file only
### Out of Scope (Future)
- Log streaming via WebSocket or SSE
- Rotated log archive access (`.gz` files)
- Log summary/analytics endpoint
- Redirection host, dead host, and stream logs (same pattern, easy to add later)
- Log download as file attachment
- Log retention configuration via API