2 Commits

Author SHA1 Message Date
110f87e53f docs: add logging configuration to README
All checks were successful
Build and Push Docker Image / build (push) Successful in 8s
2026-01-02 13:24:38 -05:00
f48dafc88f fix(logging): suppress uvicorn access logs and prevent duplicate logging
All checks were successful
Build and Push Docker Image / build (push) Successful in 14s
2026-01-02 13:20:35 -05:00
3 changed files with 61 additions and 8 deletions

View File

@@ -149,6 +149,7 @@ Add to your MCP client configuration (e.g., Claude Desktop):
| `PORT` | Server port | `3000` |
| `GRIST_MCP_TOKEN` | Agent authentication token (required) | - |
| `CONFIG_PATH` | Path to config file inside container | `/app/config.yaml` |
| `LOG_LEVEL` | Logging verbosity (`DEBUG`, `INFO`, `WARNING`, `ERROR`) | `INFO` |
### config.yaml Structure
@@ -188,6 +189,55 @@ tokens:
- `write`: Add, update, delete records
- `schema`: Create tables, add/modify/delete columns
## Logging
### Configuration
Set the `LOG_LEVEL` environment variable to control logging verbosity:
| Level | Description |
|-------|-------------|
| `DEBUG` | Show all logs including HTTP requests and tool arguments |
| `INFO` | Show tool calls with stats (default) |
| `WARNING` | Show only auth errors and warnings |
| `ERROR` | Show only errors |
```bash
# In .env or docker-compose.yml
LOG_LEVEL=INFO
```
### Log Format
At `INFO` level, each tool call produces a single log line:
```
2026-01-02 10:15:23 | agent-name (abc...xyz) | get_records | sales | 42 records | success | 125ms
```
| Field | Description |
|-------|-------------|
| Timestamp | `YYYY-MM-DD HH:MM:SS` |
| Agent | Agent name with truncated token |
| Tool | MCP tool name |
| Document | Document name (or `-` for list_documents) |
| Stats | Operation result (e.g., `42 records`, `3 tables`) |
| Status | `success`, `auth_error`, or `error` |
| Duration | Execution time in milliseconds |
Errors include details on a second indented line:
```
2026-01-02 10:15:23 | agent-name (abc...xyz) | add_records | sales | - | error | 89ms
Grist API error: Invalid column 'foo'
```
### Production Recommendations
- Use `LOG_LEVEL=INFO` for normal operation (default)
- Use `LOG_LEVEL=DEBUG` for troubleshooting (shows HTTP traffic)
- Use `LOG_LEVEL=WARNING` for minimal logging
## Security
- **Token-based auth**: Each agent has a unique token with specific document access

View File

@@ -26,6 +26,7 @@ def setup_logging() -> None:
logger = logging.getLogger("grist_mcp")
logger.setLevel(level)
logger.propagate = False # Prevent duplicate logs to root logger
# Only add handler if not already configured
if not logger.handlers:

View File

@@ -191,14 +191,16 @@ def _print_mcp_config(external_port: int, tokens: list) -> None:
print()
class HealthCheckFilter(logging.Filter):
"""Suppress health check requests unless LOG_LEVEL is DEBUG."""
class UvicornAccessFilter(logging.Filter):
"""Suppress uvicorn access logs unless LOG_LEVEL is DEBUG.
At INFO level, only grist_mcp tool logs are shown.
At DEBUG level, all HTTP requests are visible.
"""
def filter(self, record: logging.LogRecord) -> bool:
if "/health" in record.getMessage():
# Only show health checks at DEBUG level
return os.environ.get("LOG_LEVEL", "INFO").upper() == "DEBUG"
return True
# Only show uvicorn access logs at DEBUG level
return os.environ.get("LOG_LEVEL", "INFO").upper() == "DEBUG"
def main():
@@ -209,8 +211,8 @@ def main():
setup_logging()
# Add health check filter to uvicorn access logger
logging.getLogger("uvicorn.access").addFilter(HealthCheckFilter())
# Suppress uvicorn access logs at INFO level (only show tool logs)
logging.getLogger("uvicorn.access").addFilter(UvicornAccessFilter())
if not _ensure_config(config_path):
return