v0.1.11 configurable logging added, minor refactoring, orgnized docs added
This commit is contained in:
76
doc/README.md
Normal file
76
doc/README.md
Normal file
@ -0,0 +1,76 @@
|
||||
# LogWisp Documentation
|
||||
|
||||
Welcome to the LogWisp documentation. This guide covers all aspects of installing, configuring, and using LogWisp for multi-stream log monitoring.
|
||||
|
||||
## 📚 Documentation Index
|
||||
|
||||
### Getting Started
|
||||
- **[Installation Guide](installation.md)** - How to install LogWisp on various platforms
|
||||
- **[Quick Start](quickstart.md)** - Get up and running in 5 minutes
|
||||
- **[Architecture Overview](architecture.md)** - System design and components
|
||||
|
||||
### Configuration
|
||||
- **[Configuration Guide](configuration.md)** - Complete configuration reference
|
||||
- **[Environment Variables](environment.md)** - Environment variable reference
|
||||
- **[Command Line Options](cli.md)** - CLI flags and parameters
|
||||
|
||||
### Features
|
||||
- **[Filters Guide](filters.md)** - Pattern-based log filtering
|
||||
- **[Rate Limiting](ratelimiting.md)** - Request and connection limiting
|
||||
- **[Router Mode](router.md)** - Path-based multi-stream routing
|
||||
- **[Authentication](authentication.md)** - Securing your log streams *(planned)*
|
||||
|
||||
### Operations
|
||||
- **[Monitoring & Status](monitoring.md)** - Health checks and statistics
|
||||
- **[Performance Tuning](performance.md)** - Optimization guidelines
|
||||
- **[Troubleshooting](troubleshooting.md)** - Common issues and solutions
|
||||
|
||||
### Advanced Topics
|
||||
- **[Security Best Practices](security.md)** - Hardening your deployment
|
||||
- **[Integration Examples](integrations.md)** - Working with other tools
|
||||
- **[Development Guide](development.md)** - Contributing to LogWisp
|
||||
|
||||
## 🚀 Quick Links
|
||||
|
||||
- **[Example Configurations](examples/)** - Ready-to-use config templates
|
||||
- **[API Reference](api.md)** - SSE/TCP protocol documentation
|
||||
- **[Changelog](../CHANGELOG.md)** - Version history and updates
|
||||
|
||||
## 💡 Common Use Cases
|
||||
|
||||
### Single Application Monitoring
|
||||
Monitor logs from one application with basic filtering:
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "myapp"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/myapp", pattern = "*.log" }]
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = ["ERROR", "WARN"]
|
||||
```
|
||||
|
||||
### Multi-Service Architecture
|
||||
Monitor multiple services with different configurations:
|
||||
```bash
|
||||
logwisp --router --config /etc/logwisp/services.toml
|
||||
```
|
||||
|
||||
### High-Security Environments
|
||||
Enable authentication and rate limiting:
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 10.0
|
||||
max_connections_per_ip = 3
|
||||
```
|
||||
|
||||
## 🔍 Finding Help
|
||||
|
||||
- **GitHub Issues**: [Report bugs or request features](https://github.com/logwisp/logwisp/issues)
|
||||
- **Discussions**: [Ask questions and share ideas](https://github.com/logwisp/logwisp/discussions)
|
||||
- **Examples**: Check the [examples directory](examples/) for common scenarios
|
||||
|
||||
## 📝 License
|
||||
|
||||
BSD-3-Clause
|
||||
@ -1,422 +0,0 @@
|
||||
# LogWisp Architecture and Project Structure
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
logwisp/
|
||||
├── Makefile # Build automation with version injection
|
||||
├── go.mod # Go module definition
|
||||
├── go.sum # Go module checksums
|
||||
├── README.md # Project documentation
|
||||
├── config/
|
||||
│ ├── logwisp.toml.defaults # Default configuration and guide
|
||||
│ ├── logwisp.toml.example # Example configuration
|
||||
│ └── logwisp.toml.minimal # Minimal configuration template
|
||||
├── doc/
|
||||
│ └── architecture.md # This file - architecture documentation
|
||||
└── src/
|
||||
├── cmd/
|
||||
│ └── logwisp/
|
||||
│ └── main.go # Application entry point, CLI handling
|
||||
└── internal/
|
||||
├── config/
|
||||
│ ├── auth.go # Authentication configuration structures
|
||||
│ ├── config.go # Main configuration structures
|
||||
│ ├── loader.go # Configuration loading with lixenwraith/config
|
||||
│ ├── server.go # TCP/HTTP server configurations with rate limiting
|
||||
│ ├── ssl.go # SSL/TLS configuration structures
|
||||
│ ├── stream.go # Stream-specific configurations with filters
|
||||
│ └── validation.go # Configuration validation including filters and rate limits
|
||||
├── filter/
|
||||
│ ├── filter.go # Regex-based log filtering implementation
|
||||
│ └── chain.go # Sequential filter chain management
|
||||
├── monitor/
|
||||
│ ├── file_watcher.go # File watching and rotation detection
|
||||
│ └── monitor.go # Log monitoring interface and implementation
|
||||
├── ratelimit/
|
||||
│ ├── ratelimit.go # Token bucket algorithm implementation
|
||||
│ └── limiter.go # Per-stream rate limiter with IP tracking
|
||||
├── service/
|
||||
│ ├── httprouter.go # HTTP router for path-based routing
|
||||
│ ├── logstream.go # Stream lifecycle management
|
||||
│ ├── routerserver.go # Router server implementation
|
||||
│ └── service.go # Multi-stream service orchestration
|
||||
├── transport/
|
||||
│ ├── httpstreamer.go # HTTP/SSE streaming with rate limiting
|
||||
│ ├── noop_logger.go # Silent logger for gnet
|
||||
│ ├── tcpserver.go # TCP server with rate limiting (gnet)
|
||||
│ └── tcpstreamer.go # TCP streaming implementation
|
||||
└── version/
|
||||
└── version.go # Version information management
|
||||
```
|
||||
|
||||
## Configuration System
|
||||
|
||||
### Configuration Hierarchy (Highest to Lowest Priority)
|
||||
|
||||
1. **CLI Arguments**: Direct command-line flags
|
||||
2. **Environment Variables**: `LOGWISP_` prefixed variables
|
||||
3. **Configuration File**: TOML format configuration
|
||||
4. **Built-in Defaults**: Hardcoded default values
|
||||
|
||||
### Configuration Locations
|
||||
|
||||
```bash
|
||||
# Default configuration file location
|
||||
~/.config/logwisp.toml
|
||||
|
||||
# Override via environment variable
|
||||
export LOGWISP_CONFIG_FILE=/etc/logwisp/production.toml
|
||||
|
||||
# Override config directory
|
||||
export LOGWISP_CONFIG_DIR=/etc/logwisp
|
||||
export LOGWISP_CONFIG_FILE=production.toml # Relative to CONFIG_DIR
|
||||
|
||||
# Direct CLI override
|
||||
./logwisp --config /path/to/config.toml
|
||||
```
|
||||
|
||||
### Environment Variable Mapping
|
||||
|
||||
Environment variables follow a structured naming pattern:
|
||||
- Prefix: `LOGWISP_`
|
||||
- Path separator: `_` (underscore)
|
||||
- Array index: Numeric suffix (0-based)
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
# Stream-specific settings
|
||||
LOGWISP_STREAMS_0_NAME=app
|
||||
LOGWISP_STREAMS_0_MONITOR_CHECK_INTERVAL_MS=50
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_PORT=8080
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_BUFFER_SIZE=2000
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_HEARTBEAT_ENABLED=true
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_HEARTBEAT_FORMAT=json
|
||||
|
||||
# Filter configuration
|
||||
LOGWISP_STREAMS_0_FILTERS_0_TYPE=include
|
||||
LOGWISP_STREAMS_0_FILTERS_0_LOGIC=or
|
||||
LOGWISP_STREAMS_0_FILTERS_0_PATTERNS='["ERROR","WARN"]'
|
||||
|
||||
# Rate limiting configuration
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_ENABLED=true
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_REQUESTS_PER_SECOND=10.0
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_BURST_SIZE=20
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_LIMIT_BY=ip
|
||||
|
||||
# Multiple streams
|
||||
LOGWISP_STREAMS_1_NAME=system
|
||||
LOGWISP_STREAMS_1_MONITOR_CHECK_INTERVAL_MS=1000
|
||||
LOGWISP_STREAMS_1_TCPSERVER_PORT=9090
|
||||
```
|
||||
|
||||
## Component Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
1. **Service (`logstream.Service`)**
|
||||
- Manages multiple log streams
|
||||
- Handles lifecycle (creation, shutdown)
|
||||
- Provides global statistics
|
||||
- Thread-safe stream registry
|
||||
|
||||
2. **LogStream (`logstream.LogStream`)**
|
||||
- Represents a single log monitoring pipeline
|
||||
- Contains: Monitor + Filter Chain + Rate Limiter + Servers (TCP/HTTP)
|
||||
- Independent configuration
|
||||
- Per-stream statistics with filter and rate limit metrics
|
||||
|
||||
3. **Monitor (`monitor.Monitor`)**
|
||||
- Watches files and directories
|
||||
- Detects log rotation
|
||||
- Publishes log entries to subscribers
|
||||
- Configurable check intervals
|
||||
|
||||
4. **Filter (`filter.Filter`)**
|
||||
- Regex-based log filtering
|
||||
- Include (whitelist) or Exclude (blacklist) modes
|
||||
- OR/AND logic for multiple patterns
|
||||
- Per-filter statistics (processed, matched, dropped)
|
||||
|
||||
5. **Filter Chain (`filter.Chain`)**
|
||||
- Sequential application of multiple filters
|
||||
- All filters must pass for entry to be streamed
|
||||
- Aggregate statistics across filter chain
|
||||
|
||||
6. **Rate Limiter (`ratelimit.Limiter`)**
|
||||
- Token bucket algorithm for smooth rate limiting
|
||||
- Per-IP or global limiting strategies
|
||||
- Connection tracking and limits
|
||||
- Automatic cleanup of stale entries
|
||||
- Non-blocking rejection of excess requests
|
||||
|
||||
7. **Streamers**
|
||||
- **HTTPStreamer**: SSE-based streaming over HTTP
|
||||
- Rate limit enforcement before request handling
|
||||
- Connection tracking for per-IP limits
|
||||
- Configurable 429 responses
|
||||
- **TCPStreamer**: Raw JSON streaming over TCP
|
||||
- Silent connection drops when rate limited
|
||||
- Per-IP connection tracking
|
||||
- Both support configurable heartbeats
|
||||
- Non-blocking client management
|
||||
|
||||
8. **HTTPRouter (`logstream.HTTPRouter`)**
|
||||
- Optional component for path-based routing
|
||||
- Consolidates multiple HTTP streams on shared ports
|
||||
- Provides global status endpoint
|
||||
- Longest-prefix path matching
|
||||
- Dynamic stream registration/deregistration
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
File System → Monitor → LogEntry Channel → Filter Chain → [Rate Limiter] → Streamer → Network Client
|
||||
↑ ↓ ↓ ↓
|
||||
└── Rotation Detection Pattern Match Rate Limit Check
|
||||
↓ ↓
|
||||
Pass/Drop Accept/Reject
|
||||
```
|
||||
|
||||
### Filter Architecture
|
||||
|
||||
```
|
||||
Log Entry → Filter Chain → Filter 1 → Filter 2 → ... → Output
|
||||
↓ ↓
|
||||
Include? Exclude?
|
||||
↓ ↓
|
||||
OR/AND OR/AND
|
||||
Logic Logic
|
||||
```
|
||||
|
||||
### Rate Limiting Architecture
|
||||
|
||||
```
|
||||
Client Request → Rate Limiter → Token Bucket Check → Allow/Deny
|
||||
↓ ↓
|
||||
IP Tracking Refill Rate
|
||||
↓
|
||||
Cleanup Timer
|
||||
```
|
||||
|
||||
### Configuration Structure
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "stream-name"
|
||||
|
||||
[streams.monitor]
|
||||
check_interval_ms = 100 # Per-transport check interval
|
||||
targets = [
|
||||
{ path = "/path/to/logs", pattern = "*.log", is_file = false },
|
||||
{ path = "/path/to/file.log", is_file = true }
|
||||
]
|
||||
|
||||
# Filter configuration (optional)
|
||||
[[streams.filters]]
|
||||
type = "include" # "include" or "exclude"
|
||||
logic = "or" # "or" or "and"
|
||||
patterns = [
|
||||
"(?i)error", # Case-insensitive error matching
|
||||
"(?i)warn" # Case-insensitive warning matching
|
||||
]
|
||||
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = ["DEBUG", "TRACE"]
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
buffer_size = 1000
|
||||
stream_path = "/stream"
|
||||
status_path = "/status"
|
||||
|
||||
[streams.httpserver.heartbeat]
|
||||
enabled = true
|
||||
interval_seconds = 30
|
||||
format = "comment" # or "json"
|
||||
include_timestamp = true
|
||||
include_stats = false
|
||||
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = false # Disabled by default
|
||||
requests_per_second = 10.0 # Token refill rate
|
||||
burst_size = 20 # Token bucket capacity
|
||||
limit_by = "ip" # "ip" or "global"
|
||||
response_code = 429 # HTTP response code
|
||||
response_message = "Rate limit exceeded"
|
||||
max_connections_per_ip = 5 # Concurrent connection limit
|
||||
max_total_connections = 100 # Global connection limit
|
||||
|
||||
[streams.tcpserver]
|
||||
enabled = true
|
||||
port = 9090
|
||||
buffer_size = 5000
|
||||
|
||||
[streams.tcpserver.heartbeat]
|
||||
enabled = true
|
||||
interval_seconds = 60
|
||||
include_timestamp = true
|
||||
include_stats = true
|
||||
|
||||
[streams.tcpserver.rate_limit]
|
||||
enabled = false
|
||||
requests_per_second = 5.0
|
||||
burst_size = 10
|
||||
limit_by = "ip"
|
||||
```
|
||||
|
||||
## Filter Implementation
|
||||
|
||||
### Filter Types
|
||||
1. **Include Filter**: Only logs matching patterns are streamed (whitelist)
|
||||
2. **Exclude Filter**: Logs matching patterns are dropped (blacklist)
|
||||
|
||||
### Pattern Logic
|
||||
- **OR Logic**: Log matches if ANY pattern matches
|
||||
- **AND Logic**: Log matches only if ALL patterns match
|
||||
|
||||
### Filter Chain
|
||||
- Multiple filters are applied sequentially
|
||||
- All filters must pass for a log to be streamed
|
||||
- Efficient short-circuit evaluation
|
||||
|
||||
### Performance Considerations
|
||||
- Regex patterns compiled once at startup
|
||||
- Cached for efficient matching
|
||||
- Statistics tracked without locks in hot path
|
||||
|
||||
## Rate Limiting Implementation
|
||||
|
||||
### Token Bucket Algorithm
|
||||
- Each IP (or global limiter) gets a bucket with configurable capacity
|
||||
- Tokens refill at `requests_per_second` rate
|
||||
- Each request/connection consumes one token
|
||||
- Smooth rate limiting without hard cutoffs
|
||||
|
||||
### Limiting Strategies
|
||||
1. **Per-IP**: Each client IP gets its own token bucket
|
||||
2. **Global**: All clients share a single token bucket
|
||||
|
||||
### Connection Limits
|
||||
- Per-IP connection limits prevent single client resource exhaustion
|
||||
- Global connection limits protect overall system resources
|
||||
- Checked before rate limits to prevent connection hanging
|
||||
|
||||
### Cleanup
|
||||
- IP entries older than 5 minutes are automatically removed
|
||||
- Prevents unbounded memory growth
|
||||
- Runs every minute in background
|
||||
|
||||
## Build System
|
||||
|
||||
### Makefile Targets
|
||||
|
||||
```bash
|
||||
make build # Build with version information
|
||||
make install # Install to /usr/local/bin
|
||||
make clean # Remove built binary
|
||||
make test # Run test suite
|
||||
make release TAG=v1.0.0 # Create and push git tag
|
||||
```
|
||||
|
||||
### Version Management
|
||||
|
||||
Version information is injected at compile time:
|
||||
```bash
|
||||
# Automatic version detection from git
|
||||
VERSION := $(shell git describe --tags --always --dirty)
|
||||
GIT_COMMIT := $(shell git rev-parse --short HEAD)
|
||||
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
|
||||
|
||||
# Manual build with version
|
||||
go build -ldflags "-X 'logwisp/src/internal/version.Version=v1.0.0'" \
|
||||
-o logwisp ./src/cmd/logwisp
|
||||
```
|
||||
|
||||
## Operating Modes
|
||||
|
||||
### 1. Standalone Mode (Default)
|
||||
- Each stream runs its own HTTP/TCP servers
|
||||
- Direct port access per stream
|
||||
- Simple configuration
|
||||
- Best for single-stream or distinct-port setups
|
||||
|
||||
### 2. Router Mode (`--router`)
|
||||
- HTTP streams share ports via path-based routing
|
||||
- Consolidated access through URL paths
|
||||
- Global status endpoint with aggregated statistics
|
||||
- Best for multi-stream setups with limited ports
|
||||
- Streams accessible at `/{stream_name}/{path}`
|
||||
|
||||
## Testing
|
||||
|
||||
### Test Suites
|
||||
|
||||
1. **Router Testing** (`test_router.sh`)
|
||||
- Path routing verification
|
||||
- Client isolation between streams
|
||||
- Statistics aggregation
|
||||
- Graceful shutdown
|
||||
- Port conflict handling
|
||||
|
||||
2. **Rate Limiting Testing** (`test_ratelimit.sh`)
|
||||
- Per-IP rate limiting
|
||||
- Global rate limiting
|
||||
- Connection limits
|
||||
- Rate limit recovery
|
||||
- Statistics accuracy
|
||||
- Stress testing
|
||||
|
||||
3. **Filter Testing** (recommended)
|
||||
- Pattern matching accuracy
|
||||
- Include/exclude logic
|
||||
- OR/AND combination logic
|
||||
- Performance with complex patterns
|
||||
- Filter chain behavior
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Test router functionality
|
||||
./test_router.sh
|
||||
|
||||
# Test rate limiting
|
||||
./test_ratelimit.sh
|
||||
|
||||
# Run all tests
|
||||
make test
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Filter Overhead
|
||||
- Regex compilation: One-time cost at startup
|
||||
- Pattern matching: O(n*m) where n=patterns, m=text length
|
||||
- Use simple patterns when possible
|
||||
- Consider pattern order (most likely matches first)
|
||||
|
||||
### Rate Limiting Overhead
|
||||
- Token bucket checks: O(1) time complexity
|
||||
- Memory: ~100 bytes per tracked IP
|
||||
- Cleanup: Runs asynchronously every minute
|
||||
- Minimal impact when disabled
|
||||
|
||||
### Optimization Guidelines
|
||||
- Use specific patterns to reduce regex complexity
|
||||
- Place most selective filters first in chain
|
||||
- Use per-IP limiting for fairness
|
||||
- Use global limiting for resource protection
|
||||
- Set burst size to 2-3x requests_per_second
|
||||
- Monitor rate limit statistics for tuning
|
||||
- Higher check_interval_ms for low-activity logs
|
||||
|
||||
## Security Architecture
|
||||
|
||||
### Current Security Features
|
||||
- Read-only file access
|
||||
- Rate limiting for DDoS protection
|
||||
- Connection limits for resource protection
|
||||
- Non-blocking request rejection
|
||||
- Regex pattern validation at startup
|
||||
155
doc/cli.md
Normal file
155
doc/cli.md
Normal file
@ -0,0 +1,155 @@
|
||||
# Command Line Interface
|
||||
|
||||
LogWisp provides a comprehensive set of command-line options for controlling its behavior without modifying configuration files.
|
||||
|
||||
## Synopsis
|
||||
|
||||
```bash
|
||||
logwisp [options]
|
||||
```
|
||||
|
||||
## General Options
|
||||
|
||||
### `--config <path>`
|
||||
Specify the configuration file location.
|
||||
- **Default**: `~/.config/logwisp.toml`
|
||||
- **Example**: `logwisp --config /etc/logwisp/production.toml`
|
||||
|
||||
### `--router`
|
||||
Enable HTTP router mode for path-based routing of multiple streams.
|
||||
- **Default**: `false` (standalone mode)
|
||||
- **Use case**: Consolidate multiple HTTP streams on shared ports
|
||||
- **Example**: `logwisp --router`
|
||||
|
||||
### `--version`
|
||||
Display version information and exit.
|
||||
- **Example**: `logwisp --version`
|
||||
|
||||
### `--background`
|
||||
Run LogWisp as a background process.
|
||||
- **Default**: `false` (foreground mode)
|
||||
- **Example**: `logwisp --background`
|
||||
|
||||
## Logging Options
|
||||
|
||||
These options override the corresponding configuration file settings.
|
||||
|
||||
### `--log-output <mode>`
|
||||
Control where LogWisp writes its own operational logs.
|
||||
- **Values**: `file`, `stdout`, `stderr`, `both`, `none`
|
||||
- **Default**: Configured value or `stderr`
|
||||
- **Example**: `logwisp --log-output both`
|
||||
|
||||
#### Output Modes:
|
||||
- `file`: Write logs only to files
|
||||
- `stdout`: Write logs only to standard output
|
||||
- `stderr`: Write logs only to standard error
|
||||
- `both`: Write logs to both files and console
|
||||
- `none`: Disable logging (⚠️ SECURITY: Not recommended)
|
||||
|
||||
### `--log-level <level>`
|
||||
Set the minimum log level for LogWisp's operational logs.
|
||||
- **Values**: `debug`, `info`, `warn`, `error`
|
||||
- **Default**: Configured value or `info`
|
||||
- **Example**: `logwisp --log-level debug`
|
||||
|
||||
### `--log-file <path>`
|
||||
Specify the log file path when using file output.
|
||||
- **Default**: Configured value or `./logs/logwisp.log`
|
||||
- **Example**: `logwisp --log-output file --log-file /var/log/logwisp/app.log`
|
||||
|
||||
### `--log-dir <directory>`
|
||||
Specify the log directory when using file output.
|
||||
- **Default**: Configured value or `./logs`
|
||||
- **Example**: `logwisp --log-output file --log-dir /var/log/logwisp`
|
||||
|
||||
### `--log-console <target>`
|
||||
Control console output destination when using `stdout`, `stderr`, or `both` modes.
|
||||
- **Values**: `stdout`, `stderr`, `split`
|
||||
- **Default**: `stderr`
|
||||
- **Example**: `logwisp --log-output both --log-console split`
|
||||
|
||||
#### Console Targets:
|
||||
- `stdout`: All logs to standard output
|
||||
- `stderr`: All logs to standard error
|
||||
- `split`: INFO/DEBUG to stdout, WARN/ERROR to stderr (planned)
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Usage
|
||||
```bash
|
||||
# Start with default configuration
|
||||
logwisp
|
||||
|
||||
# Use a specific configuration file
|
||||
logwisp --config /etc/logwisp/production.toml
|
||||
```
|
||||
|
||||
### Development Mode
|
||||
```bash
|
||||
# Enable debug logging to console
|
||||
logwisp --log-output stderr --log-level debug
|
||||
|
||||
# Debug with file output
|
||||
logwisp --log-output both --log-level debug --log-dir ./debug-logs
|
||||
```
|
||||
|
||||
### Production Deployment
|
||||
```bash
|
||||
# File logging with info level
|
||||
logwisp --log-output file --log-dir /var/log/logwisp --log-level info
|
||||
|
||||
# Background mode with custom config
|
||||
logwisp --background --config /etc/logwisp/prod.toml
|
||||
|
||||
# Router mode for multiple services
|
||||
logwisp --router --config /etc/logwisp/services.toml
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
```bash
|
||||
# Maximum verbosity to stderr
|
||||
logwisp --log-output stderr --log-level debug
|
||||
|
||||
# Check version
|
||||
logwisp --version
|
||||
|
||||
# Test configuration without backgrounding
|
||||
logwisp --config test.toml --log-level debug
|
||||
```
|
||||
|
||||
## Priority Order
|
||||
|
||||
Configuration values are applied in the following priority order (highest to lowest):
|
||||
|
||||
1. **Command-line flags** - Explicitly specified options
|
||||
2. **Environment variables** - `LOGWISP_*` prefixed variables
|
||||
3. **Configuration file** - TOML configuration
|
||||
4. **Built-in defaults** - Hardcoded fallback values
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0`: Successful execution
|
||||
- `1`: General error (configuration, startup failure)
|
||||
- `2`: Invalid command-line arguments
|
||||
|
||||
## Signals
|
||||
|
||||
LogWisp responds to the following signals:
|
||||
|
||||
- `SIGINT` (Ctrl+C): Graceful shutdown
|
||||
- `SIGTERM`: Graceful shutdown
|
||||
- `SIGKILL`: Immediate termination (not recommended)
|
||||
|
||||
During graceful shutdown, LogWisp will:
|
||||
1. Stop accepting new connections
|
||||
2. Finish streaming to existing clients
|
||||
3. Flush all buffers
|
||||
4. Close all file handles
|
||||
5. Exit cleanly
|
||||
|
||||
## See Also
|
||||
|
||||
- [Configuration Guide](configuration.md) - Complete configuration reference
|
||||
- [Environment Variables](environment.md) - Environment variable options
|
||||
- [Router Mode](router.md) - Path-based routing details
|
||||
354
doc/configuration.md
Normal file
354
doc/configuration.md
Normal file
@ -0,0 +1,354 @@
|
||||
# Configuration Guide
|
||||
|
||||
LogWisp uses TOML format for configuration with sensible defaults for all settings.
|
||||
|
||||
## Configuration File Location
|
||||
|
||||
Default search order:
|
||||
1. Command line: `--config /path/to/config.toml`
|
||||
2. Environment: `$LOGWISP_CONFIG_FILE`
|
||||
3. User config: `~/.config/logwisp.toml`
|
||||
4. Current directory: `./logwisp.toml`
|
||||
|
||||
## Configuration Structure
|
||||
|
||||
```toml
|
||||
# Optional: LogWisp's own logging configuration
|
||||
[logging]
|
||||
output = "stderr" # file, stdout, stderr, both, none
|
||||
level = "info" # debug, info, warn, error
|
||||
|
||||
# Required: At least one stream
|
||||
[[streams]]
|
||||
name = "default" # Unique identifier
|
||||
|
||||
[streams.monitor] # Required: What to monitor
|
||||
# ... monitor settings ...
|
||||
|
||||
[streams.httpserver] # Optional: HTTP/SSE server
|
||||
# ... HTTP settings ...
|
||||
|
||||
[streams.tcpserver] # Optional: TCP server
|
||||
# ... TCP settings ...
|
||||
|
||||
[[streams.filters]] # Optional: Log filtering
|
||||
# ... filter settings ...
|
||||
```
|
||||
|
||||
## Logging Configuration
|
||||
|
||||
Controls LogWisp's operational logging (not the logs being monitored).
|
||||
|
||||
```toml
|
||||
[logging]
|
||||
output = "stderr" # Where to write LogWisp's logs
|
||||
level = "info" # Minimum log level
|
||||
|
||||
# File output settings (when output includes "file")
|
||||
[logging.file]
|
||||
directory = "./logs" # Log directory
|
||||
name = "logwisp" # Base filename
|
||||
max_size_mb = 100 # Rotate at this size
|
||||
max_total_size_mb = 1000 # Total size limit
|
||||
retention_hours = 168 # Keep for 7 days
|
||||
|
||||
# Console output settings
|
||||
[logging.console]
|
||||
target = "stderr" # stdout, stderr, split
|
||||
format = "txt" # txt or json
|
||||
```
|
||||
|
||||
## Stream Configuration
|
||||
|
||||
Each `[[streams]]` section defines an independent log monitoring pipeline.
|
||||
|
||||
### Monitor Settings
|
||||
|
||||
What files or directories to watch:
|
||||
|
||||
```toml
|
||||
[streams.monitor]
|
||||
check_interval_ms = 100 # How often to check for new entries
|
||||
|
||||
# Monitor targets (at least one required)
|
||||
targets = [
|
||||
# Watch all .log files in a directory
|
||||
{ path = "/var/log/myapp", pattern = "*.log", is_file = false },
|
||||
|
||||
# Watch a specific file
|
||||
{ path = "/var/log/app.log", is_file = true },
|
||||
|
||||
# Multiple patterns
|
||||
{ path = "/logs", pattern = "app-*.log", is_file = false },
|
||||
{ path = "/logs", pattern = "error-*.txt", is_file = false }
|
||||
]
|
||||
```
|
||||
|
||||
### HTTP Server (SSE)
|
||||
|
||||
Server-Sent Events streaming over HTTP:
|
||||
|
||||
```toml
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
buffer_size = 1000 # Per-client event buffer
|
||||
stream_path = "/stream" # SSE endpoint
|
||||
status_path = "/status" # Statistics endpoint
|
||||
|
||||
# Keep-alive heartbeat
|
||||
[streams.httpserver.heartbeat]
|
||||
enabled = true
|
||||
interval_seconds = 30
|
||||
format = "comment" # "comment" or "json"
|
||||
include_timestamp = true
|
||||
include_stats = false
|
||||
|
||||
# Rate limiting (optional)
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = false
|
||||
requests_per_second = 10.0
|
||||
burst_size = 20
|
||||
limit_by = "ip" # "ip" or "global"
|
||||
response_code = 429
|
||||
response_message = "Rate limit exceeded"
|
||||
max_connections_per_ip = 5
|
||||
max_total_connections = 100
|
||||
```
|
||||
|
||||
### TCP Server
|
||||
|
||||
Raw TCP streaming for high performance:
|
||||
|
||||
```toml
|
||||
[streams.tcpserver]
|
||||
enabled = true
|
||||
port = 9090
|
||||
buffer_size = 5000 # Larger buffer for TCP
|
||||
|
||||
# Heartbeat (always JSON format for TCP)
|
||||
[streams.tcpserver.heartbeat]
|
||||
enabled = true
|
||||
interval_seconds = 60
|
||||
include_timestamp = true
|
||||
include_stats = false
|
||||
|
||||
# Rate limiting
|
||||
[streams.tcpserver.rate_limit]
|
||||
enabled = false
|
||||
requests_per_second = 5.0
|
||||
burst_size = 10
|
||||
limit_by = "ip"
|
||||
```
|
||||
|
||||
### Filters
|
||||
|
||||
Control which log entries are streamed:
|
||||
|
||||
```toml
|
||||
# Include filter - only matching logs pass
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
logic = "or" # "or" = match any, "and" = match all
|
||||
patterns = [
|
||||
"ERROR",
|
||||
"WARN",
|
||||
"CRITICAL"
|
||||
]
|
||||
|
||||
# Exclude filter - matching logs are dropped
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = [
|
||||
"DEBUG",
|
||||
"health check"
|
||||
]
|
||||
```
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Minimal Configuration
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "simple"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "./logs", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
```
|
||||
|
||||
### Production Web Application
|
||||
|
||||
```toml
|
||||
[logging]
|
||||
output = "file"
|
||||
level = "info"
|
||||
[logging.file]
|
||||
directory = "/var/log/logwisp"
|
||||
max_size_mb = 500
|
||||
retention_hours = 336 # 14 days
|
||||
|
||||
[[streams]]
|
||||
name = "webapp"
|
||||
|
||||
[streams.monitor]
|
||||
check_interval_ms = 50
|
||||
targets = [
|
||||
{ path = "/var/log/nginx", pattern = "access.log*" },
|
||||
{ path = "/var/log/nginx", pattern = "error.log*" },
|
||||
{ path = "/var/log/myapp", pattern = "*.log" }
|
||||
]
|
||||
|
||||
# Only errors and warnings
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
logic = "or"
|
||||
patterns = [
|
||||
"\\b(ERROR|error|Error)\\b",
|
||||
"\\b(WARN|WARNING|warn|warning)\\b",
|
||||
"\\b(CRITICAL|FATAL|critical|fatal)\\b",
|
||||
"status=[4-5][0-9][0-9]" # HTTP errors
|
||||
]
|
||||
|
||||
# Exclude noise
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = [
|
||||
"/health",
|
||||
"/metrics",
|
||||
"ELB-HealthChecker"
|
||||
]
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
buffer_size = 2000
|
||||
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 25.0
|
||||
burst_size = 50
|
||||
max_connections_per_ip = 10
|
||||
```
|
||||
|
||||
### Multi-Service with Router
|
||||
|
||||
```toml
|
||||
# Run with: logwisp --router
|
||||
|
||||
# Service 1: API
|
||||
[[streams]]
|
||||
name = "api"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/api", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080 # All streams can use same port in router mode
|
||||
|
||||
# Service 2: Database
|
||||
[[streams]]
|
||||
name = "database"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/postgresql", pattern = "*.log" }]
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = ["ERROR", "FATAL", "deadlock", "timeout"]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
|
||||
# Service 3: System
|
||||
[[streams]]
|
||||
name = "system"
|
||||
[streams.monitor]
|
||||
targets = [
|
||||
{ path = "/var/log/syslog", is_file = true },
|
||||
{ path = "/var/log/auth.log", is_file = true }
|
||||
]
|
||||
[streams.tcpserver]
|
||||
enabled = true
|
||||
port = 9090
|
||||
```
|
||||
|
||||
### High-Security Configuration
|
||||
|
||||
```toml
|
||||
[logging]
|
||||
output = "file"
|
||||
level = "warn" # Less verbose
|
||||
|
||||
[[streams]]
|
||||
name = "secure"
|
||||
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/secure", pattern = "*.log" }]
|
||||
|
||||
# Only security events
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = [
|
||||
"auth",
|
||||
"sudo",
|
||||
"ssh",
|
||||
"login",
|
||||
"failed",
|
||||
"denied"
|
||||
]
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8443
|
||||
|
||||
# Strict rate limiting
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 2.0
|
||||
burst_size = 3
|
||||
limit_by = "ip"
|
||||
max_connections_per_ip = 1
|
||||
response_code = 403 # Forbidden instead of 429
|
||||
|
||||
# Future: Authentication
|
||||
# [streams.auth]
|
||||
# type = "basic"
|
||||
# [streams.auth.basic_auth]
|
||||
# users_file = "/etc/logwisp/users.htpasswd"
|
||||
```
|
||||
|
||||
## Configuration Tips
|
||||
|
||||
### Performance Tuning
|
||||
|
||||
- **check_interval_ms**: Higher values reduce CPU usage
|
||||
- **buffer_size**: Larger buffers handle bursts better
|
||||
- **rate_limit**: Essential for public-facing streams
|
||||
|
||||
### Filter Patterns
|
||||
|
||||
- Use word boundaries: `\\berror\\b` (won't match "errorCode")
|
||||
- Case-insensitive: `(?i)error`
|
||||
- Anchors for speed: `^ERROR` faster than `ERROR`
|
||||
- Test complex patterns before deployment
|
||||
|
||||
### Resource Limits
|
||||
|
||||
- Each stream uses ~10-50MB RAM (depending on buffers)
|
||||
- CPU usage scales with check_interval and file activity
|
||||
- Network bandwidth depends on log volume and client count
|
||||
|
||||
## Validation
|
||||
|
||||
LogWisp validates configuration on startup:
|
||||
- Required fields (name, monitor targets)
|
||||
- Port conflicts between streams
|
||||
- Pattern syntax for filters
|
||||
- Path accessibility
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](environment.md) - Override via environment
|
||||
- [CLI Options](cli.md) - Override via command line
|
||||
- [Filter Guide](filters.md) - Advanced filtering patterns
|
||||
- [Examples](examples/) - Ready-to-use configurations
|
||||
275
doc/environment.md
Normal file
275
doc/environment.md
Normal file
@ -0,0 +1,275 @@
|
||||
# Environment Variables
|
||||
|
||||
LogWisp supports comprehensive configuration through environment variables, allowing deployment without configuration files or dynamic overrides in containerized environments.
|
||||
|
||||
## Naming Convention
|
||||
|
||||
Environment variables follow a structured pattern:
|
||||
- **Prefix**: `LOGWISP_`
|
||||
- **Path separator**: `_` (underscore)
|
||||
- **Array indices**: Numeric suffix (0-based)
|
||||
- **Case**: UPPERCASE
|
||||
|
||||
### Examples:
|
||||
- Config file setting: `logging.level = "debug"`
|
||||
- Environment variable: `LOGWISP_LOGGING_LEVEL=debug`
|
||||
|
||||
- Array element: `streams[0].name = "app"`
|
||||
- Environment variable: `LOGWISP_STREAMS_0_NAME=app`
|
||||
|
||||
## General Variables
|
||||
|
||||
### `LOGWISP_CONFIG_FILE`
|
||||
Path to the configuration file.
|
||||
- **Default**: `~/.config/logwisp.toml`
|
||||
- **Example**: `LOGWISP_CONFIG_FILE=/etc/logwisp/config.toml`
|
||||
|
||||
### `LOGWISP_CONFIG_DIR`
|
||||
Directory containing configuration files.
|
||||
- **Usage**: Combined with `LOGWISP_CONFIG_FILE` for relative paths
|
||||
- **Example**:
|
||||
```bash
|
||||
export LOGWISP_CONFIG_DIR=/etc/logwisp
|
||||
export LOGWISP_CONFIG_FILE=production.toml
|
||||
# Loads: /etc/logwisp/production.toml
|
||||
```
|
||||
|
||||
### `LOGWISP_DISABLE_STATUS_REPORTER`
|
||||
Disable the periodic status reporter.
|
||||
- **Values**: `1` (disable), `0` or unset (enable)
|
||||
- **Default**: `0` (enabled)
|
||||
- **Example**: `LOGWISP_DISABLE_STATUS_REPORTER=1`
|
||||
|
||||
### `LOGWISP_BACKGROUND`
|
||||
Internal marker for background process detection.
|
||||
- **Note**: Set automatically by `--background` flag
|
||||
- **Values**: `1` (background), unset (foreground)
|
||||
|
||||
## Logging Variables
|
||||
|
||||
### `LOGWISP_LOGGING_OUTPUT`
|
||||
LogWisp's operational log output mode.
|
||||
- **Values**: `file`, `stdout`, `stderr`, `both`, `none`
|
||||
- **Example**: `LOGWISP_LOGGING_OUTPUT=both`
|
||||
|
||||
### `LOGWISP_LOGGING_LEVEL`
|
||||
Minimum log level for operational logs.
|
||||
- **Values**: `debug`, `info`, `warn`, `error`
|
||||
- **Example**: `LOGWISP_LOGGING_LEVEL=debug`
|
||||
|
||||
### File Logging
|
||||
```bash
|
||||
LOGWISP_LOGGING_FILE_DIRECTORY=/var/log/logwisp
|
||||
LOGWISP_LOGGING_FILE_NAME=logwisp
|
||||
LOGWISP_LOGGING_FILE_MAX_SIZE_MB=100
|
||||
LOGWISP_LOGGING_FILE_MAX_TOTAL_SIZE_MB=1000
|
||||
LOGWISP_LOGGING_FILE_RETENTION_HOURS=168 # 7 days
|
||||
```
|
||||
|
||||
### Console Logging
|
||||
```bash
|
||||
LOGWISP_LOGGING_CONSOLE_TARGET=stderr # stdout, stderr, split
|
||||
LOGWISP_LOGGING_CONSOLE_FORMAT=txt # txt, json
|
||||
```
|
||||
|
||||
## Stream Configuration
|
||||
|
||||
Streams are configured using array indices (0-based).
|
||||
|
||||
### Basic Stream Settings
|
||||
```bash
|
||||
# First stream (index 0)
|
||||
LOGWISP_STREAMS_0_NAME=app
|
||||
LOGWISP_STREAMS_0_MONITOR_CHECK_INTERVAL_MS=100
|
||||
|
||||
# Second stream (index 1)
|
||||
LOGWISP_STREAMS_1_NAME=system
|
||||
LOGWISP_STREAMS_1_MONITOR_CHECK_INTERVAL_MS=1000
|
||||
```
|
||||
|
||||
### Monitor Targets
|
||||
```bash
|
||||
# Single file target
|
||||
LOGWISP_STREAMS_0_MONITOR_TARGETS_0_PATH=/var/log/app.log
|
||||
LOGWISP_STREAMS_0_MONITOR_TARGETS_0_IS_FILE=true
|
||||
|
||||
# Directory with pattern
|
||||
LOGWISP_STREAMS_0_MONITOR_TARGETS_1_PATH=/var/log/myapp
|
||||
LOGWISP_STREAMS_0_MONITOR_TARGETS_1_PATTERN="*.log"
|
||||
LOGWISP_STREAMS_0_MONITOR_TARGETS_1_IS_FILE=false
|
||||
```
|
||||
|
||||
### Filters
|
||||
```bash
|
||||
# Include filter
|
||||
LOGWISP_STREAMS_0_FILTERS_0_TYPE=include
|
||||
LOGWISP_STREAMS_0_FILTERS_0_LOGIC=or
|
||||
LOGWISP_STREAMS_0_FILTERS_0_PATTERNS='["ERROR","WARN","CRITICAL"]'
|
||||
|
||||
# Exclude filter
|
||||
LOGWISP_STREAMS_0_FILTERS_1_TYPE=exclude
|
||||
LOGWISP_STREAMS_0_FILTERS_1_PATTERNS='["DEBUG","TRACE"]'
|
||||
```
|
||||
|
||||
### HTTP Server
|
||||
```bash
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_ENABLED=true
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_PORT=8080
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_BUFFER_SIZE=1000
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_STREAM_PATH=/stream
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_STATUS_PATH=/status
|
||||
|
||||
# Heartbeat
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_HEARTBEAT_ENABLED=true
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_HEARTBEAT_INTERVAL_SECONDS=30
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_HEARTBEAT_FORMAT=comment
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_HEARTBEAT_INCLUDE_TIMESTAMP=true
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_HEARTBEAT_INCLUDE_STATS=false
|
||||
|
||||
# Rate Limiting
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_ENABLED=true
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_REQUESTS_PER_SECOND=10.0
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_BURST_SIZE=20
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_LIMIT_BY=ip
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_MAX_CONNECTIONS_PER_IP=5
|
||||
```
|
||||
|
||||
### TCP Server
|
||||
```bash
|
||||
LOGWISP_STREAMS_0_TCPSERVER_ENABLED=true
|
||||
LOGWISP_STREAMS_0_TCPSERVER_PORT=9090
|
||||
LOGWISP_STREAMS_0_TCPSERVER_BUFFER_SIZE=5000
|
||||
|
||||
# Rate Limiting
|
||||
LOGWISP_STREAMS_0_TCPSERVER_RATE_LIMIT_ENABLED=true
|
||||
LOGWISP_STREAMS_0_TCPSERVER_RATE_LIMIT_REQUESTS_PER_SECOND=5.0
|
||||
LOGWISP_STREAMS_0_TCPSERVER_RATE_LIMIT_BURST_SIZE=10
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
Here's a complete example configuring two streams via environment variables:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Logging configuration
|
||||
export LOGWISP_LOGGING_OUTPUT=both
|
||||
export LOGWISP_LOGGING_LEVEL=info
|
||||
export LOGWISP_LOGGING_FILE_DIRECTORY=/var/log/logwisp
|
||||
export LOGWISP_LOGGING_FILE_MAX_SIZE_MB=100
|
||||
|
||||
# Stream 0: Application logs
|
||||
export LOGWISP_STREAMS_0_NAME=app
|
||||
export LOGWISP_STREAMS_0_MONITOR_CHECK_INTERVAL_MS=50
|
||||
export LOGWISP_STREAMS_0_MONITOR_TARGETS_0_PATH=/var/log/myapp
|
||||
export LOGWISP_STREAMS_0_MONITOR_TARGETS_0_PATTERN="*.log"
|
||||
export LOGWISP_STREAMS_0_MONITOR_TARGETS_0_IS_FILE=false
|
||||
|
||||
# Stream 0: Filters
|
||||
export LOGWISP_STREAMS_0_FILTERS_0_TYPE=include
|
||||
export LOGWISP_STREAMS_0_FILTERS_0_PATTERNS='["ERROR","WARN"]'
|
||||
|
||||
# Stream 0: HTTP server
|
||||
export LOGWISP_STREAMS_0_HTTPSERVER_ENABLED=true
|
||||
export LOGWISP_STREAMS_0_HTTPSERVER_PORT=8080
|
||||
export LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_ENABLED=true
|
||||
export LOGWISP_STREAMS_0_HTTPSERVER_RATE_LIMIT_REQUESTS_PER_SECOND=25.0
|
||||
|
||||
# Stream 1: System logs
|
||||
export LOGWISP_STREAMS_1_NAME=system
|
||||
export LOGWISP_STREAMS_1_MONITOR_CHECK_INTERVAL_MS=1000
|
||||
export LOGWISP_STREAMS_1_MONITOR_TARGETS_0_PATH=/var/log/syslog
|
||||
export LOGWISP_STREAMS_1_MONITOR_TARGETS_0_IS_FILE=true
|
||||
|
||||
# Stream 1: TCP server
|
||||
export LOGWISP_STREAMS_1_TCPSERVER_ENABLED=true
|
||||
export LOGWISP_STREAMS_1_TCPSERVER_PORT=9090
|
||||
|
||||
# Start LogWisp
|
||||
logwisp
|
||||
```
|
||||
|
||||
## Docker/Kubernetes Usage
|
||||
|
||||
Environment variables are ideal for containerized deployments:
|
||||
|
||||
### Docker
|
||||
```dockerfile
|
||||
FROM logwisp:latest
|
||||
ENV LOGWISP_LOGGING_OUTPUT=stdout
|
||||
ENV LOGWISP_STREAMS_0_NAME=container
|
||||
ENV LOGWISP_STREAMS_0_MONITOR_TARGETS_0_PATH=/var/log/app
|
||||
ENV LOGWISP_STREAMS_0_HTTPSERVER_PORT=8080
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
logwisp:
|
||||
image: logwisp:latest
|
||||
environment:
|
||||
- LOGWISP_LOGGING_OUTPUT=stdout
|
||||
- LOGWISP_STREAMS_0_NAME=webapp
|
||||
- LOGWISP_STREAMS_0_MONITOR_TARGETS_0_PATH=/logs
|
||||
- LOGWISP_STREAMS_0_HTTPSERVER_PORT=8080
|
||||
volumes:
|
||||
- ./logs:/logs:ro
|
||||
ports:
|
||||
- "8080:8080"
|
||||
```
|
||||
|
||||
### Kubernetes ConfigMap
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: logwisp-config
|
||||
data:
|
||||
LOGWISP_LOGGING_LEVEL: "info"
|
||||
LOGWISP_STREAMS_0_NAME: "k8s-app"
|
||||
LOGWISP_STREAMS_0_HTTPSERVER_PORT: "8080"
|
||||
```
|
||||
|
||||
## Precedence Rules
|
||||
|
||||
When the same setting is configured multiple ways, this precedence applies:
|
||||
|
||||
1. **Command-line flags** (highest priority)
|
||||
2. **Environment variables**
|
||||
3. **Configuration file**
|
||||
4. **Default values** (lowest priority)
|
||||
|
||||
Example:
|
||||
```bash
|
||||
# Config file has: logging.level = "info"
|
||||
export LOGWISP_LOGGING_LEVEL=warn
|
||||
logwisp --log-level debug
|
||||
|
||||
# Result: log level will be "debug" (CLI flag wins)
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
To see which environment variables LogWisp recognizes:
|
||||
```bash
|
||||
# List all LOGWISP variables
|
||||
env | grep ^LOGWISP_
|
||||
|
||||
# Test configuration parsing
|
||||
LOGWISP_LOGGING_LEVEL=debug logwisp --version
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- **Sensitive Values**: Avoid putting passwords or tokens in environment variables
|
||||
- **Process Visibility**: Environment variables may be visible to other processes
|
||||
- **Container Security**: Use secrets management for sensitive configuration
|
||||
- **Logging**: Be careful not to log environment variable values
|
||||
|
||||
## See Also
|
||||
|
||||
- [Configuration Guide](configuration.md) - Complete configuration reference
|
||||
- [CLI Options](cli.md) - Command-line interface
|
||||
- [Docker Deployment](integrations.md#docker) - Container-specific guidance
|
||||
439
doc/filters.md
Normal file
439
doc/filters.md
Normal file
@ -0,0 +1,439 @@
|
||||
# Filter Guide
|
||||
|
||||
LogWisp's filtering system allows you to control which log entries are streamed to clients, reducing noise and focusing on what matters.
|
||||
|
||||
## How Filters Work
|
||||
|
||||
Filters use regular expressions to match log entries. Each filter can either:
|
||||
- **Include**: Only matching logs pass through (whitelist)
|
||||
- **Exclude**: Matching logs are dropped (blacklist)
|
||||
|
||||
Multiple filters are applied sequentially - a log entry must pass ALL filters to be streamed.
|
||||
|
||||
## Filter Configuration
|
||||
|
||||
### Basic Structure
|
||||
|
||||
```toml
|
||||
[[streams.filters]]
|
||||
type = "include" # or "exclude"
|
||||
logic = "or" # or "and"
|
||||
patterns = [
|
||||
"pattern1",
|
||||
"pattern2"
|
||||
]
|
||||
```
|
||||
|
||||
### Filter Types
|
||||
|
||||
#### Include Filter (Whitelist)
|
||||
Only logs matching the patterns are streamed:
|
||||
|
||||
```toml
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
logic = "or"
|
||||
patterns = [
|
||||
"ERROR",
|
||||
"WARN",
|
||||
"CRITICAL"
|
||||
]
|
||||
# Result: Only ERROR, WARN, or CRITICAL logs are streamed
|
||||
```
|
||||
|
||||
#### Exclude Filter (Blacklist)
|
||||
Logs matching the patterns are dropped:
|
||||
|
||||
```toml
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = [
|
||||
"DEBUG",
|
||||
"TRACE",
|
||||
"/health"
|
||||
]
|
||||
# Result: DEBUG, TRACE, and health check logs are filtered out
|
||||
```
|
||||
|
||||
### Logic Operators
|
||||
|
||||
#### OR Logic (Default)
|
||||
Log matches if ANY pattern matches:
|
||||
|
||||
```toml
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
logic = "or"
|
||||
patterns = ["ERROR", "FAIL", "EXCEPTION"]
|
||||
# Matches: "ERROR: disk full" OR "FAIL: connection timeout" OR "NullPointerException"
|
||||
```
|
||||
|
||||
#### AND Logic
|
||||
Log matches only if ALL patterns match:
|
||||
|
||||
```toml
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
logic = "and"
|
||||
patterns = ["database", "timeout", "ERROR"]
|
||||
# Matches: "ERROR: database connection timeout"
|
||||
# Doesn't match: "ERROR: file not found" (missing "database" and "timeout")
|
||||
```
|
||||
|
||||
## Pattern Syntax
|
||||
|
||||
LogWisp uses Go's regular expression syntax (RE2):
|
||||
|
||||
### Basic Patterns
|
||||
|
||||
```toml
|
||||
patterns = [
|
||||
"ERROR", # Exact substring match
|
||||
"(?i)error", # Case-insensitive
|
||||
"\\berror\\b", # Word boundaries
|
||||
"^ERROR", # Start of line
|
||||
"ERROR$", # End of line
|
||||
"ERR(OR)?", # Optional group
|
||||
"error|fail|exception" # Alternatives
|
||||
]
|
||||
```
|
||||
|
||||
### Common Pattern Examples
|
||||
|
||||
#### Log Levels
|
||||
```toml
|
||||
# Standard log levels
|
||||
patterns = [
|
||||
"\\[(ERROR|WARN|INFO|DEBUG)\\]", # [ERROR] format
|
||||
"(?i)\\b(error|warning|info|debug)\\b", # Word boundaries
|
||||
"level=(error|warn|info|debug)", # key=value format
|
||||
"<(Error|Warning|Info|Debug)>" # XML-style
|
||||
]
|
||||
|
||||
# Severity patterns
|
||||
patterns = [
|
||||
"(?i)(fatal|critical|severe)",
|
||||
"(?i)(error|fail|exception)",
|
||||
"(?i)(warn|warning|caution)",
|
||||
"panic:", # Go panics
|
||||
"Traceback", # Python errors
|
||||
]
|
||||
```
|
||||
|
||||
#### Application Errors
|
||||
```toml
|
||||
# Java/JVM
|
||||
patterns = [
|
||||
"Exception",
|
||||
"\\.java:[0-9]+", # Stack trace lines
|
||||
"at com\\.mycompany\\.", # Company packages
|
||||
"NullPointerException|ClassNotFoundException"
|
||||
]
|
||||
|
||||
# Python
|
||||
patterns = [
|
||||
"Traceback \\(most recent call last\\)",
|
||||
"File \".+\\.py\", line [0-9]+",
|
||||
"(ValueError|TypeError|KeyError)"
|
||||
]
|
||||
|
||||
# Go
|
||||
patterns = [
|
||||
"panic:",
|
||||
"goroutine [0-9]+",
|
||||
"runtime error:"
|
||||
]
|
||||
|
||||
# Node.js
|
||||
patterns = [
|
||||
"Error:",
|
||||
"at .+ \\(.+\\.js:[0-9]+:[0-9]+\\)",
|
||||
"UnhandledPromiseRejection"
|
||||
]
|
||||
```
|
||||
|
||||
#### Performance Issues
|
||||
```toml
|
||||
patterns = [
|
||||
"took [0-9]{4,}ms", # Operations over 999ms
|
||||
"duration>[0-9]{3,}s", # Long durations
|
||||
"timeout|timed out", # Timeouts
|
||||
"slow query", # Database
|
||||
"memory pressure", # Memory issues
|
||||
"high cpu|cpu usage: [8-9][0-9]%" # CPU issues
|
||||
]
|
||||
```
|
||||
|
||||
#### Security Patterns
|
||||
```toml
|
||||
patterns = [
|
||||
"(?i)(unauthorized|forbidden|denied)",
|
||||
"(?i)(auth|authentication) fail",
|
||||
"invalid (token|session|credentials)",
|
||||
"SQL injection|XSS|CSRF",
|
||||
"brute force|rate limit",
|
||||
"suspicious activity"
|
||||
]
|
||||
```
|
||||
|
||||
#### HTTP Patterns
|
||||
```toml
|
||||
# Error status codes
|
||||
patterns = [
|
||||
"status[=:][4-5][0-9]{2}", # status=404, status:500
|
||||
"HTTP/[0-9.]+ [4-5][0-9]{2}", # HTTP/1.1 404
|
||||
"\"status\":\\s*[4-5][0-9]{2}" # JSON "status": 500
|
||||
]
|
||||
|
||||
# Specific endpoints
|
||||
patterns = [
|
||||
"\"(GET|POST|PUT|DELETE) /api/",
|
||||
"/api/v[0-9]+/users",
|
||||
"path=\"/admin"
|
||||
]
|
||||
```
|
||||
|
||||
## Filter Chains
|
||||
|
||||
Multiple filters create a processing chain. Each filter must pass for the log to be streamed.
|
||||
|
||||
### Example: Error Monitoring
|
||||
```toml
|
||||
# Step 1: Include only errors and warnings
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
logic = "or"
|
||||
patterns = [
|
||||
"(?i)\\b(error|fail|exception)\\b",
|
||||
"(?i)\\b(warn|warning)\\b",
|
||||
"(?i)\\b(critical|fatal|severe)\\b"
|
||||
]
|
||||
|
||||
# Step 2: Exclude known non-issues
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = [
|
||||
"Error: Expected behavior",
|
||||
"Warning: Deprecated API",
|
||||
"INFO.*error in message" # INFO logs talking about errors
|
||||
]
|
||||
|
||||
# Step 3: Exclude noisy sources
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = [
|
||||
"/health",
|
||||
"/metrics",
|
||||
"ELB-HealthChecker",
|
||||
"Googlebot"
|
||||
]
|
||||
```
|
||||
|
||||
### Example: API Monitoring
|
||||
```toml
|
||||
# Include only API calls
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = [
|
||||
"/api/",
|
||||
"/v[0-9]+/"
|
||||
]
|
||||
|
||||
# Exclude successful requests
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = [
|
||||
"\" 200 ", # HTTP 200 OK
|
||||
"\" 201 ", # HTTP 201 Created
|
||||
"\" 204 ", # HTTP 204 No Content
|
||||
"\" 304 " # HTTP 304 Not Modified
|
||||
]
|
||||
|
||||
# Exclude OPTIONS requests (CORS)
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = [
|
||||
"OPTIONS "
|
||||
]
|
||||
```
|
||||
|
||||
### Example: Security Audit
|
||||
```toml
|
||||
# Include security-relevant events
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
logic = "or"
|
||||
patterns = [
|
||||
"(?i)auth",
|
||||
"(?i)login|logout",
|
||||
"(?i)sudo|root",
|
||||
"(?i)ssh|sftp|ftp",
|
||||
"(?i)firewall|iptables",
|
||||
"COMMAND=", # sudo commands
|
||||
"USER=", # user actions
|
||||
"SELINUX"
|
||||
]
|
||||
|
||||
# Must also contain failure/success indicators
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
logic = "or"
|
||||
patterns = [
|
||||
"(?i)(fail|denied|error)",
|
||||
"(?i)(success|accepted|granted)",
|
||||
"(?i)(invalid|unauthorized)"
|
||||
]
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Pattern Complexity
|
||||
|
||||
Simple patterns are fast (~1μs per check):
|
||||
```toml
|
||||
patterns = ["ERROR", "WARN", "FATAL"]
|
||||
```
|
||||
|
||||
Complex patterns are slower (~10-100μs per check):
|
||||
```toml
|
||||
patterns = [
|
||||
"^\\[\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\]\\s+\\[(ERROR|WARN)\\]\\s+\\[([^\\]]+)\\]\\s+(.+)$"
|
||||
]
|
||||
```
|
||||
|
||||
### Optimization Tips
|
||||
|
||||
1. **Use anchors when possible**:
|
||||
```toml
|
||||
"^ERROR" # Faster than "ERROR"
|
||||
```
|
||||
|
||||
2. **Avoid nested quantifiers**:
|
||||
```toml
|
||||
# BAD: Can cause exponential backtracking
|
||||
"((a+)+)+"
|
||||
|
||||
# GOOD: Linear time
|
||||
"a+"
|
||||
```
|
||||
|
||||
3. **Use non-capturing groups**:
|
||||
```toml
|
||||
"(?:error|warn)" # Instead of "(error|warn)"
|
||||
```
|
||||
|
||||
4. **Order patterns by frequency**:
|
||||
```toml
|
||||
# Most common first
|
||||
patterns = ["ERROR", "WARN", "INFO", "DEBUG"]
|
||||
```
|
||||
|
||||
5. **Prefer character classes**:
|
||||
```toml
|
||||
"[0-9]" # Instead of "\\d"
|
||||
"[a-zA-Z]" # Instead of "\\w"
|
||||
```
|
||||
|
||||
## Testing Filters
|
||||
|
||||
### Test Configuration
|
||||
Create a test configuration with sample logs:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "test"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "./test-logs", pattern = "*.log" }]
|
||||
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = ["YOUR_PATTERN_HERE"]
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8888
|
||||
```
|
||||
|
||||
### Generate Test Logs
|
||||
```bash
|
||||
# Create test log entries
|
||||
echo "[ERROR] Database connection failed" >> test-logs/app.log
|
||||
echo "[INFO] User logged in" >> test-logs/app.log
|
||||
echo "[WARN] High memory usage: 85%" >> test-logs/app.log
|
||||
|
||||
# Run LogWisp with debug logging
|
||||
logwisp --config test.toml --log-level debug
|
||||
|
||||
# Check what passes through
|
||||
curl -N http://localhost:8888/stream
|
||||
```
|
||||
|
||||
### Debug Filter Behavior
|
||||
Enable debug logging to see filter decisions:
|
||||
|
||||
```bash
|
||||
logwisp --log-level debug --log-output stderr
|
||||
```
|
||||
|
||||
Look for messages like:
|
||||
```
|
||||
Entry filtered out component=filter_chain filter_index=0 filter_type=include
|
||||
Entry passed all filters component=filter_chain
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Case Sensitivity
|
||||
By default, patterns are case-sensitive:
|
||||
```toml
|
||||
# Won't match "error" or "Error"
|
||||
patterns = ["ERROR"]
|
||||
|
||||
# Use case-insensitive flag
|
||||
patterns = ["(?i)error"]
|
||||
```
|
||||
|
||||
### Partial Matches
|
||||
Patterns match substrings by default:
|
||||
```toml
|
||||
# Matches "ERROR", "ERRORS", "TERROR"
|
||||
patterns = ["ERROR"]
|
||||
|
||||
# Use word boundaries for exact words
|
||||
patterns = ["\\bERROR\\b"]
|
||||
```
|
||||
|
||||
### Special Characters
|
||||
Remember to escape regex special characters:
|
||||
```toml
|
||||
# Won't work as expected
|
||||
patterns = ["[ERROR]"]
|
||||
|
||||
# Correct: escape brackets
|
||||
patterns = ["\\[ERROR\\]"]
|
||||
```
|
||||
|
||||
### Performance Impact
|
||||
Too many complex patterns can impact performance:
|
||||
```toml
|
||||
# Consider splitting into multiple streams instead
|
||||
[[streams.filters]]
|
||||
patterns = [
|
||||
# 50+ complex patterns...
|
||||
]
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Start Simple**: Begin with basic patterns and refine as needed
|
||||
2. **Test Thoroughly**: Use test logs to verify filter behavior
|
||||
3. **Monitor Performance**: Check filter statistics in `/status`
|
||||
4. **Document Patterns**: Comment complex patterns for maintenance
|
||||
5. **Use Multiple Streams**: Instead of complex filters, consider separate streams
|
||||
6. **Regular Review**: Periodically review and optimize filter rules
|
||||
|
||||
## See Also
|
||||
|
||||
- [Configuration Guide](configuration.md) - Complete configuration reference
|
||||
- [Performance Tuning](performance.md) - Optimization guidelines
|
||||
- [Examples](examples/) - Real-world filter configurations
|
||||
591
doc/installation.md
Normal file
591
doc/installation.md
Normal file
@ -0,0 +1,591 @@
|
||||
# Installation Guide
|
||||
|
||||
This guide covers installing LogWisp on various platforms and deployment scenarios.
|
||||
|
||||
## Requirements
|
||||
|
||||
### System Requirements
|
||||
|
||||
- **OS**: Linux, macOS, FreeBSD, Windows (with WSL)
|
||||
- **Architecture**: amd64, arm64
|
||||
- **Memory**: 64MB minimum, 256MB recommended
|
||||
- **Disk**: 10MB for binary, plus log storage
|
||||
- **Go**: 1.23+ (for building from source)
|
||||
|
||||
### Runtime Dependencies
|
||||
|
||||
LogWisp is a single static binary with no runtime dependencies. It only requires:
|
||||
- Read access to monitored log files
|
||||
- Network access for serving streams
|
||||
- Write access for operational logs (optional)
|
||||
|
||||
## Installation Methods
|
||||
|
||||
### Pre-built Binaries
|
||||
|
||||
Download the latest release:
|
||||
|
||||
```bash
|
||||
# Linux (amd64)
|
||||
wget https://github.com/yourusername/logwisp/releases/latest/download/logwisp-linux-amd64
|
||||
chmod +x logwisp-linux-amd64
|
||||
sudo mv logwisp-linux-amd64 /usr/local/bin/logwisp
|
||||
|
||||
# macOS (Intel)
|
||||
wget https://github.com/yourusername/logwisp/releases/latest/download/logwisp-darwin-amd64
|
||||
chmod +x logwisp-darwin-amd64
|
||||
sudo mv logwisp-darwin-amd64 /usr/local/bin/logwisp
|
||||
|
||||
# macOS (Apple Silicon)
|
||||
wget https://github.com/yourusername/logwisp/releases/latest/download/logwisp-darwin-arm64
|
||||
chmod +x logwisp-darwin-arm64
|
||||
sudo mv logwisp-darwin-arm64 /usr/local/bin/logwisp
|
||||
```
|
||||
|
||||
Verify installation:
|
||||
```bash
|
||||
logwisp --version
|
||||
```
|
||||
|
||||
### From Source
|
||||
|
||||
Build from source code:
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/yourusername/logwisp.git
|
||||
cd logwisp
|
||||
|
||||
# Build
|
||||
make build
|
||||
|
||||
# Install
|
||||
sudo make install
|
||||
|
||||
# Or install to custom location
|
||||
make install PREFIX=/opt/logwisp
|
||||
```
|
||||
|
||||
### Using Go Install
|
||||
|
||||
Install directly with Go:
|
||||
|
||||
```bash
|
||||
go install github.com/yourusername/logwisp/src/cmd/logwisp@latest
|
||||
```
|
||||
|
||||
Note: This installs to `$GOPATH/bin` (usually `~/go/bin`)
|
||||
|
||||
### Docker
|
||||
|
||||
Official Docker image:
|
||||
|
||||
```bash
|
||||
# Pull image
|
||||
docker pull yourusername/logwisp:latest
|
||||
|
||||
# Run with volume mount
|
||||
docker run -d \
|
||||
--name logwisp \
|
||||
-p 8080:8080 \
|
||||
-v /var/log:/logs:ro \
|
||||
-v $PWD/config.toml:/config/logwisp.toml:ro \
|
||||
yourusername/logwisp:latest \
|
||||
--config /config/logwisp.toml
|
||||
```
|
||||
|
||||
Build your own image:
|
||||
|
||||
```dockerfile
|
||||
FROM golang:1.23-alpine AS builder
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
RUN go build -o logwisp ./src/cmd/logwisp
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk --no-cache add ca-certificates
|
||||
COPY --from=builder /build/logwisp /usr/local/bin/
|
||||
ENTRYPOINT ["logwisp"]
|
||||
```
|
||||
|
||||
## Platform-Specific Instructions
|
||||
|
||||
### Linux
|
||||
|
||||
#### Debian/Ubuntu
|
||||
|
||||
Create package (planned):
|
||||
```bash
|
||||
# Future feature
|
||||
sudo apt install logwisp
|
||||
```
|
||||
|
||||
Manual installation:
|
||||
```bash
|
||||
# Download binary
|
||||
wget https://github.com/yourusername/logwisp/releases/latest/download/logwisp-linux-amd64 -O logwisp
|
||||
chmod +x logwisp
|
||||
sudo mv logwisp /usr/local/bin/
|
||||
|
||||
# Create config directory
|
||||
sudo mkdir -p /etc/logwisp
|
||||
sudo cp config/logwisp.toml.example /etc/logwisp/logwisp.toml
|
||||
|
||||
# Create systemd service
|
||||
sudo tee /etc/systemd/system/logwisp.service << EOF
|
||||
[Unit]
|
||||
Description=LogWisp Log Monitoring Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=logwisp
|
||||
Group=logwisp
|
||||
ExecStart=/usr/local/bin/logwisp --config /etc/logwisp/logwisp.toml
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=logwisp
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadOnlyPaths=/var/log
|
||||
ReadWritePaths=/var/log/logwisp
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Create user
|
||||
sudo useradd -r -s /bin/false logwisp
|
||||
|
||||
# Create log directory
|
||||
sudo mkdir -p /var/log/logwisp
|
||||
sudo chown logwisp:logwisp /var/log/logwisp
|
||||
|
||||
# Enable and start
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable logwisp
|
||||
sudo systemctl start logwisp
|
||||
```
|
||||
|
||||
#### Red Hat/CentOS/Fedora
|
||||
|
||||
```bash
|
||||
# Similar to Debian, but use:
|
||||
sudo yum install wget # or dnf on newer versions
|
||||
|
||||
# SELinux context (if enabled)
|
||||
sudo semanage fcontext -a -t bin_t /usr/local/bin/logwisp
|
||||
sudo restorecon -v /usr/local/bin/logwisp
|
||||
```
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
AUR package (community maintained):
|
||||
```bash
|
||||
# Future feature
|
||||
yay -S logwisp
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
#### Homebrew
|
||||
|
||||
Formula (planned):
|
||||
```bash
|
||||
# Future feature
|
||||
brew install logwisp
|
||||
```
|
||||
|
||||
#### Manual Installation
|
||||
|
||||
```bash
|
||||
# Download and install
|
||||
curl -L https://github.com/yourusername/logwisp/releases/latest/download/logwisp-darwin-$(uname -m) -o logwisp
|
||||
chmod +x logwisp
|
||||
sudo mv logwisp /usr/local/bin/
|
||||
|
||||
# Create LaunchDaemon
|
||||
sudo tee /Library/LaunchDaemons/com.logwisp.plist << EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.logwisp</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/logwisp</string>
|
||||
<string>--config</string>
|
||||
<string>/usr/local/etc/logwisp/logwisp.toml</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/usr/local/var/log/logwisp.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/usr/local/var/log/logwisp.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
# Load service
|
||||
sudo launchctl load /Library/LaunchDaemons/com.logwisp.plist
|
||||
```
|
||||
|
||||
### FreeBSD
|
||||
|
||||
#### Ports
|
||||
|
||||
```bash
|
||||
# Future feature
|
||||
cd /usr/ports/sysutils/logwisp
|
||||
make install clean
|
||||
```
|
||||
|
||||
#### Manual Installation
|
||||
|
||||
```bash
|
||||
# Download
|
||||
fetch https://github.com/yourusername/logwisp/releases/latest/download/logwisp-freebsd-amd64
|
||||
chmod +x logwisp-freebsd-amd64
|
||||
mv logwisp-freebsd-amd64 /usr/local/bin/logwisp
|
||||
|
||||
# RC script
|
||||
cat > /usr/local/etc/rc.d/logwisp << 'EOF'
|
||||
#!/bin/sh
|
||||
|
||||
# PROVIDE: logwisp
|
||||
# REQUIRE: DAEMON
|
||||
# KEYWORD: shutdown
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="logwisp"
|
||||
rcvar="${name}_enable"
|
||||
command="/usr/local/bin/logwisp"
|
||||
command_args="--config /usr/local/etc/logwisp/logwisp.toml"
|
||||
pidfile="/var/run/${name}.pid"
|
||||
|
||||
load_rc_config $name
|
||||
: ${logwisp_enable:="NO"}
|
||||
|
||||
run_rc_command "$1"
|
||||
EOF
|
||||
|
||||
chmod +x /usr/local/etc/rc.d/logwisp
|
||||
|
||||
# Enable
|
||||
sysrc logwisp_enable="YES"
|
||||
service logwisp start
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
#### Windows Subsystem for Linux (WSL)
|
||||
|
||||
```bash
|
||||
# Inside WSL, follow Linux instructions
|
||||
wget https://github.com/yourusername/logwisp/releases/latest/download/logwisp-linux-amd64
|
||||
chmod +x logwisp-linux-amd64
|
||||
./logwisp-linux-amd64
|
||||
```
|
||||
|
||||
#### Native Windows (planned)
|
||||
|
||||
Future support for native Windows service.
|
||||
|
||||
## Container Deployment
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
logwisp:
|
||||
image: yourusername/logwisp:latest
|
||||
container_name: logwisp
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "9090:9090" # If using TCP
|
||||
volumes:
|
||||
- /var/log:/logs:ro
|
||||
- ./logwisp.toml:/config/logwisp.toml:ro
|
||||
command: ["--config", "/config/logwisp.toml"]
|
||||
environment:
|
||||
- LOGWISP_LOGGING_LEVEL=info
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/status"]
|
||||
interval: 30s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### Kubernetes
|
||||
|
||||
Deployment manifest:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: logwisp
|
||||
labels:
|
||||
app: logwisp
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: logwisp
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: logwisp
|
||||
spec:
|
||||
containers:
|
||||
- name: logwisp
|
||||
image: yourusername/logwisp:latest
|
||||
args:
|
||||
- --config
|
||||
- /config/logwisp.toml
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
- containerPort: 9090
|
||||
name: tcp
|
||||
volumeMounts:
|
||||
- name: logs
|
||||
mountPath: /logs
|
||||
readOnly: true
|
||||
- name: config
|
||||
mountPath: /config
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /status
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /status
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
volumes:
|
||||
- name: logs
|
||||
hostPath:
|
||||
path: /var/log
|
||||
- name: config
|
||||
configMap:
|
||||
name: logwisp-config
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: logwisp
|
||||
spec:
|
||||
selector:
|
||||
app: logwisp
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
- name: tcp
|
||||
port: 9090
|
||||
targetPort: 9090
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: logwisp-config
|
||||
data:
|
||||
logwisp.toml: |
|
||||
[[streams]]
|
||||
name = "k8s"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/logs", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
```
|
||||
|
||||
## Post-Installation
|
||||
|
||||
### Verify Installation
|
||||
|
||||
1. Check version:
|
||||
```bash
|
||||
logwisp --version
|
||||
```
|
||||
|
||||
2. Test configuration:
|
||||
```bash
|
||||
logwisp --config /etc/logwisp/logwisp.toml --log-level debug
|
||||
```
|
||||
|
||||
3. Check service status:
|
||||
```bash
|
||||
# systemd
|
||||
sudo systemctl status logwisp
|
||||
|
||||
# macOS
|
||||
sudo launchctl list | grep logwisp
|
||||
|
||||
# FreeBSD
|
||||
service logwisp status
|
||||
```
|
||||
|
||||
4. Test streaming:
|
||||
```bash
|
||||
curl -N http://localhost:8080/stream
|
||||
```
|
||||
|
||||
### Security Hardening
|
||||
|
||||
1. **Create dedicated user**:
|
||||
```bash
|
||||
sudo useradd -r -s /bin/false -d /var/lib/logwisp logwisp
|
||||
```
|
||||
|
||||
2. **Set file permissions**:
|
||||
```bash
|
||||
sudo chown root:root /usr/local/bin/logwisp
|
||||
sudo chmod 755 /usr/local/bin/logwisp
|
||||
sudo chown -R logwisp:logwisp /etc/logwisp
|
||||
sudo chmod 640 /etc/logwisp/logwisp.toml
|
||||
```
|
||||
|
||||
3. **Configure firewall**:
|
||||
```bash
|
||||
# UFW
|
||||
sudo ufw allow 8080/tcp comment "LogWisp HTTP"
|
||||
|
||||
# firewalld
|
||||
sudo firewall-cmd --permanent --add-port=8080/tcp
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
4. **Enable SELinux/AppArmor** (if applicable)
|
||||
|
||||
### Initial Configuration
|
||||
|
||||
1. Copy example configuration:
|
||||
```bash
|
||||
sudo cp /usr/local/share/logwisp/examples/logwisp.toml.example /etc/logwisp/logwisp.toml
|
||||
```
|
||||
|
||||
2. Edit configuration:
|
||||
```bash
|
||||
sudo nano /etc/logwisp/logwisp.toml
|
||||
```
|
||||
|
||||
3. Set up log monitoring:
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "myapp"
|
||||
[streams.monitor]
|
||||
targets = [
|
||||
{ path = "/var/log/myapp", pattern = "*.log" }
|
||||
]
|
||||
```
|
||||
|
||||
4. Restart service:
|
||||
```bash
|
||||
sudo systemctl restart logwisp
|
||||
```
|
||||
|
||||
## Uninstallation
|
||||
|
||||
### Linux
|
||||
```bash
|
||||
# Stop service
|
||||
sudo systemctl stop logwisp
|
||||
sudo systemctl disable logwisp
|
||||
|
||||
# Remove files
|
||||
sudo rm /usr/local/bin/logwisp
|
||||
sudo rm /etc/systemd/system/logwisp.service
|
||||
sudo rm -rf /etc/logwisp
|
||||
sudo rm -rf /var/log/logwisp
|
||||
|
||||
# Remove user
|
||||
sudo userdel logwisp
|
||||
```
|
||||
|
||||
### macOS
|
||||
```bash
|
||||
# Stop service
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.logwisp.plist
|
||||
|
||||
# Remove files
|
||||
sudo rm /usr/local/bin/logwisp
|
||||
sudo rm /Library/LaunchDaemons/com.logwisp.plist
|
||||
sudo rm -rf /usr/local/etc/logwisp
|
||||
```
|
||||
|
||||
### Docker
|
||||
```bash
|
||||
docker stop logwisp
|
||||
docker rm logwisp
|
||||
docker rmi yourusername/logwisp:latest
|
||||
```
|
||||
|
||||
## Troubleshooting Installation
|
||||
|
||||
### Permission Denied
|
||||
|
||||
If you get permission errors:
|
||||
```bash
|
||||
# Check file ownership
|
||||
ls -la /usr/local/bin/logwisp
|
||||
|
||||
# Fix permissions
|
||||
sudo chmod +x /usr/local/bin/logwisp
|
||||
|
||||
# Check log directory
|
||||
sudo mkdir -p /var/log/logwisp
|
||||
sudo chown logwisp:logwisp /var/log/logwisp
|
||||
```
|
||||
|
||||
### Service Won't Start
|
||||
|
||||
Check logs:
|
||||
```bash
|
||||
# systemd
|
||||
sudo journalctl -u logwisp -f
|
||||
|
||||
# Manual run
|
||||
sudo -u logwisp /usr/local/bin/logwisp --config /etc/logwisp/logwisp.toml
|
||||
```
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
Find conflicting process:
|
||||
```bash
|
||||
sudo lsof -i :8080
|
||||
# or
|
||||
sudo netstat -tlnp | grep 8080
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Quick Start](quickstart.md) - Get running quickly
|
||||
- [Configuration Guide](configuration.md) - Configure LogWisp
|
||||
- [Troubleshooting](troubleshooting.md) - Common issues
|
||||
- [Security Best Practices](security.md) - Hardening guide
|
||||
511
doc/monitoring.md
Normal file
511
doc/monitoring.md
Normal file
@ -0,0 +1,511 @@
|
||||
# Monitoring & Status Guide
|
||||
|
||||
LogWisp provides comprehensive monitoring capabilities through status endpoints, operational logs, and metrics.
|
||||
|
||||
## Status Endpoints
|
||||
|
||||
### Stream Status
|
||||
|
||||
Each stream exposes its own status endpoint:
|
||||
|
||||
```bash
|
||||
# Standalone mode
|
||||
curl http://localhost:8080/status
|
||||
|
||||
# Router mode
|
||||
curl http://localhost:8080/streamname/status
|
||||
```
|
||||
|
||||
Example response:
|
||||
```json
|
||||
{
|
||||
"service": "LogWisp",
|
||||
"version": "1.0.0",
|
||||
"server": {
|
||||
"type": "http",
|
||||
"port": 8080,
|
||||
"active_clients": 5,
|
||||
"buffer_size": 1000,
|
||||
"uptime_seconds": 3600,
|
||||
"mode": {
|
||||
"standalone": true,
|
||||
"router": false
|
||||
}
|
||||
},
|
||||
"monitor": {
|
||||
"active_watchers": 3,
|
||||
"total_entries": 152341,
|
||||
"dropped_entries": 12,
|
||||
"start_time": "2024-01-20T10:00:00Z",
|
||||
"last_entry_time": "2024-01-20T11:00:00Z"
|
||||
},
|
||||
"filters": {
|
||||
"filter_count": 2,
|
||||
"total_processed": 152341,
|
||||
"total_passed": 48234,
|
||||
"filters": [
|
||||
{
|
||||
"type": "include",
|
||||
"logic": "or",
|
||||
"pattern_count": 3,
|
||||
"total_processed": 152341,
|
||||
"total_matched": 48234,
|
||||
"total_dropped": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"features": {
|
||||
"heartbeat": {
|
||||
"enabled": true,
|
||||
"interval": 30,
|
||||
"format": "comment"
|
||||
},
|
||||
"rate_limit": {
|
||||
"enabled": true,
|
||||
"total_requests": 8234,
|
||||
"blocked_requests": 89,
|
||||
"active_ips": 12,
|
||||
"total_connections": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Global Status (Router Mode)
|
||||
|
||||
In router mode, a global status endpoint provides aggregated information:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8080/status
|
||||
```
|
||||
|
||||
## Key Metrics
|
||||
|
||||
### Monitor Metrics
|
||||
|
||||
Track file watching performance:
|
||||
|
||||
| Metric | Description | Healthy Range |
|
||||
|--------|-------------|---------------|
|
||||
| `active_watchers` | Number of files being watched | 1-1000 |
|
||||
| `total_entries` | Total log entries processed | Increasing |
|
||||
| `dropped_entries` | Entries dropped due to buffer full | < 1% of total |
|
||||
| `entries_per_second` | Current processing rate | Varies |
|
||||
|
||||
### Connection Metrics
|
||||
|
||||
Monitor client connections:
|
||||
|
||||
| Metric | Description | Warning Signs |
|
||||
|--------|-------------|---------------|
|
||||
| `active_clients` | Current SSE connections | Near limit |
|
||||
| `tcp_connections` | Current TCP connections | Near limit |
|
||||
| `total_connections` | All active connections | > 80% of max |
|
||||
|
||||
### Filter Metrics
|
||||
|
||||
Understand filtering effectiveness:
|
||||
|
||||
| Metric | Description | Optimization |
|
||||
|--------|-------------|--------------|
|
||||
| `total_processed` | Entries checked | - |
|
||||
| `total_passed` | Entries that passed | Very low = too restrictive |
|
||||
| `total_dropped` | Entries filtered out | Very high = review patterns |
|
||||
|
||||
### Rate Limit Metrics
|
||||
|
||||
Track rate limiting impact:
|
||||
|
||||
| Metric | Description | Action Needed |
|
||||
|--------|-------------|---------------|
|
||||
| `blocked_requests` | Rejected requests | High = increase limits |
|
||||
| `active_ips` | Unique clients | High = scale out |
|
||||
| `blocked_percentage` | Rejection rate | > 10% = review |
|
||||
|
||||
## Operational Logging
|
||||
|
||||
### Log Levels
|
||||
|
||||
Configure LogWisp's operational logging:
|
||||
|
||||
```toml
|
||||
[logging]
|
||||
output = "both" # file and stderr
|
||||
level = "info" # info for production
|
||||
```
|
||||
|
||||
Log levels and their use:
|
||||
- **DEBUG**: Detailed internal operations
|
||||
- **INFO**: Normal operations, connections
|
||||
- **WARN**: Recoverable issues
|
||||
- **ERROR**: Errors requiring attention
|
||||
|
||||
### Important Log Messages
|
||||
|
||||
#### Startup Messages
|
||||
```
|
||||
LogWisp starting version=1.0.0 config_file=/etc/logwisp.toml
|
||||
Stream registered with router stream=app
|
||||
TCP endpoint configured transport=system port=9090
|
||||
HTTP endpoints configured transport=app stream_url=http://localhost:8080/stream
|
||||
```
|
||||
|
||||
#### Connection Events
|
||||
```
|
||||
HTTP client connected remote_addr=192.168.1.100:54231 active_clients=6
|
||||
HTTP client disconnected remote_addr=192.168.1.100:54231 active_clients=5
|
||||
TCP connection opened remote_addr=192.168.1.100:54232 active_connections=3
|
||||
```
|
||||
|
||||
#### Error Conditions
|
||||
```
|
||||
Failed to open file for checking path=/var/log/app.log error=permission denied
|
||||
Scanner error while reading file path=/var/log/huge.log error=token too long
|
||||
Request rate limited ip=192.168.1.100
|
||||
Connection limit exceeded ip=192.168.1.100 connections=5 limit=5
|
||||
```
|
||||
|
||||
#### Performance Warnings
|
||||
```
|
||||
Dropped log entry - subscriber buffer full
|
||||
Dropped entry for slow client remote_addr=192.168.1.100
|
||||
Check interval too small: 5ms (min: 10ms)
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
### Basic Health Check
|
||||
|
||||
Simple up/down check:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# health_check.sh
|
||||
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/status)
|
||||
|
||||
if [ "$STATUS" -eq 200 ]; then
|
||||
echo "LogWisp is healthy"
|
||||
exit 0
|
||||
else
|
||||
echo "LogWisp is unhealthy (status: $STATUS)"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### Advanced Health Check
|
||||
|
||||
Check specific conditions:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# advanced_health_check.sh
|
||||
|
||||
RESPONSE=$(curl -s http://localhost:8080/status)
|
||||
|
||||
# Check if processing logs
|
||||
ENTRIES=$(echo "$RESPONSE" | jq -r '.monitor.total_entries')
|
||||
if [ "$ENTRIES" -eq 0 ]; then
|
||||
echo "WARNING: No log entries processed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check dropped entries
|
||||
DROPPED=$(echo "$RESPONSE" | jq -r '.monitor.dropped_entries')
|
||||
TOTAL=$(echo "$RESPONSE" | jq -r '.monitor.total_entries')
|
||||
DROP_PERCENT=$(( DROPPED * 100 / TOTAL ))
|
||||
|
||||
if [ "$DROP_PERCENT" -gt 5 ]; then
|
||||
echo "WARNING: High drop rate: ${DROP_PERCENT}%"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check connections
|
||||
CONNECTIONS=$(echo "$RESPONSE" | jq -r '.server.active_clients')
|
||||
echo "OK: Processing logs, $CONNECTIONS active clients"
|
||||
exit 0
|
||||
```
|
||||
|
||||
### Container Health Check
|
||||
|
||||
Docker/Kubernetes configuration:
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/status || exit 1
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Kubernetes
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /status
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /status
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
```
|
||||
|
||||
## Monitoring Integration
|
||||
|
||||
### Prometheus Metrics
|
||||
|
||||
Export metrics in Prometheus format:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# prometheus_exporter.sh
|
||||
|
||||
while true; do
|
||||
STATUS=$(curl -s http://localhost:8080/status)
|
||||
|
||||
# Extract metrics
|
||||
CLIENTS=$(echo "$STATUS" | jq -r '.server.active_clients')
|
||||
ENTRIES=$(echo "$STATUS" | jq -r '.monitor.total_entries')
|
||||
DROPPED=$(echo "$STATUS" | jq -r '.monitor.dropped_entries')
|
||||
|
||||
# Output Prometheus format
|
||||
cat << EOF
|
||||
# HELP logwisp_active_clients Number of active streaming clients
|
||||
# TYPE logwisp_active_clients gauge
|
||||
logwisp_active_clients $CLIENTS
|
||||
|
||||
# HELP logwisp_total_entries Total log entries processed
|
||||
# TYPE logwisp_total_entries counter
|
||||
logwisp_total_entries $ENTRIES
|
||||
|
||||
# HELP logwisp_dropped_entries Total log entries dropped
|
||||
# TYPE logwisp_dropped_entries counter
|
||||
logwisp_dropped_entries $DROPPED
|
||||
EOF
|
||||
|
||||
sleep 60
|
||||
done
|
||||
```
|
||||
|
||||
### Grafana Dashboard
|
||||
|
||||
Key panels for Grafana:
|
||||
|
||||
1. **Active Connections**
|
||||
- Query: `logwisp_active_clients`
|
||||
- Visualization: Graph
|
||||
- Alert: > 80% of max
|
||||
|
||||
2. **Log Processing Rate**
|
||||
- Query: `rate(logwisp_total_entries[5m])`
|
||||
- Visualization: Graph
|
||||
- Alert: < 1 entry/min
|
||||
|
||||
3. **Drop Rate**
|
||||
- Query: `rate(logwisp_dropped_entries[5m]) / rate(logwisp_total_entries[5m])`
|
||||
- Visualization: Gauge
|
||||
- Alert: > 5%
|
||||
|
||||
4. **Rate Limit Rejections**
|
||||
- Query: `rate(logwisp_blocked_requests[5m])`
|
||||
- Visualization: Graph
|
||||
- Alert: > 10/min
|
||||
|
||||
### Datadog Integration
|
||||
|
||||
Send custom metrics:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# datadog_metrics.sh
|
||||
|
||||
while true; do
|
||||
STATUS=$(curl -s http://localhost:8080/status)
|
||||
|
||||
# Send metrics to Datadog
|
||||
echo "$STATUS" | jq -r '
|
||||
"logwisp.connections:\(.server.active_clients)|g",
|
||||
"logwisp.entries:\(.monitor.total_entries)|c",
|
||||
"logwisp.dropped:\(.monitor.dropped_entries)|c"
|
||||
' | while read metric; do
|
||||
echo "$metric" | nc -u -w1 localhost 8125
|
||||
done
|
||||
|
||||
sleep 60
|
||||
done
|
||||
```
|
||||
|
||||
## Performance Monitoring
|
||||
|
||||
### CPU Usage
|
||||
|
||||
Monitor CPU usage by component:
|
||||
|
||||
```bash
|
||||
# Check process CPU
|
||||
top -p $(pgrep logwisp) -b -n 1
|
||||
|
||||
# Profile CPU usage
|
||||
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
|
||||
```
|
||||
|
||||
Common CPU consumers:
|
||||
- File watching (reduce check_interval_ms)
|
||||
- Regex filtering (simplify patterns)
|
||||
- JSON encoding (reduce clients)
|
||||
|
||||
### Memory Usage
|
||||
|
||||
Track memory consumption:
|
||||
|
||||
```bash
|
||||
# Check process memory
|
||||
ps aux | grep logwisp
|
||||
|
||||
# Detailed memory stats
|
||||
cat /proc/$(pgrep logwisp)/status | grep -E "Vm(RSS|Size)"
|
||||
```
|
||||
|
||||
Memory optimization:
|
||||
- Reduce buffer sizes
|
||||
- Limit connections
|
||||
- Simplify filters
|
||||
|
||||
### Network Bandwidth
|
||||
|
||||
Monitor streaming bandwidth:
|
||||
|
||||
```bash
|
||||
# Network statistics
|
||||
netstat -i
|
||||
iftop -i eth0 -f "port 8080"
|
||||
|
||||
# Connection count
|
||||
ss -tan | grep :8080 | wc -l
|
||||
```
|
||||
|
||||
## Alerting
|
||||
|
||||
### Basic Alerts
|
||||
|
||||
Essential alerts to configure:
|
||||
|
||||
| Alert | Condition | Severity |
|
||||
|-------|-----------|----------|
|
||||
| Service Down | Status endpoint fails | Critical |
|
||||
| High Drop Rate | > 10% entries dropped | Warning |
|
||||
| No Log Activity | 0 entries/min for 5 min | Warning |
|
||||
| Connection Limit | > 90% of max connections | Warning |
|
||||
| Rate Limit High | > 20% requests blocked | Warning |
|
||||
|
||||
### Alert Script
|
||||
|
||||
Example monitoring script:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# monitor_alerts.sh
|
||||
|
||||
check_alert() {
|
||||
local name=$1
|
||||
local condition=$2
|
||||
local message=$3
|
||||
|
||||
if eval "$condition"; then
|
||||
echo "ALERT: $name - $message"
|
||||
# Send to alerting system
|
||||
# curl -X POST https://alerts.example.com/...
|
||||
fi
|
||||
}
|
||||
|
||||
while true; do
|
||||
STATUS=$(curl -s http://localhost:8080/status)
|
||||
|
||||
if [ -z "$STATUS" ]; then
|
||||
check_alert "SERVICE_DOWN" "true" "LogWisp not responding"
|
||||
sleep 60
|
||||
continue
|
||||
fi
|
||||
|
||||
# Extract metrics
|
||||
DROPPED=$(echo "$STATUS" | jq -r '.monitor.dropped_entries')
|
||||
TOTAL=$(echo "$STATUS" | jq -r '.monitor.total_entries')
|
||||
CLIENTS=$(echo "$STATUS" | jq -r '.server.active_clients')
|
||||
|
||||
# Check conditions
|
||||
check_alert "HIGH_DROP_RATE" \
|
||||
"[ $((DROPPED * 100 / TOTAL)) -gt 10 ]" \
|
||||
"Drop rate above 10%"
|
||||
|
||||
check_alert "HIGH_CONNECTIONS" \
|
||||
"[ $CLIENTS -gt 90 ]" \
|
||||
"Near connection limit: $CLIENTS/100"
|
||||
|
||||
sleep 60
|
||||
done
|
||||
```
|
||||
|
||||
## Troubleshooting with Monitoring
|
||||
|
||||
### No Logs Appearing
|
||||
|
||||
Check monitor stats:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '.monitor'
|
||||
```
|
||||
|
||||
Look for:
|
||||
- `active_watchers` = 0 (no files found)
|
||||
- `total_entries` not increasing (files not updating)
|
||||
|
||||
### High CPU Usage
|
||||
|
||||
Enable debug logging:
|
||||
```bash
|
||||
logwisp --log-level debug --log-output stderr
|
||||
```
|
||||
|
||||
Watch for:
|
||||
- Frequent "checkFile" messages (reduce check_interval)
|
||||
- Many filter operations (optimize patterns)
|
||||
|
||||
### Memory Growth
|
||||
|
||||
Monitor over time:
|
||||
```bash
|
||||
while true; do
|
||||
ps aux | grep logwisp | grep -v grep
|
||||
curl -s http://localhost:8080/status | jq '.server.active_clients'
|
||||
sleep 10
|
||||
done
|
||||
```
|
||||
|
||||
### Connection Issues
|
||||
|
||||
Check connection stats:
|
||||
```bash
|
||||
# Current connections
|
||||
curl -s http://localhost:8080/status | jq '.server'
|
||||
|
||||
# Rate limit stats
|
||||
curl -s http://localhost:8080/status | jq '.features.rate_limit'
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Regular Monitoring**: Check status endpoints every 30-60 seconds
|
||||
2. **Set Alerts**: Configure alerts for critical conditions
|
||||
3. **Log Rotation**: Rotate LogWisp's own logs to prevent disk fill
|
||||
4. **Baseline Metrics**: Establish normal ranges for your environment
|
||||
5. **Capacity Planning**: Monitor trends for scaling decisions
|
||||
6. **Test Monitoring**: Verify alerts work before issues occur
|
||||
|
||||
## See Also
|
||||
|
||||
- [Performance Tuning](performance.md) - Optimization guide
|
||||
- [Troubleshooting](troubleshooting.md) - Common issues
|
||||
- [Configuration Guide](configuration.md) - Monitoring configuration
|
||||
- [Integration Examples](integrations.md) - Monitoring system integration
|
||||
209
doc/quickstart.md
Normal file
209
doc/quickstart.md
Normal file
@ -0,0 +1,209 @@
|
||||
# Quick Start Guide
|
||||
|
||||
Get LogWisp up and running in 5 minutes!
|
||||
|
||||
## Installation
|
||||
|
||||
### From Source
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/yourusername/logwisp.git
|
||||
cd logwisp
|
||||
|
||||
# Build and install
|
||||
make install
|
||||
|
||||
# Or just build
|
||||
make build
|
||||
./logwisp --version
|
||||
```
|
||||
|
||||
### Using Go Install
|
||||
```bash
|
||||
go install github.com/yourusername/logwisp/src/cmd/logwisp@latest
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### 1. Monitor Current Directory
|
||||
|
||||
Start LogWisp with defaults (monitors `*.log` files in current directory):
|
||||
|
||||
```bash
|
||||
logwisp
|
||||
```
|
||||
|
||||
### 2. Stream Logs
|
||||
|
||||
In another terminal, connect to the log stream:
|
||||
|
||||
```bash
|
||||
# Using curl (SSE stream)
|
||||
curl -N http://localhost:8080/stream
|
||||
|
||||
# Check status
|
||||
curl http://localhost:8080/status | jq .
|
||||
```
|
||||
|
||||
### 3. Create Some Logs
|
||||
|
||||
Generate test logs to see streaming in action:
|
||||
|
||||
```bash
|
||||
# In a third terminal
|
||||
echo "[ERROR] Something went wrong!" >> test.log
|
||||
echo "[INFO] Application started" >> test.log
|
||||
echo "[WARN] Low memory warning" >> test.log
|
||||
```
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Monitor Specific Directory
|
||||
|
||||
Create a configuration file `~/.config/logwisp.toml`:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "myapp"
|
||||
|
||||
[streams.monitor]
|
||||
targets = [
|
||||
{ path = "/var/log/myapp", pattern = "*.log", is_file = false }
|
||||
]
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
```
|
||||
|
||||
Run LogWisp:
|
||||
```bash
|
||||
logwisp
|
||||
```
|
||||
|
||||
### Filter Only Errors and Warnings
|
||||
|
||||
Add filters to your configuration:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "errors"
|
||||
|
||||
[streams.monitor]
|
||||
targets = [
|
||||
{ path = "./", pattern = "*.log" }
|
||||
]
|
||||
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = ["ERROR", "WARN", "CRITICAL", "FATAL"]
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
```
|
||||
|
||||
### Multiple Log Sources
|
||||
|
||||
Monitor different applications on different ports:
|
||||
|
||||
```toml
|
||||
# Stream 1: Web application
|
||||
[[streams]]
|
||||
name = "webapp"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/nginx", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
|
||||
# Stream 2: Database
|
||||
[[streams]]
|
||||
name = "database"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/postgresql", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8081
|
||||
```
|
||||
|
||||
### TCP Streaming
|
||||
|
||||
For high-performance streaming, use TCP:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "highperf"
|
||||
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/app", pattern = "*.log" }]
|
||||
|
||||
[streams.tcpserver]
|
||||
enabled = true
|
||||
port = 9090
|
||||
buffer_size = 5000
|
||||
```
|
||||
|
||||
Connect with netcat:
|
||||
```bash
|
||||
nc localhost 9090
|
||||
```
|
||||
|
||||
### Router Mode
|
||||
|
||||
Consolidate multiple streams on one port using router mode:
|
||||
|
||||
```bash
|
||||
# With the multi-stream config above
|
||||
logwisp --router
|
||||
|
||||
# Access streams at:
|
||||
# http://localhost:8080/webapp/stream
|
||||
# http://localhost:8080/database/stream
|
||||
# http://localhost:8080/status (global status)
|
||||
```
|
||||
|
||||
## Quick Tips
|
||||
|
||||
### Enable Debug Logging
|
||||
```bash
|
||||
logwisp --log-level debug --log-output stderr
|
||||
```
|
||||
|
||||
### Run in Background
|
||||
```bash
|
||||
logwisp --background --config /etc/logwisp/prod.toml
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
Protect your streams from abuse:
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 10.0
|
||||
burst_size = 20
|
||||
max_connections_per_ip = 5
|
||||
```
|
||||
|
||||
### JSON Output Format
|
||||
For structured logging:
|
||||
|
||||
```toml
|
||||
[logging.console]
|
||||
format = "json"
|
||||
```
|
||||
|
||||
## What's Next?
|
||||
|
||||
- Read the [Configuration Guide](configuration.md) for all options
|
||||
- Learn about [Filters](filters.md) for advanced pattern matching
|
||||
- Explore [Rate Limiting](ratelimiting.md) for production deployments
|
||||
- Check out [Example Configurations](examples/) for more scenarios
|
||||
|
||||
## Getting Help
|
||||
|
||||
- Run `logwisp --help` for CLI options
|
||||
- Check `http://localhost:8080/status` for runtime statistics
|
||||
- Enable debug logging for troubleshooting
|
||||
- Visit our [GitHub repository](https://github.com/yourusername/logwisp) for issues and discussions
|
||||
526
doc/ratelimiting.md
Normal file
526
doc/ratelimiting.md
Normal file
@ -0,0 +1,526 @@
|
||||
# Rate Limiting Guide
|
||||
|
||||
LogWisp provides configurable rate limiting to protect against abuse, prevent resource exhaustion, and ensure fair access to log streams.
|
||||
|
||||
## How Rate Limiting Works
|
||||
|
||||
LogWisp uses a **token bucket algorithm** for smooth, burst-tolerant rate limiting:
|
||||
|
||||
1. Each client (or globally) gets a bucket with a fixed capacity
|
||||
2. Tokens are added to the bucket at a configured rate
|
||||
3. Each request consumes one token
|
||||
4. If no tokens are available, the request is rejected
|
||||
5. The bucket can accumulate tokens up to its capacity for bursts
|
||||
|
||||
## Configuration
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true # Enable rate limiting
|
||||
requests_per_second = 10.0 # Token refill rate
|
||||
burst_size = 20 # Maximum tokens (bucket capacity)
|
||||
limit_by = "ip" # "ip" or "global"
|
||||
```
|
||||
|
||||
### Complete Options
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
# Core settings
|
||||
enabled = true # Enable/disable rate limiting
|
||||
requests_per_second = 10.0 # Token generation rate (float)
|
||||
burst_size = 20 # Token bucket capacity
|
||||
|
||||
# Limiting strategy
|
||||
limit_by = "ip" # "ip" or "global"
|
||||
|
||||
# Connection limits
|
||||
max_connections_per_ip = 5 # Max concurrent connections per IP
|
||||
max_total_connections = 100 # Max total concurrent connections
|
||||
|
||||
# Response configuration
|
||||
response_code = 429 # HTTP status code when limited
|
||||
response_message = "Rate limit exceeded" # Error message
|
||||
|
||||
# Same options available for TCP
|
||||
[streams.tcpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 5.0
|
||||
burst_size = 10
|
||||
limit_by = "ip"
|
||||
```
|
||||
|
||||
## Limiting Strategies
|
||||
|
||||
### Per-IP Limiting (Default)
|
||||
|
||||
Each client IP address gets its own token bucket:
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
limit_by = "ip"
|
||||
requests_per_second = 10.0
|
||||
burst_size = 20
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
- Fair access for multiple users
|
||||
- Prevent single client from monopolizing resources
|
||||
- Public-facing endpoints
|
||||
|
||||
**Example behavior:**
|
||||
- Client A: Can make 10 req/sec
|
||||
- Client B: Also can make 10 req/sec
|
||||
- Total: Up to 10 × number of clients
|
||||
|
||||
### Global Limiting
|
||||
|
||||
All clients share a single token bucket:
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
limit_by = "global"
|
||||
requests_per_second = 50.0
|
||||
burst_size = 100
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
- Protect backend resources
|
||||
- Control total system load
|
||||
- Internal services with known clients
|
||||
|
||||
**Example behavior:**
|
||||
- All clients combined: 50 req/sec max
|
||||
- One aggressive client can consume all tokens
|
||||
|
||||
## Connection Limits
|
||||
|
||||
In addition to request rate limiting, you can limit concurrent connections:
|
||||
|
||||
### Per-IP Connection Limit
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
max_connections_per_ip = 5 # Each IP can have max 5 connections
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- Prevents connection exhaustion attacks
|
||||
- Limits resource usage per client
|
||||
- Checked before rate limits
|
||||
|
||||
### Total Connection Limit
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
max_total_connections = 100 # Max 100 connections total
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- Protects server resources
|
||||
- Prevents memory exhaustion
|
||||
- Global limit across all IPs
|
||||
|
||||
## Response Behavior
|
||||
|
||||
### HTTP Responses
|
||||
|
||||
When rate limited, HTTP clients receive:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Rate limit exceeded",
|
||||
"retry_after": "60"
|
||||
}
|
||||
```
|
||||
|
||||
With these headers:
|
||||
- Status code: 429 (default) or configured value
|
||||
- Content-Type: application/json
|
||||
|
||||
Configure custom responses:
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
response_code = 503 # Service Unavailable
|
||||
response_message = "Server overloaded, please retry later"
|
||||
```
|
||||
|
||||
### TCP Behavior
|
||||
|
||||
TCP connections are **silently dropped** when rate limited:
|
||||
- No error message sent
|
||||
- Connection immediately closed
|
||||
- Prevents information leakage
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Light Protection
|
||||
|
||||
For internal or trusted environments:
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 50.0
|
||||
burst_size = 100
|
||||
limit_by = "ip"
|
||||
```
|
||||
|
||||
### Moderate Protection
|
||||
|
||||
For semi-public endpoints:
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 10.0
|
||||
burst_size = 30
|
||||
limit_by = "ip"
|
||||
max_connections_per_ip = 5
|
||||
max_total_connections = 200
|
||||
```
|
||||
|
||||
### Strict Protection
|
||||
|
||||
For public or sensitive endpoints:
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 2.0
|
||||
burst_size = 5
|
||||
limit_by = "ip"
|
||||
max_connections_per_ip = 2
|
||||
max_total_connections = 50
|
||||
response_code = 503
|
||||
response_message = "Service temporarily unavailable"
|
||||
```
|
||||
|
||||
### Debug/Development
|
||||
|
||||
Disable for testing:
|
||||
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = false
|
||||
```
|
||||
|
||||
## Use Case Scenarios
|
||||
|
||||
### Public Log Viewer
|
||||
|
||||
Prevent abuse while allowing legitimate use:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "public-logs"
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 5.0 # 5 new connections per second
|
||||
burst_size = 10 # Allow short bursts
|
||||
limit_by = "ip"
|
||||
max_connections_per_ip = 3 # Max 3 streams per user
|
||||
max_total_connections = 100
|
||||
```
|
||||
|
||||
### Internal Monitoring
|
||||
|
||||
Protect against accidental overload:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "internal-metrics"
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8081
|
||||
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 100.0 # High limit for internal use
|
||||
burst_size = 200
|
||||
limit_by = "global" # Total system limit
|
||||
max_total_connections = 500
|
||||
```
|
||||
|
||||
### High-Security Audit Logs
|
||||
|
||||
Very restrictive access:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "audit"
|
||||
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8443
|
||||
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 0.5 # 1 request every 2 seconds
|
||||
burst_size = 2
|
||||
limit_by = "ip"
|
||||
max_connections_per_ip = 1 # Single connection only
|
||||
max_total_connections = 10
|
||||
response_code = 403 # Forbidden (hide rate limit)
|
||||
response_message = "Access denied"
|
||||
```
|
||||
|
||||
### Multi-Tenant Service
|
||||
|
||||
Different limits per stream:
|
||||
|
||||
```toml
|
||||
# Free tier
|
||||
[[streams]]
|
||||
name = "logs-free"
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 1.0
|
||||
burst_size = 5
|
||||
max_connections_per_ip = 1
|
||||
|
||||
# Premium tier
|
||||
[[streams]]
|
||||
name = "logs-premium"
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 50.0
|
||||
burst_size = 100
|
||||
max_connections_per_ip = 10
|
||||
```
|
||||
|
||||
## Monitoring Rate Limits
|
||||
|
||||
### Status Endpoint
|
||||
|
||||
Check rate limit statistics:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8080/status | jq '.server.features.rate_limit'
|
||||
```
|
||||
|
||||
Response includes:
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"total_requests": 15234,
|
||||
"blocked_requests": 89,
|
||||
"active_ips": 12,
|
||||
"total_connections": 8,
|
||||
"config": {
|
||||
"requests_per_second": 10,
|
||||
"burst_size": 20,
|
||||
"limit_by": "ip"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Debug Logging
|
||||
|
||||
Enable debug logs to see rate limit decisions:
|
||||
|
||||
```bash
|
||||
logwisp --log-level debug
|
||||
```
|
||||
|
||||
Look for messages:
|
||||
```
|
||||
Request rate limited ip=192.168.1.100
|
||||
Connection limit exceeded ip=192.168.1.100 connections=5 limit=5
|
||||
Created new IP limiter ip=192.168.1.100 total_ips=3
|
||||
```
|
||||
|
||||
## Testing Rate Limits
|
||||
|
||||
### Test Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Test rate limiting behavior
|
||||
|
||||
URL="http://localhost:8080/stream"
|
||||
PARALLEL=10
|
||||
DURATION=10
|
||||
|
||||
echo "Testing rate limits..."
|
||||
echo "URL: $URL"
|
||||
echo "Parallel connections: $PARALLEL"
|
||||
echo "Duration: ${DURATION}s"
|
||||
echo
|
||||
|
||||
# Function to connect and count lines
|
||||
test_connection() {
|
||||
local id=$1
|
||||
local count=0
|
||||
local start=$(date +%s)
|
||||
|
||||
while (( $(date +%s) - start < DURATION )); do
|
||||
if curl -s -N --max-time 1 "$URL" >/dev/null 2>&1; then
|
||||
((count++))
|
||||
echo "[$id] Connected successfully (total: $count)"
|
||||
else
|
||||
echo "[$id] Rate limited!"
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
}
|
||||
|
||||
# Run parallel connections
|
||||
for i in $(seq 1 $PARALLEL); do
|
||||
test_connection $i &
|
||||
done
|
||||
|
||||
wait
|
||||
echo "Test complete"
|
||||
```
|
||||
|
||||
### Load Testing
|
||||
|
||||
Using Apache Bench (ab):
|
||||
|
||||
```bash
|
||||
# Test burst handling
|
||||
ab -n 100 -c 20 http://localhost:8080/status
|
||||
|
||||
# Test sustained load
|
||||
ab -n 1000 -c 5 -r http://localhost:8080/status
|
||||
```
|
||||
|
||||
Using curl:
|
||||
|
||||
```bash
|
||||
# Test connection limit
|
||||
for i in {1..10}; do
|
||||
curl -N http://localhost:8080/stream &
|
||||
done
|
||||
```
|
||||
|
||||
## Tuning Guidelines
|
||||
|
||||
### Setting requests_per_second
|
||||
|
||||
Consider:
|
||||
- Expected legitimate traffic
|
||||
- Server capacity
|
||||
- Client retry behavior
|
||||
|
||||
**Formula**: `requests_per_second = expected_clients × requests_per_client`
|
||||
|
||||
### Setting burst_size
|
||||
|
||||
General rule: `burst_size = 2-3 × requests_per_second`
|
||||
|
||||
Examples:
|
||||
- `10 req/s → burst_size = 20-30`
|
||||
- `1 req/s → burst_size = 3-5`
|
||||
- `100 req/s → burst_size = 200-300`
|
||||
|
||||
### Connection Limits
|
||||
|
||||
Based on available memory:
|
||||
- Each HTTP connection: ~1-2MB
|
||||
- Each TCP connection: ~0.5-1MB
|
||||
|
||||
**Formula**: `max_connections = available_memory / memory_per_connection`
|
||||
|
||||
## Common Issues
|
||||
|
||||
### "All requests blocked"
|
||||
|
||||
Check if:
|
||||
- Rate limits too strict
|
||||
- Burst size too small
|
||||
- Using global limiting with many clients
|
||||
|
||||
### "Memory growth"
|
||||
|
||||
Possible causes:
|
||||
- No connection limits set
|
||||
- Slow clients holding connections
|
||||
- Too high burst_size
|
||||
|
||||
Solutions:
|
||||
```toml
|
||||
max_connections_per_ip = 5
|
||||
max_total_connections = 100
|
||||
```
|
||||
|
||||
### "Legitimate users blocked"
|
||||
|
||||
Consider:
|
||||
- Increasing burst_size for short spikes
|
||||
- Using per-IP instead of global limiting
|
||||
- Different streams for different user tiers
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Information Disclosure
|
||||
|
||||
Rate limit responses can reveal information:
|
||||
|
||||
```toml
|
||||
# Default - informative
|
||||
response_code = 429
|
||||
response_message = "Rate limit exceeded"
|
||||
|
||||
# Security-focused - generic
|
||||
response_code = 503
|
||||
response_message = "Service unavailable"
|
||||
|
||||
# High security - misleading
|
||||
response_code = 403
|
||||
response_message = "Forbidden"
|
||||
```
|
||||
|
||||
### DDoS Protection
|
||||
|
||||
Rate limiting helps but isn't complete DDoS protection:
|
||||
- Use with firewall rules
|
||||
- Consider CDN/proxy rate limiting
|
||||
- Monitor for distributed attacks
|
||||
|
||||
### Resource Exhaustion
|
||||
|
||||
Protect against:
|
||||
- Connection exhaustion
|
||||
- Memory exhaustion
|
||||
- CPU exhaustion
|
||||
|
||||
```toml
|
||||
# Comprehensive protection
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 10.0
|
||||
burst_size = 20
|
||||
max_connections_per_ip = 5
|
||||
max_total_connections = 100
|
||||
limit_by = "ip"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Start Conservative**: Begin with strict limits and relax as needed
|
||||
2. **Monitor Statistics**: Use `/status` endpoint to track behavior
|
||||
3. **Test Thoroughly**: Verify limits work as expected under load
|
||||
4. **Document Limits**: Make rate limits clear to users
|
||||
5. **Provide Retry Info**: Help clients implement proper retry logic
|
||||
6. **Different Tiers**: Consider different limits for different user types
|
||||
7. **Regular Review**: Adjust limits based on usage patterns
|
||||
|
||||
## See Also
|
||||
|
||||
- [Configuration Guide](configuration.md) - Complete configuration reference
|
||||
- [Security Best Practices](security.md) - Security hardening
|
||||
- [Performance Tuning](performance.md) - Optimization guidelines
|
||||
- [Troubleshooting](troubleshooting.md) - Common issues
|
||||
520
doc/router.md
Normal file
520
doc/router.md
Normal file
@ -0,0 +1,520 @@
|
||||
# Router Mode Guide
|
||||
|
||||
Router mode allows multiple LogWisp streams to share HTTP ports through path-based routing, simplifying deployment and access control.
|
||||
|
||||
## Overview
|
||||
|
||||
In standard mode, each stream requires its own port:
|
||||
- Stream 1: `http://localhost:8080/stream`
|
||||
- Stream 2: `http://localhost:8081/stream`
|
||||
- Stream 3: `http://localhost:8082/stream`
|
||||
|
||||
In router mode, streams share ports via paths:
|
||||
- Stream 1: `http://localhost:8080/app/stream`
|
||||
- Stream 2: `http://localhost:8080/database/stream`
|
||||
- Stream 3: `http://localhost:8080/system/stream`
|
||||
- Global status: `http://localhost:8080/status`
|
||||
|
||||
## Enabling Router Mode
|
||||
|
||||
Start LogWisp with the `--router` flag:
|
||||
|
||||
```bash
|
||||
logwisp --router --config /etc/logwisp/multi-stream.toml
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Basic Router Configuration
|
||||
|
||||
```toml
|
||||
# All streams can use the same port in router mode
|
||||
[[streams]]
|
||||
name = "app"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/app", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080 # Same port OK
|
||||
|
||||
[[streams]]
|
||||
name = "database"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/postgresql", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080 # Shared port
|
||||
|
||||
[[streams]]
|
||||
name = "nginx"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/nginx", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080 # Shared port
|
||||
```
|
||||
|
||||
### Path Structure
|
||||
|
||||
In router mode, paths are automatically prefixed with the stream name:
|
||||
|
||||
| Stream Name | Configuration Path | Router Mode Path |
|
||||
|------------|-------------------|------------------|
|
||||
| `app` | `/stream` | `/app/stream` |
|
||||
| `app` | `/status` | `/app/status` |
|
||||
| `database` | `/stream` | `/database/stream` |
|
||||
| `database` | `/status` | `/database/status` |
|
||||
|
||||
### Custom Paths
|
||||
|
||||
You can customize the paths in each stream:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "api"
|
||||
[streams.httpserver]
|
||||
stream_path = "/logs" # Becomes /api/logs
|
||||
status_path = "/health" # Becomes /api/health
|
||||
```
|
||||
|
||||
## URL Endpoints
|
||||
|
||||
### Stream Endpoints
|
||||
|
||||
Access individual streams:
|
||||
|
||||
```bash
|
||||
# SSE stream for 'app' logs
|
||||
curl -N http://localhost:8080/app/stream
|
||||
|
||||
# Status for 'database' stream
|
||||
curl http://localhost:8080/database/status
|
||||
|
||||
# Custom path example
|
||||
curl -N http://localhost:8080/api/logs
|
||||
```
|
||||
|
||||
### Global Status
|
||||
|
||||
Router mode provides a global status endpoint:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8080/status | jq .
|
||||
```
|
||||
|
||||
Returns aggregated information:
|
||||
```json
|
||||
{
|
||||
"service": "LogWisp Router",
|
||||
"version": "1.0.0",
|
||||
"port": 8080,
|
||||
"total_streams": 3,
|
||||
"streams": {
|
||||
"app": { /* stream stats */ },
|
||||
"database": { /* stream stats */ },
|
||||
"nginx": { /* stream stats */ }
|
||||
},
|
||||
"router": {
|
||||
"uptime_seconds": 3600,
|
||||
"total_requests": 15234,
|
||||
"routed_requests": 15220,
|
||||
"failed_requests": 14
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Port Sharing
|
||||
|
||||
### How It Works
|
||||
|
||||
1. Router server listens on configured ports
|
||||
2. Examines request path to determine target stream
|
||||
3. Routes request to appropriate stream handler
|
||||
4. Stream handles request as if standalone
|
||||
|
||||
### Port Assignment Rules
|
||||
|
||||
In router mode:
|
||||
- Multiple streams can use the same port
|
||||
- Router detects and consolidates shared ports
|
||||
- Each unique port gets one router server
|
||||
- TCP servers remain independent (no routing)
|
||||
|
||||
Example with multiple ports:
|
||||
|
||||
```toml
|
||||
# Streams 1-3 share port 8080
|
||||
[[streams]]
|
||||
name = "app"
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
|
||||
[[streams]]
|
||||
name = "db"
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
|
||||
[[streams]]
|
||||
name = "web"
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
|
||||
# Stream 4 uses different port
|
||||
[[streams]]
|
||||
name = "admin"
|
||||
[streams.httpserver]
|
||||
port = 9090
|
||||
|
||||
# Result: 2 router servers (8080 and 9090)
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Microservices Architecture
|
||||
|
||||
Route logs from different services:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "frontend"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/frontend", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
|
||||
[[streams]]
|
||||
name = "backend"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/backend", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
|
||||
[[streams]]
|
||||
name = "worker"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/var/log/worker", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080
|
||||
```
|
||||
|
||||
Access via:
|
||||
- Frontend logs: `http://localhost:8080/frontend/stream`
|
||||
- Backend logs: `http://localhost:8080/backend/stream`
|
||||
- Worker logs: `http://localhost:8080/worker/stream`
|
||||
|
||||
### Environment-Based Routing
|
||||
|
||||
Different log levels per environment:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "prod"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/logs/prod", pattern = "*.log" }]
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = ["ERROR", "WARN"]
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
|
||||
[[streams]]
|
||||
name = "staging"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/logs/staging", pattern = "*.log" }]
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = ["ERROR", "WARN", "INFO"]
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
|
||||
[[streams]]
|
||||
name = "dev"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/logs/dev", pattern = "*.log" }]
|
||||
# No filters - all logs
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
```
|
||||
|
||||
### Department Access
|
||||
|
||||
Separate streams for different teams:
|
||||
|
||||
```toml
|
||||
[[streams]]
|
||||
name = "engineering"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/logs/apps", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 50.0
|
||||
|
||||
[[streams]]
|
||||
name = "security"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/logs/audit", pattern = "*.log" }]
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 5.0
|
||||
max_connections_per_ip = 1
|
||||
|
||||
[[streams]]
|
||||
name = "support"
|
||||
[streams.monitor]
|
||||
targets = [{ path = "/logs/customer", pattern = "*.log" }]
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = ["password", "token", "secret"]
|
||||
[streams.httpserver]
|
||||
port = 8080
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Mixed Mode Deployment
|
||||
|
||||
Combine router and standalone modes:
|
||||
|
||||
```toml
|
||||
# Public streams via router
|
||||
[[streams]]
|
||||
name = "public-api"
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080 # Router mode
|
||||
|
||||
[[streams]]
|
||||
name = "public-web"
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 8080 # Router mode
|
||||
|
||||
# Internal stream standalone
|
||||
[[streams]]
|
||||
name = "internal"
|
||||
[streams.httpserver]
|
||||
enabled = true
|
||||
port = 9999 # Different port, standalone
|
||||
|
||||
# High-performance TCP
|
||||
[[streams]]
|
||||
name = "metrics"
|
||||
[streams.tcpserver]
|
||||
enabled = true
|
||||
port = 9090 # TCP not affected by router
|
||||
```
|
||||
|
||||
### Load Balancer Integration
|
||||
|
||||
Router mode works well with load balancers:
|
||||
|
||||
```nginx
|
||||
# Nginx configuration
|
||||
upstream logwisp {
|
||||
server logwisp1:8080;
|
||||
server logwisp2:8080;
|
||||
server logwisp3:8080;
|
||||
}
|
||||
|
||||
location /logs/ {
|
||||
proxy_pass http://logwisp/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
}
|
||||
```
|
||||
|
||||
Access becomes:
|
||||
- `https://example.com/logs/app/stream`
|
||||
- `https://example.com/logs/database/stream`
|
||||
- `https://example.com/logs/status`
|
||||
|
||||
### Path-Based Access Control
|
||||
|
||||
Use reverse proxy for authentication:
|
||||
|
||||
```nginx
|
||||
# Require auth for security logs
|
||||
location /logs/security/ {
|
||||
auth_basic "Security Logs";
|
||||
auth_basic_user_file /etc/nginx/security.htpasswd;
|
||||
proxy_pass http://localhost:8080/security/;
|
||||
}
|
||||
|
||||
# Public access for status
|
||||
location /logs/app/ {
|
||||
proxy_pass http://localhost:8080/app/;
|
||||
}
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
### Router Mode Limitations
|
||||
|
||||
1. **HTTP Only**: Router mode only works for HTTP/SSE streams
|
||||
2. **No TCP Routing**: TCP streams remain on separate ports
|
||||
3. **Path Conflicts**: Stream names must be unique
|
||||
4. **Same Config**: All streams on a port share SSL/auth settings
|
||||
|
||||
### When Not to Use Router Mode
|
||||
|
||||
- High-performance scenarios (use TCP)
|
||||
- Streams need different SSL certificates
|
||||
- Complex authentication per stream
|
||||
- Network isolation requirements
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Path not found"
|
||||
|
||||
Check available routes:
|
||||
```bash
|
||||
curl http://localhost:8080/invalid-path
|
||||
```
|
||||
|
||||
Response shows available routes:
|
||||
```json
|
||||
{
|
||||
"error": "Not Found",
|
||||
"requested_path": "/invalid-path",
|
||||
"available_routes": [
|
||||
"/status (global status)",
|
||||
"/app/stream (stream: app)",
|
||||
"/app/status (status: app)",
|
||||
"/database/stream (stream: database)",
|
||||
"/database/status (status: database)"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### "Port conflict"
|
||||
|
||||
If you see port conflicts:
|
||||
1. Ensure `--router` flag is used
|
||||
2. Check all streams have `httpserver.enabled = true`
|
||||
3. Verify no other services use the port
|
||||
|
||||
### Debug Routing
|
||||
|
||||
Enable debug logging:
|
||||
```bash
|
||||
logwisp --router --log-level debug
|
||||
```
|
||||
|
||||
Look for routing decisions:
|
||||
```
|
||||
Router request method=GET path=/app/stream remote_addr=127.0.0.1:54321
|
||||
Routing request to stream stream=app original_path=/app/stream remaining_path=/stream
|
||||
```
|
||||
|
||||
### Performance Impact
|
||||
|
||||
Router mode adds minimal overhead:
|
||||
- ~100-200ns per request for path matching
|
||||
- Negligible memory overhead
|
||||
- No impact on streaming performance
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
Use clear, consistent stream names:
|
||||
```toml
|
||||
# Good: Clear purpose
|
||||
name = "frontend-prod"
|
||||
name = "backend-staging"
|
||||
name = "worker-payments"
|
||||
|
||||
# Bad: Ambiguous
|
||||
name = "logs1"
|
||||
name = "stream2"
|
||||
name = "test"
|
||||
```
|
||||
|
||||
### Path Organization
|
||||
|
||||
Group related streams:
|
||||
```
|
||||
/prod/frontend/stream
|
||||
/prod/backend/stream
|
||||
/staging/frontend/stream
|
||||
/staging/backend/stream
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
Document your routing structure:
|
||||
```toml
|
||||
# Stream for production API logs
|
||||
# Access: https://logs.example.com/api-prod/stream
|
||||
[[streams]]
|
||||
name = "api-prod"
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
Use global status for overview:
|
||||
```bash
|
||||
# Monitor all streams
|
||||
watch -n 5 'curl -s localhost:8080/status | jq .streams'
|
||||
|
||||
# Check specific stream
|
||||
curl -s localhost:8080/status | jq '.streams.app'
|
||||
```
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From Standalone to Router
|
||||
|
||||
1. **Update configuration** - ensure consistent ports:
|
||||
```toml
|
||||
# Change from different ports
|
||||
[streams.httpserver]
|
||||
port = 8080 # Was 8081, 8082, etc.
|
||||
```
|
||||
|
||||
2. **Start with router flag**:
|
||||
```bash
|
||||
logwisp --router --config existing.toml
|
||||
```
|
||||
|
||||
3. **Update client URLs**:
|
||||
```bash
|
||||
# Old: http://localhost:8081/stream
|
||||
# New: http://localhost:8080/streamname/stream
|
||||
```
|
||||
|
||||
4. **Update monitoring**:
|
||||
```bash
|
||||
# Global status now available
|
||||
curl http://localhost:8080/status
|
||||
```
|
||||
|
||||
### Gradual Migration
|
||||
|
||||
Run both modes during transition:
|
||||
```bash
|
||||
# Week 1: Run standalone (current)
|
||||
logwisp --config prod.toml
|
||||
|
||||
# Week 2: Run both
|
||||
logwisp --config prod.toml & # Standalone
|
||||
logwisp --router --config prod-router.toml & # Router
|
||||
|
||||
# Week 3: Router only
|
||||
logwisp --router --config prod.toml
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Configuration Guide](configuration.md) - Stream configuration
|
||||
- [HTTP Streaming](api.md#http-sse) - SSE protocol details
|
||||
- [Load Balancing](integrations.md#load-balancers) - Integration patterns
|
||||
- [Security Best Practices](security.md) - Securing router deployments
|
||||
537
doc/troubleshooting.md
Normal file
537
doc/troubleshooting.md
Normal file
@ -0,0 +1,537 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
This guide helps diagnose and resolve common issues with LogWisp.
|
||||
|
||||
## Diagnostic Tools
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
The first step in troubleshooting is enabling debug logs:
|
||||
|
||||
```bash
|
||||
# Via command line
|
||||
logwisp --log-level debug --log-output stderr
|
||||
|
||||
# Via environment
|
||||
export LOGWISP_LOGGING_LEVEL=debug
|
||||
logwisp
|
||||
|
||||
# Via config
|
||||
[logging]
|
||||
level = "debug"
|
||||
output = "stderr"
|
||||
```
|
||||
|
||||
### Check Status Endpoint
|
||||
|
||||
Verify LogWisp is running and processing:
|
||||
|
||||
```bash
|
||||
# Basic check
|
||||
curl http://localhost:8080/status
|
||||
|
||||
# Pretty print
|
||||
curl -s http://localhost:8080/status | jq .
|
||||
|
||||
# Check specific metrics
|
||||
curl -s http://localhost:8080/status | jq '.monitor'
|
||||
```
|
||||
|
||||
### Test Log Streaming
|
||||
|
||||
Verify streams are working:
|
||||
|
||||
```bash
|
||||
# Test SSE stream (should show heartbeats if enabled)
|
||||
curl -N http://localhost:8080/stream
|
||||
|
||||
# Test with timeout
|
||||
timeout 5 curl -N http://localhost:8080/stream
|
||||
|
||||
# Test TCP stream
|
||||
nc localhost 9090
|
||||
```
|
||||
|
||||
## Common Issues
|
||||
|
||||
### No Logs Appearing
|
||||
|
||||
**Symptoms:**
|
||||
- Stream connects but no log entries appear
|
||||
- Status shows `total_entries: 0`
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
1. Check monitor configuration:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '.monitor'
|
||||
```
|
||||
|
||||
2. Verify file paths exist:
|
||||
```bash
|
||||
# Check your configured paths
|
||||
ls -la /var/log/myapp/
|
||||
```
|
||||
|
||||
3. Check file permissions:
|
||||
```bash
|
||||
# LogWisp user must have read access
|
||||
sudo -u logwisp ls /var/log/myapp/
|
||||
```
|
||||
|
||||
4. Verify files match pattern:
|
||||
```bash
|
||||
# If pattern is "*.log"
|
||||
ls /var/log/myapp/*.log
|
||||
```
|
||||
|
||||
5. Check if files are being updated:
|
||||
```bash
|
||||
# Should show recent timestamps
|
||||
ls -la /var/log/myapp/*.log
|
||||
tail -f /var/log/myapp/app.log
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Fix file permissions:
|
||||
```bash
|
||||
sudo chmod 644 /var/log/myapp/*.log
|
||||
sudo usermod -a -G adm logwisp # Add to log group
|
||||
```
|
||||
|
||||
- Correct path configuration:
|
||||
```toml
|
||||
targets = [
|
||||
{ path = "/correct/path/to/logs", pattern = "*.log" }
|
||||
]
|
||||
```
|
||||
|
||||
- Use absolute paths:
|
||||
```toml
|
||||
# Bad: Relative path
|
||||
targets = [{ path = "./logs", pattern = "*.log" }]
|
||||
|
||||
# Good: Absolute path
|
||||
targets = [{ path = "/var/log/app", pattern = "*.log" }]
|
||||
```
|
||||
|
||||
### High CPU Usage
|
||||
|
||||
**Symptoms:**
|
||||
- LogWisp process using excessive CPU
|
||||
- System slowdown
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
1. Check process CPU:
|
||||
```bash
|
||||
top -p $(pgrep logwisp)
|
||||
```
|
||||
|
||||
2. Review check intervals:
|
||||
```bash
|
||||
grep check_interval /etc/logwisp/logwisp.toml
|
||||
```
|
||||
|
||||
3. Count active watchers:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '.monitor.active_watchers'
|
||||
```
|
||||
|
||||
4. Check filter complexity:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '.filters'
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Increase check interval:
|
||||
```toml
|
||||
[streams.monitor]
|
||||
check_interval_ms = 1000 # Was 50ms
|
||||
```
|
||||
|
||||
- Reduce watched files:
|
||||
```toml
|
||||
# Instead of watching entire directory
|
||||
targets = [
|
||||
{ path = "/var/log/specific-app.log", is_file = true }
|
||||
]
|
||||
```
|
||||
|
||||
- Simplify filter patterns:
|
||||
```toml
|
||||
# Complex regex (slow)
|
||||
patterns = ["^\\[\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\]\\s+\\[(ERROR|WARN)\\]"]
|
||||
|
||||
# Simple patterns (fast)
|
||||
patterns = ["ERROR", "WARN"]
|
||||
```
|
||||
|
||||
### Memory Growth
|
||||
|
||||
**Symptoms:**
|
||||
- Increasing memory usage over time
|
||||
- Eventually runs out of memory
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
1. Monitor memory usage:
|
||||
```bash
|
||||
watch -n 10 'ps aux | grep logwisp'
|
||||
```
|
||||
|
||||
2. Check connection count:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '.server.active_clients'
|
||||
```
|
||||
|
||||
3. Check for dropped entries:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '.monitor.dropped_entries'
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Limit connections:
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
max_connections_per_ip = 5
|
||||
max_total_connections = 100
|
||||
```
|
||||
|
||||
- Reduce buffer sizes:
|
||||
```toml
|
||||
[streams.httpserver]
|
||||
buffer_size = 500 # Was 5000
|
||||
```
|
||||
|
||||
- Enable rate limiting:
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
enabled = true
|
||||
requests_per_second = 10.0
|
||||
```
|
||||
|
||||
### Connection Refused
|
||||
|
||||
**Symptoms:**
|
||||
- Cannot connect to LogWisp
|
||||
- `curl: (7) Failed to connect`
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
1. Check if LogWisp is running:
|
||||
```bash
|
||||
ps aux | grep logwisp
|
||||
systemctl status logwisp
|
||||
```
|
||||
|
||||
2. Verify listening ports:
|
||||
```bash
|
||||
sudo netstat -tlnp | grep logwisp
|
||||
# or
|
||||
sudo ss -tlnp | grep logwisp
|
||||
```
|
||||
|
||||
3. Check firewall:
|
||||
```bash
|
||||
sudo iptables -L -n | grep 8080
|
||||
sudo ufw status
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Start the service:
|
||||
```bash
|
||||
sudo systemctl start logwisp
|
||||
```
|
||||
|
||||
- Fix port configuration:
|
||||
```toml
|
||||
[streams.httpserver]
|
||||
enabled = true # Must be true
|
||||
port = 8080 # Correct port
|
||||
```
|
||||
|
||||
- Open firewall:
|
||||
```bash
|
||||
sudo ufw allow 8080/tcp
|
||||
```
|
||||
|
||||
### Rate Limit Errors
|
||||
|
||||
**Symptoms:**
|
||||
- HTTP 429 responses
|
||||
- "Rate limit exceeded" errors
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
1. Check rate limit stats:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '.features.rate_limit'
|
||||
```
|
||||
|
||||
2. Test rate limits:
|
||||
```bash
|
||||
# Rapid requests
|
||||
for i in {1..20}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/status; done
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Increase rate limits:
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
requests_per_second = 50.0 # Was 10.0
|
||||
burst_size = 100 # Was 20
|
||||
```
|
||||
|
||||
- Use per-IP limiting:
|
||||
```toml
|
||||
limit_by = "ip" # Instead of "global"
|
||||
```
|
||||
|
||||
- Disable for internal use:
|
||||
```toml
|
||||
enabled = false
|
||||
```
|
||||
|
||||
### Filter Not Working
|
||||
|
||||
**Symptoms:**
|
||||
- Unwanted logs still appearing
|
||||
- Wanted logs being filtered out
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
1. Check filter configuration:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '.filters'
|
||||
```
|
||||
|
||||
2. Test patterns:
|
||||
```bash
|
||||
# Test regex pattern
|
||||
echo "ERROR: test message" | grep -E "your-pattern"
|
||||
```
|
||||
|
||||
3. Enable debug logging to see filter decisions:
|
||||
```bash
|
||||
logwisp --log-level debug 2>&1 | grep filter
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Fix pattern syntax:
|
||||
```toml
|
||||
# Word boundaries
|
||||
patterns = ["\\bERROR\\b"] # Not "ERROR" which matches "TERROR"
|
||||
|
||||
# Case insensitive
|
||||
patterns = ["(?i)error"]
|
||||
```
|
||||
|
||||
- Check filter order:
|
||||
```toml
|
||||
# Include filters run first
|
||||
[[streams.filters]]
|
||||
type = "include"
|
||||
patterns = ["ERROR", "WARN"]
|
||||
|
||||
# Then exclude filters
|
||||
[[streams.filters]]
|
||||
type = "exclude"
|
||||
patterns = ["IGNORE_THIS"]
|
||||
```
|
||||
|
||||
- Use correct logic:
|
||||
```toml
|
||||
logic = "or" # Match ANY pattern
|
||||
# not
|
||||
logic = "and" # Match ALL patterns
|
||||
```
|
||||
|
||||
### Logs Dropping
|
||||
|
||||
**Symptoms:**
|
||||
- `dropped_entries` counter increasing
|
||||
- Missing log entries in stream
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
1. Check drop statistics:
|
||||
```bash
|
||||
curl -s http://localhost:8080/status | jq '{
|
||||
dropped: .monitor.dropped_entries,
|
||||
total: .monitor.total_entries,
|
||||
percent: (.monitor.dropped_entries / .monitor.total_entries * 100)
|
||||
}'
|
||||
```
|
||||
|
||||
2. Monitor drop rate:
|
||||
```bash
|
||||
watch -n 5 'curl -s http://localhost:8080/status | jq .monitor.dropped_entries'
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Increase buffer sizes:
|
||||
```toml
|
||||
[streams.httpserver]
|
||||
buffer_size = 5000 # Was 1000
|
||||
```
|
||||
|
||||
- Add flow control:
|
||||
```toml
|
||||
[streams.monitor]
|
||||
check_interval_ms = 500 # Slow down reading
|
||||
```
|
||||
|
||||
- Reduce clients:
|
||||
```toml
|
||||
[streams.httpserver.rate_limit]
|
||||
max_total_connections = 50
|
||||
```
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow Response Times
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
# Measure response time
|
||||
time curl -s http://localhost:8080/status > /dev/null
|
||||
|
||||
# Check system load
|
||||
uptime
|
||||
top
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
- Reduce concurrent operations
|
||||
- Increase system resources
|
||||
- Use TCP instead of HTTP for high volume
|
||||
|
||||
### Network Bandwidth
|
||||
|
||||
**Diagnosis:**
|
||||
```bash
|
||||
# Monitor network usage
|
||||
iftop -i eth0 -f "port 8080"
|
||||
|
||||
# Check connection count
|
||||
ss -tan | grep :8080 | wc -l
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
- Enable compression (future feature)
|
||||
- Filter more aggressively
|
||||
- Use TCP for local connections
|
||||
|
||||
## Debug Commands
|
||||
|
||||
### System Information
|
||||
|
||||
```bash
|
||||
# LogWisp version
|
||||
logwisp --version
|
||||
|
||||
# System resources
|
||||
free -h
|
||||
df -h
|
||||
ulimit -a
|
||||
|
||||
# Network state
|
||||
ss -tlnp
|
||||
netstat -anp | grep logwisp
|
||||
```
|
||||
|
||||
### Process Inspection
|
||||
|
||||
```bash
|
||||
# Process details
|
||||
ps aux | grep logwisp
|
||||
|
||||
# Open files
|
||||
lsof -p $(pgrep logwisp)
|
||||
|
||||
# System calls (Linux)
|
||||
strace -p $(pgrep logwisp) -e trace=open,read,write
|
||||
|
||||
# File system activity
|
||||
inotifywait -m /var/log/myapp/
|
||||
```
|
||||
|
||||
### Configuration Validation
|
||||
|
||||
```bash
|
||||
# Test configuration
|
||||
logwisp --config test.toml --log-level debug --log-output stderr
|
||||
|
||||
# Check file syntax
|
||||
cat /etc/logwisp/logwisp.toml | grep -E "^\s*\["
|
||||
|
||||
# Validate TOML
|
||||
python3 -m pip install toml
|
||||
python3 -c "import toml; toml.load('/etc/logwisp/logwisp.toml'); print('Valid')"
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Collect Diagnostic Information
|
||||
|
||||
Create a diagnostic bundle:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# diagnostic.sh
|
||||
|
||||
DIAG_DIR="logwisp-diag-$(date +%Y%m%d-%H%M%S)"
|
||||
mkdir -p "$DIAG_DIR"
|
||||
|
||||
# Version
|
||||
logwisp --version > "$DIAG_DIR/version.txt" 2>&1
|
||||
|
||||
# Configuration (sanitized)
|
||||
grep -v "password\|secret\|token" /etc/logwisp/logwisp.toml > "$DIAG_DIR/config.toml"
|
||||
|
||||
# Status
|
||||
curl -s http://localhost:8080/status > "$DIAG_DIR/status.json"
|
||||
|
||||
# System info
|
||||
uname -a > "$DIAG_DIR/system.txt"
|
||||
free -h >> "$DIAG_DIR/system.txt"
|
||||
df -h >> "$DIAG_DIR/system.txt"
|
||||
|
||||
# Process info
|
||||
ps aux | grep logwisp > "$DIAG_DIR/process.txt"
|
||||
lsof -p $(pgrep logwisp) > "$DIAG_DIR/files.txt" 2>&1
|
||||
|
||||
# Recent logs
|
||||
journalctl -u logwisp -n 1000 > "$DIAG_DIR/logs.txt" 2>&1
|
||||
|
||||
# Create archive
|
||||
tar -czf "$DIAG_DIR.tar.gz" "$DIAG_DIR"
|
||||
rm -rf "$DIAG_DIR"
|
||||
|
||||
echo "Diagnostic bundle created: $DIAG_DIR.tar.gz"
|
||||
```
|
||||
|
||||
### Report Issues
|
||||
|
||||
When reporting issues, include:
|
||||
1. LogWisp version
|
||||
2. Configuration (sanitized)
|
||||
3. Error messages
|
||||
4. Steps to reproduce
|
||||
5. Diagnostic bundle
|
||||
|
||||
## See Also
|
||||
|
||||
- [Monitoring Guide](monitoring.md) - Status and metrics
|
||||
- [Performance Tuning](performance.md) - Optimization
|
||||
- [Configuration Guide](configuration.md) - Settings reference
|
||||
- [FAQ](faq.md) - Frequently asked questions
|
||||
Reference in New Issue
Block a user