Add comprehensive PHP metrics tracking documentation
DOCUMENTATION: Complete guide to PHP configuration hierarchy and metrics CRITICAL ADDITIONS: 1. PHP Config Hierarchy (.user.ini > pool php.ini > global) 2. How to determine which config takes effect 3. 70+ PHP settings to track with explanations COMPREHENSIVE METRICS COVERAGE: **Memory Settings:** - memory_limit, upload_max_filesize, post_max_size - max_input_vars, realpath_cache_size - Detection: memory exhausted errors, upload failures **PHP-FPM Pool Settings (MOST CRITICAL!):** - pm (static/dynamic/ondemand modes) - pm.max_children, pm.start_servers, pm.min/max_spare_servers - pm.max_requests, pm.process_idle_timeout - request_terminate_timeout, request_slowlog_timeout - Detection: max_children reached errors, slow logs **OPcache (MASSIVE PERFORMANCE!):** - opcache.enable, opcache.memory_consumption - opcache.max_accelerated_files - opcache.jit, opcache.jit_buffer_size (PHP 8+) - Hit rate calculation, cache effectiveness **Execution & Timeout:** - max_execution_time, max_input_time - default_socket_timeout - Detection: timeout errors **Session Management:** - session.save_handler (files/redis/memcached) - session.gc_maxlifetime - Performance impact analysis **Security Settings:** - disable_functions, open_basedir - display_errors (MUST be Off in production!) - allow_url_include prevention **APCu Cache:** - apc.shm_size, apc.ttl - User cache tracking **Detection Commands:** - Find all php.ini files affecting domain - Get effective settings hierarchy - Check opcache hit rates - Find max_children errors - Track slow requests - Calculate memory per process **Per-Domain Metrics Matrix:** Complete YAML template showing all tracked metrics, live stats, issue detection, and recommendations This documentation enables intelligent optimization with precise detection and actionable recommendations!
This commit is contained in:
@@ -0,0 +1,469 @@
|
||||
# Comprehensive PHP Metrics Tracking Guide
|
||||
|
||||
## PHP Configuration Hierarchy & Detection
|
||||
|
||||
### Configuration File Priority (Highest to Lowest)
|
||||
Understanding which config takes effect is critical for accurate optimization.
|
||||
|
||||
```
|
||||
1. .user.ini (per-directory, PHP-FPM only)
|
||||
Location: /home/user/public_html/.user.ini
|
||||
Scope: Specific directory and subdirectories
|
||||
Reloads: Automatically every user_ini.cache_ttl seconds (default 300)
|
||||
|
||||
2. .htaccess (Apache with mod_php only, NOT PHP-FPM!)
|
||||
Location: /home/user/public_html/.htaccess
|
||||
Scope: Directory-specific
|
||||
Note: Does NOT work with PHP-FPM!
|
||||
|
||||
3. php.ini (per-pool, cPanel EA-PHP)
|
||||
Location: /opt/cpanel/ea-php*/root/etc/php.ini
|
||||
Scope: All domains using that PHP version
|
||||
|
||||
4. Additional .ini files (per-pool)
|
||||
Location: /opt/cpanel/ea-php*/root/etc/php.d/*.ini
|
||||
Scope: Per PHP version, loaded alphabetically
|
||||
|
||||
5. Global php.ini
|
||||
Location: /etc/php.ini (legacy)
|
||||
Scope: System-wide fallback
|
||||
```
|
||||
|
||||
### How to Determine Effective Settings
|
||||
|
||||
**Method 1: Query via PHP (Most Accurate)**
|
||||
```bash
|
||||
# Get effective value for specific domain
|
||||
echo '<?php echo ini_get("memory_limit"); ?>' | \
|
||||
su -s /bin/bash $username -c "php -q -d open_basedir="
|
||||
|
||||
# Get ALL effective settings
|
||||
php -r 'print_r(ini_get_all());' > /tmp/php_all_settings.txt
|
||||
|
||||
# Per-domain via web request (if domain is accessible)
|
||||
curl -s "http://$domain/phpinfo.php" | grep -A1 "memory_limit"
|
||||
```
|
||||
|
||||
**Method 2: Parse Configuration Files**
|
||||
```bash
|
||||
# Find ALL possible config files affecting a domain
|
||||
find_php_configs() {
|
||||
local domain="$1"
|
||||
local user="$2"
|
||||
local php_version="$3" # e.g., "ea-php82"
|
||||
|
||||
# Priority order
|
||||
echo "=== Config Hierarchy for $domain ==="
|
||||
|
||||
# 1. .user.ini
|
||||
local user_ini="/home/$user/public_html/.user.ini"
|
||||
if [ -f "$user_ini" ]; then
|
||||
echo "1. .user.ini: $user_ini (HIGHEST PRIORITY)"
|
||||
grep -E "memory_limit|max_execution_time|upload_max_filesize" "$user_ini"
|
||||
fi
|
||||
|
||||
# 2. Pool-specific php.ini
|
||||
local pool_ini="/opt/cpanel/$php_version/root/etc/php.ini"
|
||||
if [ -f "$pool_ini" ]; then
|
||||
echo "2. Pool php.ini: $pool_ini"
|
||||
grep -E "memory_limit|max_execution_time|upload_max_filesize" "$pool_ini"
|
||||
fi
|
||||
|
||||
# 3. Additional .ini files
|
||||
local ini_dir="/opt/cpanel/$php_version/root/etc/php.d"
|
||||
if [ -d "$ini_dir" ]; then
|
||||
echo "3. Additional .ini files: $ini_dir/*.ini"
|
||||
grep -h -E "memory_limit|max_execution_time|upload_max_filesize" "$ini_dir"/*.ini 2>/dev/null
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
## Complete PHP Metrics to Track
|
||||
|
||||
### 1. **Memory Settings** (Critical for Performance)
|
||||
|
||||
```ini
|
||||
# Basic Memory
|
||||
memory_limit = 256M # Per-script memory limit
|
||||
# Track: Current value, recommended, % of total RAM
|
||||
|
||||
# Upload Limits (Related to Memory)
|
||||
upload_max_filesize = 64M # Max single file upload
|
||||
post_max_size = 128M # Max POST data (should be >= upload_max_filesize)
|
||||
max_input_vars = 1000 # Max input variables (forms with many fields)
|
||||
max_input_nesting_level = 64 # Max array nesting depth
|
||||
max_input_time = 60 # Max time parsing input data
|
||||
|
||||
# Realpath Cache (Memory for path resolution)
|
||||
realpath_cache_size = 4096K # Cache size for realpath() calls
|
||||
realpath_cache_ttl = 120 # TTL in seconds
|
||||
```
|
||||
|
||||
**Why Track:**
|
||||
- `memory_limit` too low → "Allowed memory size exhausted" errors
|
||||
- `post_max_size < upload_max_filesize` → Upload failures
|
||||
- `realpath_cache_size` too small → File I/O slowdowns
|
||||
|
||||
**Detection:**
|
||||
```bash
|
||||
# Find memory exhausted errors
|
||||
grep -r "Allowed memory size.*exhausted" /home/$user/*/logs/error_log
|
||||
|
||||
# Find upload failures
|
||||
grep -r "POST Content-Length.*exceeds" /home/$user/*/logs/error_log
|
||||
```
|
||||
|
||||
### 2. **Execution & Timeout Settings**
|
||||
|
||||
```ini
|
||||
# Script Execution
|
||||
max_execution_time = 30 # Max script runtime (seconds)
|
||||
max_input_time = 60 # Max time for input parsing
|
||||
default_socket_timeout = 60 # Default socket timeout
|
||||
|
||||
# CGI-specific
|
||||
cgi.force_redirect = 1
|
||||
cgi.fix_pathinfo = 0 # Security: prevent path injection
|
||||
```
|
||||
|
||||
**Why Track:**
|
||||
- `max_execution_time` too low → Scripts timeout on slow operations
|
||||
- Long-running cron jobs need higher limits
|
||||
|
||||
**Detection:**
|
||||
```bash
|
||||
# Find timeout errors
|
||||
grep -r "Maximum execution time.*exceeded" /home/$user/*/logs/error_log
|
||||
```
|
||||
|
||||
### 3. **PHP-FPM Pool Settings** (Most Critical for Optimization!)
|
||||
|
||||
```ini
|
||||
# Process Manager Type
|
||||
pm = dynamic # static | dynamic | ondemand
|
||||
# static: Fixed number of children
|
||||
# dynamic: Scales between min/max
|
||||
# ondemand: Spawns on-demand (saves memory)
|
||||
|
||||
# Process Limits (DYNAMIC mode)
|
||||
pm.max_children = 50 # Max simultaneous processes
|
||||
pm.start_servers = 5 # Processes started at boot
|
||||
pm.min_spare_servers = 5 # Minimum idle processes
|
||||
pm.max_spare_servers = 35 # Maximum idle processes
|
||||
|
||||
# Process Limits (STATIC mode)
|
||||
pm.max_children = 50 # Fixed number of processes
|
||||
|
||||
# Process Limits (ONDEMAND mode)
|
||||
pm.max_children = 50 # Max processes
|
||||
pm.process_idle_timeout = 10s # Kill idle process after X seconds
|
||||
|
||||
# Process Recycling
|
||||
pm.max_requests = 500 # Respawn after X requests (prevent memory leaks)
|
||||
|
||||
# Status & Monitoring
|
||||
pm.status_path = /fpm-status # Status page URL
|
||||
ping.path = /fpm-ping # Health check URL
|
||||
ping.response = pong
|
||||
|
||||
# Timeouts
|
||||
request_terminate_timeout = 30s # Kill request after X seconds (0 = disabled)
|
||||
request_slowlog_timeout = 5s # Log slow requests taking > X seconds
|
||||
|
||||
# Logging
|
||||
slowlog = /var/log/php-fpm/$pool-slow.log
|
||||
catch_workers_output = yes # Capture stdout/stderr
|
||||
php_admin_value[error_log] = /var/log/php-fpm/$pool-error.log
|
||||
```
|
||||
|
||||
**Why Track (CRITICAL!):**
|
||||
- `pm.max_children` too low → "server reached pm.max_children" errors → requests queue/fail
|
||||
- `pm.max_children` too high → OOM kills, server crashes
|
||||
- `pm = static` wastes memory on low-traffic sites
|
||||
- `pm = ondemand` adds latency (process spawn time)
|
||||
- `pm.max_requests = 0` → memory leaks never cleared
|
||||
|
||||
**Detection:**
|
||||
```bash
|
||||
# Find max_children errors (CRITICAL)
|
||||
grep "server reached pm.max_children" /opt/cpanel/ea-php*/root/usr/var/log/php-fpm/*error.log
|
||||
|
||||
# Find slow requests
|
||||
tail -100 /opt/cpanel/ea-php*/root/usr/var/log/php-fpm/*slow.log
|
||||
|
||||
# Current process count vs limit
|
||||
current=$(ps aux | grep "php-fpm: pool $domain" | grep -v grep | wc -l)
|
||||
max=$(grep "pm.max_children" /opt/cpanel/ea-php*/root/etc/php-fpm.d/$user.conf | cut -d'=' -f2)
|
||||
echo "Current: $current / Max: $max"
|
||||
```
|
||||
|
||||
### 4. **OPcache Settings** (Massive Performance Impact!)
|
||||
|
||||
```ini
|
||||
[opcache]
|
||||
; Enable/Disable
|
||||
opcache.enable = 1 # Enable opcache
|
||||
opcache.enable_cli = 0 # Disable for CLI (causes issues)
|
||||
|
||||
; Memory Settings
|
||||
opcache.memory_consumption = 128 # MB for opcache (CRITICAL!)
|
||||
opcache.interned_strings_buffer = 8 # MB for string interning
|
||||
opcache.max_accelerated_files = 10000 # Max cached files (set to > total PHP files)
|
||||
|
||||
; Validation & Updates
|
||||
opcache.revalidate_freq = 2 # Check file changes every X seconds (0 = always check)
|
||||
opcache.validate_timestamps = 1 # Check if files changed (0 = never check, production)
|
||||
opcache.fast_shutdown = 1 # Faster shutdown
|
||||
|
||||
; Advanced
|
||||
opcache.enable_file_override = 1 # Optimize file_exists(), is_file()
|
||||
opcache.optimization_level = 0x7FFFBFFF
|
||||
opcache.save_comments = 1 # Required for some frameworks (Doctrine, Symfony)
|
||||
opcache.load_comments = 1
|
||||
|
||||
; JIT (PHP 8.0+)
|
||||
opcache.jit = tracing # off | function | tracing
|
||||
opcache.jit_buffer_size = 100M # JIT compilation buffer
|
||||
```
|
||||
|
||||
**Why Track (HUGE PERFORMANCE!):**
|
||||
- Opcache disabled → 40-70% slower, 300% more CPU
|
||||
- `opcache.memory_consumption` too small → Cache thrashing
|
||||
- `opcache.max_accelerated_files` too low → Not all files cached
|
||||
- Hit rate < 90% → Increase memory or max files
|
||||
|
||||
**Detection:**
|
||||
```bash
|
||||
# Get opcache status (MOST IMPORTANT METRICS!)
|
||||
php -r "print_r(opcache_get_status());" | grep -E "opcache_enabled|memory_usage|opcache_statistics|num_cached_scripts|hits|misses|blacklist_misses"
|
||||
|
||||
# Calculate hit rate
|
||||
stats=$(php -r '$s=opcache_get_status(); echo $s["opcache_statistics"]["hits"].",".$s["opcache_statistics"]["misses"];')
|
||||
hits=$(echo $stats | cut -d',' -f1)
|
||||
misses=$(echo $stats | cut -d',' -f2)
|
||||
total=$((hits + misses))
|
||||
hit_rate=$((hits * 100 / total))
|
||||
echo "Opcache Hit Rate: ${hit_rate}%"
|
||||
|
||||
# If hit rate < 90% → Need more memory or max_files!
|
||||
```
|
||||
|
||||
### 5. **Session Settings**
|
||||
|
||||
```ini
|
||||
session.save_handler = files # files | memcached | redis
|
||||
session.save_path = "/var/lib/php/session"
|
||||
session.gc_maxlifetime = 1440 # Session timeout (seconds)
|
||||
session.gc_probability = 1
|
||||
session.gc_divisor = 1000 # GC runs 1/1000 requests
|
||||
session.cookie_lifetime = 0 # Session cookie expires on browser close
|
||||
```
|
||||
|
||||
**Why Track:**
|
||||
- `session.save_path` full disk → Session writes fail
|
||||
- Using `files` on high-traffic → I/O bottleneck (use Redis!)
|
||||
|
||||
### 6. **Error Handling & Logging**
|
||||
|
||||
```ini
|
||||
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
|
||||
display_errors = Off # CRITICAL: Must be Off in production!
|
||||
display_startup_errors = Off
|
||||
log_errors = On # Log to file
|
||||
error_log = /home/$user/logs/php_error.log
|
||||
ignore_repeated_errors = Off
|
||||
ignore_repeated_source = Off
|
||||
report_memleaks = On
|
||||
```
|
||||
|
||||
**Why Track:**
|
||||
- `display_errors = On` in production → Security risk (exposes paths)
|
||||
- No `error_log` set → Errors go to Apache log (harder to track)
|
||||
|
||||
### 7. **Security Settings**
|
||||
|
||||
```ini
|
||||
; Disable Dangerous Functions
|
||||
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
|
||||
|
||||
; Open Basedir (Restrict File Access)
|
||||
open_basedir = /home/$user:/tmp # Prevent directory traversal
|
||||
|
||||
; File Uploads
|
||||
file_uploads = On
|
||||
upload_tmp_dir = /tmp # Temp upload directory
|
||||
|
||||
; Misc Security
|
||||
expose_php = Off # Hide PHP version in headers
|
||||
allow_url_fopen = On # Allow remote file access (needed for many apps)
|
||||
allow_url_include = Off # CRITICAL: Prevent remote code execution
|
||||
```
|
||||
|
||||
### 8. **APCu Cache** (User Cache, separate from OPcache)
|
||||
|
||||
```ini
|
||||
[apcu]
|
||||
apc.enabled = 1
|
||||
apc.shm_size = 32M # Shared memory size
|
||||
apc.ttl = 7200 # Time to live
|
||||
apc.gc_ttl = 3600 # Garbage collection TTL
|
||||
apc.enable_cli = 0
|
||||
```
|
||||
|
||||
**Why Track:**
|
||||
- WordPress object cache, WooCommerce, etc. use APCu
|
||||
- Low hit rate → Increase shm_size
|
||||
|
||||
### 9. **MySQL/Database Settings** (php.ini side)
|
||||
|
||||
```ini
|
||||
mysqli.max_persistent = -1 # Max persistent connections (-1 = unlimited)
|
||||
mysqli.max_links = -1 # Max total connections
|
||||
mysqli.default_socket = /var/lib/mysql/mysql.sock
|
||||
pdo_mysql.default_socket = /var/lib/mysql/mysql.sock
|
||||
```
|
||||
|
||||
### 10. **Zend Extensions**
|
||||
|
||||
```ini
|
||||
zend_extension=opcache.so
|
||||
zend_extension=ioncube_loader_lin_8.2.so # If using IonCube
|
||||
```
|
||||
|
||||
## Complete Metrics Tracking List
|
||||
|
||||
### Per-Domain Tracking Matrix
|
||||
|
||||
```yaml
|
||||
domain: example.com
|
||||
user: examplec
|
||||
php_version: ea-php82
|
||||
|
||||
config_hierarchy:
|
||||
1_user_ini: /home/examplec/public_html/.user.ini
|
||||
2_pool_ini: /opt/cpanel/ea-php82/root/etc/php.ini
|
||||
3_pool_d: /opt/cpanel/ea-php82/root/etc/php.d/
|
||||
4_global: /etc/php.ini
|
||||
|
||||
effective_settings:
|
||||
# Memory
|
||||
memory_limit: 256M
|
||||
upload_max_filesize: 64M
|
||||
post_max_size: 128M
|
||||
max_input_vars: 1000
|
||||
realpath_cache_size: 4096K
|
||||
|
||||
# Execution
|
||||
max_execution_time: 30
|
||||
max_input_time: 60
|
||||
request_terminate_timeout: 30
|
||||
|
||||
# PHP-FPM Pool
|
||||
pm: dynamic
|
||||
pm.max_children: 50
|
||||
pm.start_servers: 5
|
||||
pm.min_spare_servers: 5
|
||||
pm.max_spare_servers: 35
|
||||
pm.max_requests: 500
|
||||
pm.process_idle_timeout: 10s
|
||||
|
||||
# OPcache
|
||||
opcache.enable: 1
|
||||
opcache.memory_consumption: 128M
|
||||
opcache.max_accelerated_files: 10000
|
||||
opcache.jit: tracing
|
||||
opcache.jit_buffer_size: 100M
|
||||
|
||||
# Sessions
|
||||
session.save_handler: redis
|
||||
session.save_path: "tcp://127.0.0.1:6379"
|
||||
|
||||
# Security
|
||||
display_errors: Off
|
||||
open_basedir: /home/examplec:/tmp
|
||||
disable_functions: exec,passthru,shell_exec
|
||||
|
||||
live_metrics:
|
||||
# Process Stats
|
||||
current_processes: 12
|
||||
avg_memory_per_process: 45MB
|
||||
total_memory_usage: 540MB
|
||||
cpu_usage: 15%
|
||||
|
||||
# OPcache Stats
|
||||
opcache_hit_rate: 95.3%
|
||||
opcache_memory_used: 87MB / 128MB
|
||||
opcache_cached_scripts: 2847 / 10000
|
||||
opcache_wasted_memory: 2.1MB
|
||||
|
||||
# Traffic Stats (last 24h)
|
||||
peak_concurrent_requests: 18
|
||||
avg_requests_per_minute: 45
|
||||
total_requests: 64,800
|
||||
|
||||
# Error Stats (last 7 days)
|
||||
memory_exhausted: 0
|
||||
max_execution_time: 3
|
||||
max_children_reached: 47 # CRITICAL!
|
||||
slow_requests: 12
|
||||
|
||||
issues_detected:
|
||||
- type: CRITICAL
|
||||
code: MAX_CHILDREN_REACHED
|
||||
count: 47
|
||||
message: "pm.max_children limit hit 47 times in 7 days"
|
||||
recommendation: "Increase from 50 to 75"
|
||||
|
||||
- type: WARNING
|
||||
code: SLOW_REQUESTS
|
||||
count: 12
|
||||
message: "12 requests took > 5 seconds"
|
||||
recommendation: "Review slow log, optimize code"
|
||||
|
||||
recommendations:
|
||||
- priority: HIGH
|
||||
setting: pm.max_children
|
||||
current: 50
|
||||
recommended: 75
|
||||
reason: "Peak concurrent (18) + buffer (50%) + safety margin"
|
||||
impact: "Handle 75 concurrent PHP requests vs 50"
|
||||
memory_impact: +1.1GB
|
||||
|
||||
- priority: MEDIUM
|
||||
setting: opcache.max_accelerated_files
|
||||
current: 10000
|
||||
recommended: 15000
|
||||
reason: "Currently caching 2847 files, room for growth"
|
||||
impact: "Better cache coverage as site grows"
|
||||
```
|
||||
|
||||
## Detection Commands Cheat Sheet
|
||||
|
||||
```bash
|
||||
# Find ALL php.ini files affecting a domain
|
||||
find /opt/cpanel/ea-php*/root/etc/ -name "php.ini"
|
||||
find /home/$user/public_html -name ".user.ini"
|
||||
|
||||
# Find FPM pool config
|
||||
grep -r "pool.*$domain" /opt/cpanel/ea-php*/root/etc/php-fpm.d/
|
||||
|
||||
# Get effective settings for domain
|
||||
su -s /bin/bash $user -c "php -r 'phpinfo();'" | grep -A1 "memory_limit"
|
||||
|
||||
# Check opcache status
|
||||
php -r "var_dump(opcache_get_status());"
|
||||
|
||||
# Find max_children errors
|
||||
grep -r "max_children" /opt/cpanel/ea-php*/root/usr/var/log/php-fpm/
|
||||
|
||||
# Find slow requests
|
||||
find /opt/cpanel/ea-php*/root/usr/var/log/php-fpm/ -name "*slow.log" -exec tail -50 {} \;
|
||||
|
||||
# Count current FPM processes
|
||||
ps aux | grep "php-fpm: pool $domain" | wc -l
|
||||
|
||||
# Memory per process
|
||||
ps aux | grep "php-fpm: pool $domain" | awk '{sum+=$6} END {print sum/NR " KB avg per process"}'
|
||||
```
|
||||
|
||||
This comprehensive tracking will allow us to build an intelligent optimizer that knows EXACTLY what to fix!
|
||||
Reference in New Issue
Block a user