Add intelligent root cause correlation to error analyzer
- Automatically detects error root causes: • .htaccess configuration issues • ModSecurity WAF blocks (with rule IDs) • PHP memory exhaustion (shows current limit) • PHP timeout/upload limits • File permission issues • Missing PHP extensions (GD, cURL, mysqli, etc.) • Database issues (max connections, auth failures, timeouts) • Apache configuration errors (502/503/504) • PHP syntax/parse errors • Missing files - Enhanced error display with: • Root cause identification for each error • Color-coded severity indicators • Actionable fix instructions per error type • Root cause breakdown summary with counts - Intelligent recommendations based on detected causes: • Specific commands to run for diagnosis • Configuration file locations to check • Recommended PHP module installations • Memory/timeout limit suggestions Makes troubleshooting much faster by immediately identifying whether issues are from .htaccess, ModSecurity, PHP config, permissions, or missing dependencies. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -310,7 +310,102 @@ extract_useful_info() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$domain|$file_path|$error_msg"
|
# Correlate to root cause
|
||||||
|
root_cause=$(correlate_root_cause "$line" "$error_msg" "$domain")
|
||||||
|
|
||||||
|
echo "$domain|$file_path|$error_msg|$root_cause"
|
||||||
|
}
|
||||||
|
|
||||||
|
correlate_root_cause() {
|
||||||
|
local line="$1"
|
||||||
|
local error_msg="$2"
|
||||||
|
local domain="$3"
|
||||||
|
local cause="UNKNOWN"
|
||||||
|
|
||||||
|
# .htaccess issues
|
||||||
|
if echo "$line" | grep -qiE "\.htaccess|Invalid command|RewriteRule|RewriteCond"; then
|
||||||
|
cause="HTACCESS"
|
||||||
|
|
||||||
|
# ModSecurity blocks
|
||||||
|
elif echo "$line" | grep -qiE "ModSecurity|mod_security|WAF"; then
|
||||||
|
cause="MODSECURITY"
|
||||||
|
|
||||||
|
# PHP memory limit
|
||||||
|
elif echo "$error_msg" | grep -qiE "memory.*exhausted|Allowed memory size"; then
|
||||||
|
# Get current memory limit for this domain/user
|
||||||
|
if [ -n "$domain" ] && [ "$domain" != "unknown" ]; then
|
||||||
|
user=$(grep -l "DNS.*$domain" /var/cpanel/users/* 2>/dev/null | head -1 | xargs basename 2>/dev/null)
|
||||||
|
if [ -n "$user" ]; then
|
||||||
|
mem_limit=$(grep -h "memory_limit" /home/$user/public_html/.user.ini /home/$user/.php.ini 2>/dev/null | tail -1 | cut -d'=' -f2 | tr -d ' ')
|
||||||
|
[ -n "$mem_limit" ] && cause="PHP_MEMORY:$mem_limit" || cause="PHP_MEMORY:default"
|
||||||
|
else
|
||||||
|
cause="PHP_MEMORY"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cause="PHP_MEMORY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PHP max execution time
|
||||||
|
elif echo "$error_msg" | grep -qiE "max_execution_time|Maximum execution time"; then
|
||||||
|
cause="PHP_TIMEOUT"
|
||||||
|
|
||||||
|
# PHP upload/post size limits
|
||||||
|
elif echo "$error_msg" | grep -qiE "upload_max_filesize|post_max_size"; then
|
||||||
|
cause="PHP_UPLOAD_LIMIT"
|
||||||
|
|
||||||
|
# File permissions
|
||||||
|
elif echo "$error_msg" | grep -qiE "Permission denied|Failed to open stream.*permission"; then
|
||||||
|
cause="FILE_PERMISSIONS"
|
||||||
|
|
||||||
|
# Missing PHP modules/extensions
|
||||||
|
elif echo "$error_msg" | grep -qiE "undefined function|Call to undefined function|Class .* not found"; then
|
||||||
|
# Try to identify which module
|
||||||
|
if echo "$error_msg" | grep -qiE "imagecreate|imagefilter|gd_"; then
|
||||||
|
cause="MISSING_PHP_GD"
|
||||||
|
elif echo "$error_msg" | grep -qiE "curl_|CURL"; then
|
||||||
|
cause="MISSING_PHP_CURL"
|
||||||
|
elif echo "$error_msg" | grep -qiE "json_|JSON"; then
|
||||||
|
cause="MISSING_PHP_JSON"
|
||||||
|
elif echo "$error_msg" | grep -qiE "mysqli|mysql_connect"; then
|
||||||
|
cause="MISSING_PHP_MYSQLI"
|
||||||
|
else
|
||||||
|
cause="MISSING_PHP_MODULE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Database issues
|
||||||
|
elif echo "$error_msg" | grep -qiE "database|MySQL|mysqli"; then
|
||||||
|
if echo "$error_msg" | grep -qiE "Too many connections|max_connections"; then
|
||||||
|
cause="DB_MAX_CONNECTIONS"
|
||||||
|
elif echo "$error_msg" | grep -qiE "Access denied|authentication"; then
|
||||||
|
cause="DB_AUTH_FAILED"
|
||||||
|
elif echo "$error_msg" | grep -qiE "gone away|Lost connection"; then
|
||||||
|
cause="DB_TIMEOUT"
|
||||||
|
else
|
||||||
|
cause="DB_ERROR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apache configuration
|
||||||
|
elif echo "$error_msg" | grep -qiE "Invalid URI|Request exceeded the limit|LimitRequestLine"; then
|
||||||
|
cause="APACHE_CONFIG"
|
||||||
|
|
||||||
|
# PHP parse/syntax errors (code issues)
|
||||||
|
elif echo "$error_msg" | grep -qiE "Parse error|syntax error|unexpected"; then
|
||||||
|
cause="PHP_SYNTAX_ERROR"
|
||||||
|
|
||||||
|
# File not found (may indicate bad symlinks or missing files)
|
||||||
|
elif echo "$error_msg" | grep -qiE "No such file|failed to open stream"; then
|
||||||
|
cause="MISSING_FILE"
|
||||||
|
|
||||||
|
# Generic PHP fatal error
|
||||||
|
elif echo "$error_msg" | grep -qiE "Fatal error"; then
|
||||||
|
cause="PHP_FATAL_ERROR"
|
||||||
|
|
||||||
|
# 500 errors without specific cause
|
||||||
|
elif echo "$error_msg" | grep -qiE "500|Internal Server"; then
|
||||||
|
cause="SERVER_ERROR_500"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$cause"
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -356,7 +451,12 @@ while IFS='|' read -r log_path log_type; do
|
|||||||
uri=$(echo "$line" | grep -oE "uri: [^ ]+" | sed 's/uri: //' || echo "")
|
uri=$(echo "$line" | grep -oE "uri: [^ ]+" | sed 's/uri: //' || echo "")
|
||||||
domain=$(echo "$line" | grep -oE "hostname: [^ ]+" | sed 's/hostname: //' || echo "unknown")
|
domain=$(echo "$line" | grep -oE "hostname: [^ ]+" | sed 's/hostname: //' || echo "unknown")
|
||||||
|
|
||||||
echo "$domain||ModSecurity blocked: $attack_type $uri" >> "$CRITICAL_ERRORS"
|
# Extract rule ID if available
|
||||||
|
rule_id=$(echo "$line" | grep -oE "id \"[0-9]+\"" | grep -oE "[0-9]+" || echo "")
|
||||||
|
root_cause="MODSECURITY"
|
||||||
|
[ -n "$rule_id" ] && root_cause="MODSECURITY:rule_$rule_id"
|
||||||
|
|
||||||
|
echo "$domain||ModSecurity blocked: $attack_type $uri|$root_cause" >> "$CRITICAL_ERRORS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done < <(tail -n 5000 "$log_path" 2>/dev/null)
|
done < <(tail -n 5000 "$log_path" 2>/dev/null)
|
||||||
@@ -384,8 +484,17 @@ while IFS='|' read -r log_path log_type; do
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Determine root cause from status code
|
||||||
|
case $status in
|
||||||
|
500) root_cause="SERVER_ERROR_500" ;;
|
||||||
|
502) root_cause="APACHE_CONFIG:bad_gateway" ;;
|
||||||
|
503) root_cause="APACHE_CONFIG:service_unavailable" ;;
|
||||||
|
504) root_cause="APACHE_CONFIG:gateway_timeout" ;;
|
||||||
|
*) root_cause="SERVER_ERROR_5XX" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
((critical_found++))
|
((critical_found++))
|
||||||
echo "$domain||HTTP $status - $url (from $ip)" >> "$CRITICAL_ERRORS"
|
echo "$domain||HTTP $status - $url (from $ip)|$root_cause" >> "$CRITICAL_ERRORS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done < <(tail -n 5000 "$log_path" 2>/dev/null)
|
done < <(tail -n 5000 "$log_path" 2>/dev/null)
|
||||||
@@ -462,8 +571,8 @@ echo ""
|
|||||||
|
|
||||||
# Group identical errors and count them
|
# Group identical errors and count them
|
||||||
awk -F'|' '{
|
awk -F'|' '{
|
||||||
key = $1 "|" $3 # domain|error_msg (skip file_path for grouping)
|
key = $1 "|" $3 "|" $4 # domain|error_msg|root_cause
|
||||||
file[$1"|"$3] = $2 # Store file path
|
file[$1"|"$3"|"$4] = $2 # Store file path
|
||||||
count[key]++
|
count[key]++
|
||||||
}
|
}
|
||||||
END {
|
END {
|
||||||
@@ -471,14 +580,15 @@ END {
|
|||||||
split(key, parts, "|")
|
split(key, parts, "|")
|
||||||
domain = parts[1]
|
domain = parts[1]
|
||||||
error = parts[2]
|
error = parts[2]
|
||||||
|
root_cause = parts[3]
|
||||||
file_path = file[key]
|
file_path = file[key]
|
||||||
|
|
||||||
# Skip empty errors
|
# Skip empty errors
|
||||||
if (length(error) == 0) next
|
if (length(error) == 0) next
|
||||||
|
|
||||||
print count[key] "|" domain "|" file_path "|" error
|
print count[key] "|" domain "|" file_path "|" error "|" root_cause
|
||||||
}
|
}
|
||||||
}' "$CRITICAL_ERRORS" | sort -t'|' -k1 -rn | head -20 | while IFS='|' read -r count domain file_path error_msg; do
|
}' "$CRITICAL_ERRORS" | sort -t'|' -k1 -rn | head -20 | while IFS='|' read -r count domain file_path error_msg root_cause; do
|
||||||
|
|
||||||
# Color code by frequency
|
# Color code by frequency
|
||||||
if [ "$count" -ge 10 ]; then
|
if [ "$count" -ge 10 ]; then
|
||||||
@@ -496,14 +606,150 @@ END {
|
|||||||
[ -n "$domain" ] && [ "$domain" != "unknown" ] && echo " Domain: $domain"
|
[ -n "$domain" ] && [ "$domain" != "unknown" ] && echo " Domain: $domain"
|
||||||
[ -n "$file_path" ] && echo " File: $file_path"
|
[ -n "$file_path" ] && echo " File: $file_path"
|
||||||
echo " Error: $error_msg"
|
echo " Error: $error_msg"
|
||||||
|
|
||||||
|
# Display root cause with color coding and actionable fix
|
||||||
|
if [ -n "$root_cause" ] && [ "$root_cause" != "UNKNOWN" ]; then
|
||||||
|
echo -ne " ${CYAN}Root Cause: ${BOLD}"
|
||||||
|
|
||||||
|
case "$root_cause" in
|
||||||
|
HTACCESS)
|
||||||
|
echo -e "${YELLOW}⚙️ .htaccess Configuration${NC}"
|
||||||
|
echo " → Check .htaccess syntax in domain root"
|
||||||
|
echo " → Look for invalid RewriteRule or directives"
|
||||||
|
;;
|
||||||
|
MODSECURITY*)
|
||||||
|
rule=$(echo "$root_cause" | cut -d':' -f2)
|
||||||
|
echo -e "${YELLOW}🛡️ ModSecurity WAF Block${NC}"
|
||||||
|
[ -n "$rule" ] && echo " → Rule ID: $rule"
|
||||||
|
echo " → Check if this is a false positive"
|
||||||
|
echo " → Review: /usr/local/apache/logs/modsec_audit.log"
|
||||||
|
;;
|
||||||
|
PHP_MEMORY*)
|
||||||
|
limit=$(echo "$root_cause" | cut -d':' -f2)
|
||||||
|
echo -e "${RED}💾 PHP Memory Exhausted${NC}"
|
||||||
|
[ -n "$limit" ] && echo " → Current limit: $limit"
|
||||||
|
echo " → Increase memory_limit in .user.ini or php.ini"
|
||||||
|
echo " → Recommended: 256M minimum, 512M for WooCommerce"
|
||||||
|
;;
|
||||||
|
PHP_TIMEOUT)
|
||||||
|
echo -e "${YELLOW}⏱️ PHP Execution Timeout${NC}"
|
||||||
|
echo " → Increase max_execution_time in php.ini"
|
||||||
|
echo " → Recommended: 300 for imports/backups"
|
||||||
|
;;
|
||||||
|
PHP_UPLOAD_LIMIT)
|
||||||
|
echo -e "${YELLOW}📤 PHP Upload Size Limit${NC}"
|
||||||
|
echo " → Increase upload_max_filesize and post_max_size"
|
||||||
|
echo " → Match both values (e.g., 64M)"
|
||||||
|
;;
|
||||||
|
FILE_PERMISSIONS)
|
||||||
|
echo -e "${RED}🔒 File Permission Denied${NC}"
|
||||||
|
echo " → Check file ownership and permissions"
|
||||||
|
echo " → Files should be 644, directories 755"
|
||||||
|
echo " → Owner should match cPanel user"
|
||||||
|
;;
|
||||||
|
MISSING_PHP_GD)
|
||||||
|
echo -e "${RED}📦 Missing PHP GD Extension${NC}"
|
||||||
|
echo " → Install: yum install ea-phpXX-php-gd"
|
||||||
|
echo " → Required for image processing"
|
||||||
|
;;
|
||||||
|
MISSING_PHP_CURL)
|
||||||
|
echo -e "${RED}📦 Missing PHP cURL Extension${NC}"
|
||||||
|
echo " → Install: yum install ea-phpXX-php-curl"
|
||||||
|
;;
|
||||||
|
MISSING_PHP_*)
|
||||||
|
module=$(echo "$root_cause" | sed 's/MISSING_PHP_//' | tr '[:upper:]' '[:lower:]')
|
||||||
|
echo -e "${RED}📦 Missing PHP Extension: $module${NC}"
|
||||||
|
echo " → Install: yum install ea-phpXX-php-$module"
|
||||||
|
;;
|
||||||
|
DB_MAX_CONNECTIONS)
|
||||||
|
echo -e "${RED}🗄️ Database Max Connections Reached${NC}"
|
||||||
|
echo " → Check: mysql -e 'SHOW VARIABLES LIKE \"max_connections\"'"
|
||||||
|
echo " → Increase max_connections in my.cnf"
|
||||||
|
echo " → Or optimize slow queries reducing connection time"
|
||||||
|
;;
|
||||||
|
DB_AUTH_FAILED)
|
||||||
|
echo -e "${RED}🗄️ Database Authentication Failed${NC}"
|
||||||
|
echo " → Verify credentials in wp-config.php / config files"
|
||||||
|
echo " → Check database user permissions"
|
||||||
|
;;
|
||||||
|
DB_TIMEOUT)
|
||||||
|
echo -e "${YELLOW}🗄️ Database Connection Timeout${NC}"
|
||||||
|
echo " → MySQL server may be overloaded"
|
||||||
|
echo " → Check slow query log"
|
||||||
|
;;
|
||||||
|
DB_ERROR)
|
||||||
|
echo -e "${RED}🗄️ Database Error${NC}"
|
||||||
|
echo " → Check MySQL error log for details"
|
||||||
|
;;
|
||||||
|
APACHE_CONFIG*)
|
||||||
|
issue=$(echo "$root_cause" | cut -d':' -f2)
|
||||||
|
echo -e "${YELLOW}⚙️ Apache Configuration${NC}"
|
||||||
|
[ -n "$issue" ] && echo " → Issue: $issue"
|
||||||
|
echo " → Check Apache config and vhost settings"
|
||||||
|
;;
|
||||||
|
PHP_SYNTAX_ERROR)
|
||||||
|
echo -e "${RED}⚠️ PHP Syntax Error in Code${NC}"
|
||||||
|
echo " → Review recent code changes"
|
||||||
|
echo " → Check for missing semicolons, brackets, quotes"
|
||||||
|
;;
|
||||||
|
MISSING_FILE)
|
||||||
|
echo -e "${YELLOW}📄 File Not Found${NC}"
|
||||||
|
echo " → File may have been deleted or moved"
|
||||||
|
echo " → Check for broken symlinks"
|
||||||
|
;;
|
||||||
|
PHP_FATAL_ERROR)
|
||||||
|
echo -e "${RED}⚠️ PHP Fatal Error${NC}"
|
||||||
|
echo " → Review PHP error logs for details"
|
||||||
|
echo " → May be compatibility issue or code bug"
|
||||||
|
;;
|
||||||
|
SERVER_ERROR_500)
|
||||||
|
echo -e "${RED}🔥 Generic 500 Internal Server Error${NC}"
|
||||||
|
echo " → Check PHP/Apache error logs for specifics"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${NC}$root_cause"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Root Cause Summary
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " 📊 ROOT CAUSE BREAKDOWN"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Count errors by root cause
|
||||||
|
cut -d'|' -f4 "$CRITICAL_ERRORS" | grep -v "^$" | sort | uniq -c | sort -rn | while read -r count cause; do
|
||||||
|
# Clean up cause display
|
||||||
|
clean_cause=$(echo "$cause" | sed 's/:.*//; s/_/ /g')
|
||||||
|
|
||||||
|
# Color code by severity
|
||||||
|
case "$cause" in
|
||||||
|
PHP_MEMORY*|DB_MAX_CONNECTIONS|PHP_FATAL_ERROR|SERVER_ERROR_500)
|
||||||
|
color="${RED}"; icon="🔥" ;;
|
||||||
|
HTACCESS|MODSECURITY*|PHP_TIMEOUT|DB_*)
|
||||||
|
color="${YELLOW}"; icon="⚠️ " ;;
|
||||||
|
MISSING_PHP*|FILE_PERMISSIONS)
|
||||||
|
color="${YELLOW}"; icon="📦" ;;
|
||||||
|
*)
|
||||||
|
color="${INFO_COLOR}"; icon="•" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf " ${color}${icon} %-35s %4d errors${NC}\n" "$clean_cause" "$count"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Intelligent Recommendations
|
# Intelligent Recommendations
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
echo " 🔧 RECOMMENDED ACTIONS"
|
echo " 🔧 RECOMMENDED ACTIONS"
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|||||||
Reference in New Issue
Block a user