funcwatch 0.1.0
A Python decorator for comprehensive function monitoring with execution timing, memory usage tracking, CPU monitoring, input/output validation, and structured logging.
Requires Python: >=3.11

# Function Monitor

A Python decorator for comprehensive function monitoring with execution timing, memory usage tracking, CPU monitoring, input/output validation, and structured logging.

## Features

- **Execution Monitoring**: Track function execution time, memory usage, and CPU utilization
- **Input/Output Validation**: Automatic validation using Pydantic models and type hints
- **Structured Logging**: Configurable structured logging with support for file output
- **Error Handling**: Comprehensive exception handling and error reporting
- **Flexible Configuration**: Global and per-function configuration options
- **Production Ready**: Designed for production use with proper error handling and performance considerations

## Installation

```bash
pip install function-monitor
```

## Quick Start

```python
from function_monitor import monitor_function

@monitor_function()
def add_numbers(a: int, b: int) -> int:
    return a + b

result = add_numbers(5, 3)
print(result)
```

This will output a dictionary containing the function result, execution metrics, and monitoring data:

```python
{
    'result': 8,
    'status': 'success',
    'errors': None,
    'execution_time': 0.0001,
    'memory_usage': {
        'before': 15728640,
        'after': 15728640,
        'peak': 15728640,
        'delta': 0
    },
    'cpu_usage': 0.0,
    'timestamp': '2024-01-15T10:30:45.123456',
    'function_name': 'add_numbers'
}
```

## Configuration

### Global Configuration

Configure the monitor globally for your application:

```python
from function_monitor import configure_monitor

configure_monitor(
    log_to_file=True,
    log_file_path="./app_monitor.log",
    log_level=20,  # INFO level
    default_return_raw_result=True
)
```

### Environment Variables

You can also configure using environment variables:

```bash
export FUNCTION_MONITOR_LOG_LEVEL=10  # DEBUG level
export FUNCTION_MONITOR_LOG_TO_FILE=true
export FUNCTION_MONITOR_LOG_FILE=./debug.log
```

### Per-Function Configuration

Override global settings for specific functions:

```python
@monitor_function(
    validate_input=True,
    validate_output=False,
    log_level="DEBUG",
    return_raw_result=True
)
def my_function(x: int) -> str:
    return str(x * 2)
```

## Advanced Usage

### Input/Output Validation with Pydantic

```python
from pydantic import BaseModel
from function_monitor import monitor_function

class User(BaseModel):
    name: str
    age: int
    email: str

class UserResponse(BaseModel):
    user: User
    message: str

@monitor_function(validate_input=True, validate_output=True)
def create_user(user_data: User) -> UserResponse:
    return UserResponse(
        user=user_data,
        message=f"User {user_data.name} created successfully"
    )

# Usage
user = User(name="John Doe", age=30, email="john@example.com")
result = create_user(user)
```

### Custom Monitor Instances

```python
from function_monitor import FunctionMonitor

# Create custom monitor with specific settings
production_monitor = FunctionMonitor(
    validate_input=True,
    validate_output=True,
    log_execution=True,
    return_raw_result=True
)

debug_monitor = FunctionMonitor(
    validate_input=True,
    validate_output=False,
    log_level="DEBUG",
    return_raw_result=False
)

@production_monitor
def critical_function(data: dict) -> dict:
    # Process critical data
    return {"processed": True}

@debug_monitor
def experimental_function(x: int) -> int:
    # Experimental code
    return x ** 2
```

### Error Handling

The monitor automatically captures and reports errors:

```python
@monitor_function()
def divide_numbers(a: float, b: float) -> float:
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

result = divide_numbers(10, 0)
print(result["status"])  # "error"
print(result["errors"])  # List of error messages
```

### Memory and CPU Monitoring

Monitor resource usage:

```python
@monitor_function(
    enable_memory_monitoring=True,
    enable_cpu_monitoring=True
)
def memory_intensive_function(size: int) -> list:
    # Create a large list
    return list(range(size))

result = memory_intensive_function(1000000)
print(result["memory_usage"])  # Memory usage statistics
print(result["cpu_usage"])     # CPU usage percentage
```

## Configuration Options

### Global Configuration Parameters

- `log_level`: Logging level (default: INFO)
- `log_to_file`: Enable file logging (default: False)
- `log_file_path`: Path to log file (default: "./function_monitor.log")
- `log_file_max_size`: Maximum log file size in bytes (default: 10MB)
- `log_file_backup_count`: Number of backup log files (default: 5)
- `default_validate_input`: Default input validation setting (default: True)
- `default_validate_output`: Default output validation setting (default: True)
- `default_log_execution`: Default execution logging setting (default: True)
- `default_return_raw_result`: Default return format setting (default: False)
- `enable_memory_monitoring`: Enable memory monitoring (default: True)
- `enable_cpu_monitoring`: Enable CPU monitoring (default: True)

### Decorator Parameters

- `validate_input`: Enable input validation using type hints
- `validate_output`: Enable output validation using type hints
- `log_execution`: Enable structured logging
- `log_level`: Log level for the function ("DEBUG", "INFO", "WARNING", "ERROR")
- `return_raw_result`: Return original result on success, structured format on error
- `enable_memory_monitoring`: Enable memory usage monitoring
- `enable_cpu_monitoring`: Enable CPU usage monitoring

## Logging

The monitor uses structured logging with configurable output formats:

### Console Logging (Development)
```python
configure_monitor(log_to_file=False)  # Logs to console with readable format
```

### File Logging (Production)
```python
configure_monitor(
    log_to_file=True,
    log_file_path="/var/log/myapp/monitor.log"
)  # Logs to file in JSON format
```

### Custom Log Levels
```python
@monitor_function(log_level="DEBUG")
def debug_function():
    pass

@monitor_function(log_level="ERROR")  # Only log errors
def critical_function():
    pass
```

## Best Practices

1. **Production Usage**: Use `return_raw_result=True` in production to avoid overhead
2. **Input Validation**: Enable input validation for external-facing functions
3. **Log File Management**: Configure log rotation to prevent disk space issues
4. **Selective Monitoring**: Disable CPU/memory monitoring for high-frequency functions if needed
5. **Error Handling**: Always handle the case where monitoring might return error status

```python
# Production configuration example
configure_monitor(
    log_to_file=True,
    log_file_path="/var/log/myapp/function_monitor.log",
    log_level=20,  # INFO
    default_return_raw_result=True,
    default_validate_input=True,
    default_validate_output=False  # Disable output validation for performance
)

@monitor_function()
def api_endpoint(request_data: RequestModel) -> ResponseModel:
    # Your business logic here
    return process_request(request_data)

# Usage with error handling
result = api_endpoint(request)
if isinstance(result, dict) and result.get("status") == "error":
    # Handle error case
    logger.error("Function failed", errors=result["errors"])
    return error_response()
else:
    # Success case - result is the actual return value
    return result
```

## Performance Considerations

- **Minimal Overhead**: The monitor is designed to have minimal impact on function performance
- **Selective Monitoring**: Disable features you don't need for better performance
- **Memory Monitoring**: Uses `psutil` for accurate memory measurements
- **CPU Monitoring**: Lightweight CPU usage tracking
- **Logging**: Structured logging with configurable levels to reduce I/O overhead

## Error Handling and Debugging

### Common Issues

1. **Import Errors**: Ensure all dependencies are installed
2. **Permission Issues**: Check file permissions for log files
3. **Memory Monitoring**: Requires `psutil` package
4. **Type Validation**: Requires proper type hints for validation to work

### Debug Mode

Enable debug mode for detailed logging:

```python
from function_monitor import configure_monitor

configure_monitor(
    log_level=10,  # DEBUG level
    log_to_file=True,
    log_file_path="./debug.log"
)
```

## API Reference

### Decorators

#### `monitor_function(**kwargs)`
Main decorator for function monitoring.

**Parameters:**
- `validate_input` (bool): Enable input validation
- `validate_output` (bool): Enable output validation  
- `log_execution` (bool): Enable execution logging
- `log_level` (str): Logging level
- `return_raw_result` (bool): Return format preference
- `enable_memory_monitoring` (bool): Enable memory tracking
- `enable_cpu_monitoring` (bool): Enable CPU tracking

#### `FunctionMonitor(**kwargs)`
Class-based monitor for reusable configurations.

### Configuration Functions

#### `configure_monitor(**kwargs)`
Configure global monitoring settings.

#### `get_config() -> MonitorConfig`
Get current global configuration.

#### `set_config(config: MonitorConfig)`
Set global configuration from MonitorConfig instance.

### Models

#### `ExecutionResult`
- `result`: Function return value
- `status`: "success" or "error"
- `errors`: List of error messages (if any)
- `execution_time`: Execution time in seconds
- `memory_usage`: Memory usage statistics
- `cpu_usage`: CPU usage percentage
- `timestamp`: ISO timestamp
- `function_name`: Function name

#### `MemoryUsage`
- `before`: Memory before execution (bytes)
- `after`: Memory after execution (bytes)
- `peak`: Peak memory usage (bytes)
- `delta`: Memory difference (bytes)

## Testing

Run the test suite:

```bash
# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=function_monitor

# Run specific test file
pytest tests/test_monitor.py
```

## Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Add tests for new functionality
5. Run the test suite (`pytest`)
6. Commit your changes (`git commit -m 'Add amazing feature'`)
7. Push to the branch (`git push origin feature/amazing-feature`)
8. Open a Pull Request

### Development Setup

```bash
# Clone the repository
git clone https://github.com/yourusername/function-monitor.git
cd function-monitor

# Install in development mode
pip install -e ".[dev]"

# Install pre-commit hooks
pre-commit install

# Run tests
pytest
```

## Changelog

### v0.1.0 (Initial Release)
- Basic function monitoring with execution timing
- Memory and CPU usage tracking
- Input/output validation with Pydantic
- Structured logging with configurable output
- Comprehensive error handling
- Global and per-function configuration
- Production-ready design

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Support

- **Issues**: [GitHub Issues](https://github.com/yourusername/function-monitor/issues)
- **Documentation**: [Read the Docs](https://function-monitor.readthedocs.io/)
- **PyPI**: [PyPI Package](https://pypi.org/project/function-monitor/)

## Acknowledgments

- Built with [Pydantic](https://pydantic-docs.helpmanual.io/) for data validation
- Uses [structlog](https://www.structlog.org/) for structured logging
- System monitoring powered by [psutil](https://psutil.readthedocs.io/)

---

**Note**: This package is designed for production use but always test thoroughly in your specific environment before deploying to production.