A Rust-based HTTP proxy service that intercepts and logs traffic between clients and the Anthropic API. Switchboard transparently forwards all requests and responses while providing comprehensive logging of both non-streaming and streaming API interactions.
- Transparently proxies requests to Anthropic API endpoints
- Detailed request and response logging with sensitive data masking
- Support for both streaming and non-streaming API responses
- Graceful shutdown handling for reliable operation
- Configurable via environment variables or .env file
- Rust 1.68.1 or later
- An Anthropic API key
For contributing to the project, you'll need the following additional tools:
- Git
- Python 3.6+ (for pre-commit hooks)
- pip (for installing pre-commit)
- glance (optional, used by post-commit hook)
| Variable | Description | Default |
|---|---|---|
PORT |
HTTP port to listen on | DEFAULT_PORT (8080) |
ANTHROPIC_API_KEY |
Your Anthropic API key (required) | - |
ANTHROPIC_TARGET_URL |
Anthropic API base URL | DEFAULT_ANTHROPIC_TARGET_URL (https://api.anthropic.com) |
| Variable | Description | Default |
|---|---|---|
LOG_LEVEL |
Minimum log level for stdout (trace, debug, info, warn, error) | DEFAULT_LOG_STDOUT_LEVEL (info) |
LOG_FILE_LEVEL |
Minimum log level for file output | DEFAULT_LOG_FILE_LEVEL (debug) |
LOG_FORMAT |
Log output format for stdout (pretty or json) | DEFAULT_LOG_FORMAT (pretty) |
LOG_FILE_PATH |
Path to the log file with daily rotation | DEFAULT_LOG_FILE_PATH (./switchboard.log) |
LOG_BODIES |
Whether to log full request and response bodies | DEFAULT_LOG_BODIES (true) |
LOG_MAX_BODY_SIZE |
Maximum size in bytes for logged bodies before truncation | DEFAULT_LOG_MAX_BODY_SIZE (20480) |
LOG_DIRECTORY_MODE |
Controls how the log directory is determined (default, xdg, system) | LogDirectoryMode::Default (default) |
LOG_MAX_AGE_DAYS |
Maximum age for log files in days before automatic cleanup | DEFAULT_LOG_MAX_AGE_DAYS (None - disabled) |
Note: All default values are centralized in
src/config.rsas constants to ensure consistency throughout the application.
- Clone the repository
- Create a
.envfile in the project root (or set environment variables):
# Server configuration
PORT=8080 # From DEFAULT_PORT
ANTHROPIC_API_KEY=your-api-key-here
ANTHROPIC_TARGET_URL=https://api.anthropic.com # From DEFAULT_ANTHROPIC_TARGET_URL
# Logging configuration
LOG_LEVEL=info # From DEFAULT_LOG_STDOUT_LEVEL
LOG_FILE_LEVEL=debug # From DEFAULT_LOG_FILE_LEVEL
LOG_FORMAT=pretty # From DEFAULT_LOG_FORMAT
LOG_FILE_PATH=./switchboard.log # From DEFAULT_LOG_FILE_PATH
LOG_BODIES=true # From DEFAULT_LOG_BODIES
LOG_MAX_BODY_SIZE=20480 # From DEFAULT_LOG_MAX_BODY_SIZE
LOG_DIRECTORY_MODE=default # Maps to LogDirectoryMode::Default
LOG_MAX_AGE_DAYS=30 # Cleanup logs older than 30 days (defaults to None when not set)
# Development build
cargo build
# Production build
cargo build --release# Run in development mode
cargo run
# Run with the compiled binary
./target/release/switchboard
# View command-line options
./target/release/switchboard --help
# Run log cleanup and exit
./target/release/switchboard --clean-logs# Run all tests
cargo testOnce running, the proxy service listens on the configured port (default: 8080). Configure your Anthropic API client to direct requests to this proxy instead of the Anthropic API directly:
# Original API endpoint
https://api.anthropic.com/v1/messages
# Proxied endpoint (if running locally on port 8080)
http://localhost:8080/v1/messages
Requests will be forwarded to the Anthropic API, and both requests and responses will be logged according to your logging configuration.
Switchboard implements a dual-output logging system that provides comprehensive logging capabilities with minimal performance impact.
Logs are sent to two destinations simultaneously:
-
File Output:
- JSON-formatted logs for machine parsing
- Daily log rotation (files named like
switchboard.log.2023-04-24) - Non-blocking I/O for minimal performance impact
- Typically more verbose (default level: debug)
- Ideal for automated analysis and troubleshooting
-
Stdout Output:
- Configurable format (pretty or JSON)
- Typically less verbose (default level: info)
- Ideal for immediate feedback during development
2023-04-24T12:34:56.789012Z INFO switchboard::proxy_handler: Processing request method=POST path=/v1/messages query=
2023-04-24T12:34:56.842125Z INFO switchboard::proxy_handler: Received response from Anthropic API with status 200 request_id=d29f1db4-73cb-4e8f-9cd1-b9a971b088ff status=200 headers_count=12
{
"timestamp": "2023-04-24T12:34:56.789012Z",
"level": "INFO",
"fields": {
"message": "Processing request",
"method": "POST",
"path": "/v1/messages",
"query": ""
},
"target": "switchboard::proxy_handler"
}Logs can be filtered by level, from most to least verbose:
- trace: Very detailed debugging information (rarely used)
- debug: Technical details useful for debugging
- info: General information about normal operation
- warn: Warning conditions that don't prevent operation
- error: Error conditions that may impair functionality
- Bodies are logged when
LOG_BODIES=true(the default) - Bodies are truncated at
LOG_MAX_BODY_SIZE(default: 20480 bytes) - Logged at DEBUG level for both request and response
- JSON bodies are pretty-printed for readability
- Sensitive headers like
Authorizationare automatically redacted
LOG_LEVEL=warn # Higher than DEFAULT_LOG_STDOUT_LEVEL for less verbose output
LOG_FILE_LEVEL=info # Less verbose than DEFAULT_LOG_FILE_LEVEL
LOG_DIRECTORY_MODE=system # Use LogDirectoryMode::System
LOG_FILE_PATH=app.log # Different from DEFAULT_LOG_FILE_PATH
LOG_BODIES=false # Opposite of DEFAULT_LOG_BODIES
LOG_MAX_AGE_DAYS=90 # Longer retention than default
LOG_LEVEL=debug # More verbose than DEFAULT_LOG_STDOUT_LEVEL
LOG_FORMAT=pretty # Same as DEFAULT_LOG_FORMAT
LOG_DIRECTORY_MODE=default # Same as LogDirectoryMode::Default
LOG_FILE_PATH=dev.log # Different from DEFAULT_LOG_FILE_PATH
LOG_BODIES=true # Same as DEFAULT_LOG_BODIES
LOG_MAX_AGE_DAYS=14 # Shorter retention than typical production
LOG_LEVEL=info # Same as DEFAULT_LOG_STDOUT_LEVEL
LOG_FILE_LEVEL=debug # Same as DEFAULT_LOG_FILE_LEVEL
LOG_DIRECTORY_MODE=xdg # Use LogDirectoryMode::Xdg
LOG_FILE_PATH=switchboard.log # Similar to DEFAULT_LOG_FILE_PATH
LOG_MAX_AGE_DAYS=30 # Custom retention period
LOG_LEVEL=error # Minimal logging compared to DEFAULT_LOG_STDOUT_LEVEL
LOG_FILE_LEVEL=error # Minimal logging compared to DEFAULT_LOG_FILE_LEVEL
LOG_DIRECTORY_MODE=default # Same as LogDirectoryMode::Default
LOG_BODIES=false # Opposite of DEFAULT_LOG_BODIES for performance
# LOG_MAX_AGE_DAYS # Omitted to use DEFAULT_LOG_MAX_AGE_DAYS (None)
File logs are automatically rotated daily with date suffixes:
switchboard.log.2023-04-23switchboard.log.2023-04-24
This prevents log files from growing too large and makes it easier to find logs from a specific date.
Switchboard includes automatic log cleanup functionality to prevent logs from accumulating indefinitely:
- Configuration: Set
LOG_MAX_AGE_DAYSto specify the maximum age for log files (in days) - Startup Cleanup: Logs older than the specified age are automatically cleaned up at application startup
- Manual Cleanup: Run the application with the
--clean-logsflag to perform cleanup and exit# Clean up logs and exit (using configured LOG_MAX_AGE_DAYS) ./target/release/switchboard --clean-logs - Cleanup Scope: Both application and test logs are cleaned up
- Safety: Non-log files are never removed, even if they're in the log directories
- Detailed Reporting: Cleanup results are logged with file counts and total bytes removed
When cleanup is enabled, the application will scan both the app/ and test/ log subdirectories and remove any log files with modification times older than the specified cutoff date.
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
│ Configuration │ │ Cleanup Trigger │ │ Log Directories │
│ LOG_MAX_AGE_DAYS=30│────▶│ - Application Start│────▶│ - logs/app/ │
│ │ │ - --clean-logs Flag│ │ - logs/test/ │
└────────────────────┘ └────────────────────┘ └────────────────────┘
│ │
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Log Cleanup Process │
│ │
│ 1. Calculate cutoff date (current date - LOG_MAX_AGE_DAYS) │
│ 2. Scan both app/ and test/ directories │
│ 3. Identify log files (*.log and *.log.YYYY-MM-DD format) │
│ 4. Check modification times against cutoff date │
│ 5. Remove log files older than cutoff date │
│ 6. Report cleanup results (files and bytes removed) │
└─────────────────────────────────────────────────────────────────────────┘
Logs are organized in a structured directory hierarchy with separate subdirectories for application and test logs. The base directory depends on the deployment environment (configurable with LOG_DIRECTORY_MODE):
<base_directory>/
├── app/ # Application logs
│ ├── switchboard.log
│ ├── switchboard.log.2023-04-23
│ └── switchboard.log.2023-04-24
└── test/ # Test logs
├── test_switchboard.log
├── test_switchboard.log.2023-04-23
└── test_switchboard.log.2023-04-24
This separation ensures:
- Application logs don't get mixed with test logs
- Log files are organized by purpose
- Cleanup and retention policies can be applied separately
The base directory for logs is determined based on the deployment environment and the LOG_DIRECTORY_MODE setting:
| Environment | LOG_DIRECTORY_MODE | Base Directory |
|---|---|---|
| Development | default | ./logs/ (current directory) |
| User Installation | default or xdg | Linux: ~/.local/share/switchboard/logsmacOS: ~/Library/Application Support/switchboard/logsWindows: C:\Users\<user>\AppData\Roaming\switchboard\logs |
| System Service | default or system | Unix: /var/log/switchboardWindows: C:\ProgramData\Switchboard\Logs |
Environment detection is automatic:
- Development is detected by debug builds or when
SWITCHBOARD_DEVenvironment variable is set - User Installation is detected when running from a user's home directory
- System Service is detected when running as a service (systemd, launchd, Windows Service)
When you provide a log file path (e.g., ./switchboard.log), the logger:
- Extracts just the filename from the provided path
- Determines the appropriate base directory based on environment and configuration
- Appends the appropriate subdirectory (
app/ortest/) based on the log type - Creates the directory structure if it doesn't exist
- Sets appropriate permissions on the directories
- Returns the fully resolved path (e.g.,
./logs/app/switchboard.log)
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
│ User Configuration │ │ Environment │ │ Log Type │
│ LOG_FILE_PATH │ │ - Development │ │ - Application │
│ LOG_DIRECTORY_MODE │────▶│ - User Installation│────▶│ - Test │
│ │ │ - System Service │ │ │
└────────────────────┘ └────────────────────┘ └────────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Path Resolution Process │
│ │
│ 1. Extract filename from LOG_FILE_PATH │
│ 2. Determine base directory from environment & LOG_DIRECTORY_MODE │
│ 3. Append appropriate subdirectory (app/ or test/) │
│ 4. Create directories if needed │
│ 5. Set appropriate permissions │
└─────────────────────────────────────────────────────────────────────────┘
│
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Final Resolved Path │
│ │
│ <base_directory>/<subdirectory>/<filename> │
│ Example: ./logs/app/switchboard.log │
└─────────────────────────────────────────────────────────────────────────┘
If you're upgrading from a previous version, you may have log files in the root directory. Use the provided migration utility to move them to the correct locations:
# Run the log migration utility
./tools/migrate_logs.shThis script will automatically:
- Identify log files in the root directory
- Move them to the appropriate subdirectory based on naming patterns
- Preserve timestamps and handle duplicates
The LOG_DIRECTORY_MODE environment variable controls how the application selects the base directory for logs, allowing for different deployment scenarios:
-
default: Automatically determines the log directory based on environment detection
- Development: Uses
./logs/in the current directory - User Installation: Uses XDG-compliant directory for the current user
- System Service: Uses system log path (
/var/log/switchboardon Unix-like systems)
- Development: Uses
-
xdg: Forces use of XDG Base Directory specification
- Linux:
~/.local/share/switchboard/logs - macOS:
~/Library/Application Support/switchboard/logs - Windows:
C:\Users\<user>\AppData\Roaming\switchboard\logs
- Linux:
-
system: Forces use of system log directories
- Unix-like:
/var/log/switchboard - Windows:
C:\ProgramData\Switchboard\Logs
- Unix-like:
This allows for seamless operation in different environments without requiring manual configuration changes.
When investigating issues:
- Check file logs first: They contain more detailed information (debug level)
- Increase stdout verbosity: Set
LOG_LEVEL=debugto see more details on the console - Enable body logging: Make sure
LOG_BODIES=trueto see full request/response content - Look for correlation IDs: Each request gets a unique ID that appears in all related logs
- Check log file permissions: Ensure the application has write access to the log directory
# Check for errors without building
cargo check
# Run formatter
cargo fmt
# Run linter
cargo clippy
# Generate and open documentation
cargo doc --openWe use the Python-based pre-commit framework to manage Git hooks for ensuring code quality and commit message standards. This tool automatically runs checks before commits are created to catch issues early.
Before setting up the hooks, ensure you have the following tools installed:
- Python 3.6+: Required to run the pre-commit framework
- pip: Python package manager to install pre-commit
- glance (optional): Used by post-commit hook to automatically scan the repository after commits
- Install pre-commit:
# Install pre-commit using pip
pip install pre-commit- Set up the hooks:
# Install the pre-commit and commit-msg hooks
pre-commit install --hook-type pre-commit --hook-type commit-msg- Set up the post-commit hook (optional):
# Copy the post-commit hook template to your Git hooks directory
cp templates/post-commit.template .git/hooks/post-commit
# Make it executable (required on Unix-based systems)
chmod +x .git/hooks/post-commitThe pre-commit framework manages the following hooks:
-
rustfmt (pre-commit):
- Ensures all Rust code follows the project's formatting standards
- Uses
cargo fmt -- --checkto verify without modifying files - Fails if any file doesn't meet the formatting standards
-
clippy (pre-commit):
- Performs static analysis on Rust code to detect common issues
- Uses
cargo clippy --all-targets -- -D warningsto treat warnings as errors - Helps catch potential bugs and improve code quality
-
commitlint (commit-msg):
- Validates commit messages follow the Conventional Commits format
- Ensures messages have the proper structure (type, scope, description)
- Helps maintain a clean, readable commit history
-
post-commit (optional):
- Runs the
glancetool asynchronously after successful commits - Provides quick feedback on the repository state
- Non-blocking, so it won't delay your workflow
- Runs the
The hooks run automatically when you commit changes. If a hook fails, the commit will be aborted with an error message. Fix the reported issues and try again.
If you need to bypass hooks in exceptional circumstances:
# Skip one or more specific hooks
SKIP=rustfmt,clippy git commit -m "your commit message"
# Skip all pre-commit hooks (not recommended for regular use)
SKIP=pre-commit git commit -m "your commit message"Note: Using git commit --no-verify to bypass hooks is strongly discouraged as it circumvents all quality checks. Use the SKIP mechanism instead, which clearly documents which specific checks are being skipped.
If you encounter issues with the hooks:
- Ensure pre-commit is installed:
pre-commit --version - Update pre-commit and hooks:
pre-commit autoupdate - Check hook configurations in
.pre-commit-config.yaml - For commitlint issues, verify your commit message format against the Conventional Commits specification
This project is licensed under the MIT License.