diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..25d68a2 --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2025-12-14 - [Bash Script UX] +**Learning:** CLI tools in this repo are heavily macOS-dependent but lack compatibility checks, leading to confusing errors on other systems. +**Action:** When working on shell scripts, always add `command -v` checks or OS detection to provide clear feedback instead of raw errors. diff --git a/ops/monitoring/swap-ssd-health.sh b/ops/monitoring/swap-ssd-health.sh index b510e2e..480a4cc 100755 --- a/ops/monitoring/swap-ssd-health.sh +++ b/ops/monitoring/swap-ssd-health.sh @@ -11,57 +11,108 @@ # - BEFORE: System lag, high swap activity, potential data loss # - AFTER: Proactive monitoring, early warning system, optimal performance -LOG_FILE="$HOME/Library/Logs/swap-ssd-health.log" +# ANSI Color Codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +RESET='\033[0m' + +LOG_DIR="$HOME/Library/Logs" +LOG_FILE="$LOG_DIR/swap-ssd-health.log" DATE=$(date '+%Y-%m-%d %H:%M:%S') -echo "=== Swap & SSD Health Check - $DATE ===" | tee -a "$LOG_FILE" +# Ensure log directory exists +if [ ! -d "$LOG_DIR" ]; then + mkdir -p "$LOG_DIR" 2>/dev/null || { + # Fallback to current directory if can't create in Library + LOG_DIR="." + LOG_FILE="./swap-ssd-health.log" + } +fi + +# Helper function for logging +# Usage: log "Message" [COLOR] +log() { + local message="$1" + local color="${2:-$RESET}" + + # Print to console with color + echo -e "${color}${message}${RESET}" + + # Strip color codes for log file + echo "$message" | sed 's/\x1b\[[0-9;]*m//g' >> "$LOG_FILE" +} + +log "=== Swap & SSD Health Check - $DATE ===" "$BOLD$BLUE" # Check Swap Usage -echo "--- Swap Usage ---" | tee -a "$LOG_FILE" -SWAP_INFO=$(sysctl vm.swapusage 2>/dev/null) -echo "$SWAP_INFO" | tee -a "$LOG_FILE" - -# Extract swap usage values -TOTAL_SWAP=$(echo "$SWAP_INFO" | grep 'total' | awk '{print $4}' | sed 's/M//' | sed 's/\..*//') -USED_SWAP=$(echo "$SWAP_INFO" | grep 'used' | awk '{print $4}' | sed 's/M//' | sed 's/\..*//') - -# Check if swap usage is high (>50% of total) -if [ ! -z "$TOTAL_SWAP" ] && [ ! -z "$USED_SWAP" ]; then - SWAP_PERCENT=$((USED_SWAP * 100 / TOTAL_SWAP)) - if [ $SWAP_PERCENT -gt 50 ]; then - echo "⚠️ WARNING: High swap usage detected ($SWAP_PERCENT%)" | tee -a "$LOG_FILE" - echo " Consider closing unused apps or adding more RAM" | tee -a "$LOG_FILE" +log "--- Swap Usage ---" "$BOLD" + +if command -v sysctl &> /dev/null; then + SWAP_INFO=$(sysctl vm.swapusage 2>/dev/null) + log "$SWAP_INFO" + + # Extract swap usage values + TOTAL_SWAP=$(echo "$SWAP_INFO" | grep 'total' | awk '{print $4}' | sed 's/M//' | sed 's/\..*//') + USED_SWAP=$(echo "$SWAP_INFO" | grep 'used' | awk '{print $4}' | sed 's/M//' | sed 's/\..*//') + + # Check if swap usage is high (>50% of total) + if [ ! -z "$TOTAL_SWAP" ] && [ ! -z "$USED_SWAP" ] && [ "$TOTAL_SWAP" -gt 0 ]; then + SWAP_PERCENT=$((USED_SWAP * 100 / TOTAL_SWAP)) + if [ $SWAP_PERCENT -gt 50 ]; then + log "⚠️ WARNING: High swap usage detected ($SWAP_PERCENT%)" "$YELLOW" + log " Consider closing unused apps or adding more RAM" + else + log "✅ Swap usage is normal ($SWAP_PERCENT%)" "$GREEN" + fi else - echo "✅ Swap usage is normal ($SWAP_PERCENT%)" | tee -a "$LOG_FILE" + log "ℹ️ Swap usage info not parsable or swap is 0" fi +else + log "ℹ️ sysctl not available (not on macOS?)" "$YELLOW" fi # Check Pageouts (indicates active swapping) -echo "--- Pageouts (Active Swapping) ---" | tee -a "$LOG_FILE" -PAGEOUTS=$(vm_stat | grep 'pageouts' | awk '{print $2}' | sed 's/\.//') -if [ ! -z "$PAGEOUTS" ] && [ $PAGEOUTS -gt 0 ]; then - echo "⚠️ WARNING: Active swapping detected ($PAGEOUTS pageouts)" | tee -a "$LOG_FILE" - echo " System is using disk as virtual memory" | tee -a "$LOG_FILE" +log "--- Pageouts (Active Swapping) ---" "$BOLD" + +if command -v vm_stat &> /dev/null; then + PAGEOUTS=$(vm_stat | grep 'pageouts' | awk '{print $2}' | sed 's/\.//') + if [ ! -z "$PAGEOUTS" ] && [ $PAGEOUTS -gt 0 ]; then + log "⚠️ WARNING: Active swapping detected ($PAGEOUTS pageouts)" "$YELLOW" + log " System is using disk as virtual memory" + else + log "✅ No active swapping detected" "$GREEN" + fi else - echo "✅ No active swapping detected" | tee -a "$LOG_FILE" + log "ℹ️ vm_stat not available" "$YELLOW" fi # Check SSD SMART Status -echo "--- SSD Health ---" | tee -a "$LOG_FILE" +log "--- SSD Health ---" "$BOLD" # Basic SMART status -SMART_STATUS=$(diskutil info disk0 | grep 'SMART Status' | awk '{print $3}') -echo "SMART Status: $SMART_STATUS" | tee -a "$LOG_FILE" - -if [ "$SMART_STATUS" != "Verified" ]; then - echo "⚠️ WARNING: SSD SMART status is not verified" | tee -a "$LOG_FILE" +if command -v diskutil &> /dev/null; then + SMART_STATUS=$(diskutil info disk0 | grep 'SMART Status' | awk '{print $3}') + if [ ! -z "$SMART_STATUS" ]; then + log "SMART Status: $SMART_STATUS" + + if [ "$SMART_STATUS" != "Verified" ]; then + log "⚠️ WARNING: SSD SMART status is not verified" "$YELLOW" + else + log "✅ SSD SMART status is verified" "$GREEN" + fi + else + log "ℹ️ Could not retrieve SMART status via diskutil" "$YELLOW" + fi else - echo "✅ SSD SMART status is verified" | tee -a "$LOG_FILE" + log "ℹ️ diskutil not available" "$YELLOW" fi # Detailed SSD health (if smartctl is available) if command -v smartctl &> /dev/null; then - echo "--- Detailed SSD Health ---" | tee -a "$LOG_FILE" + log "--- Detailed SSD Health ---" "$BOLD" # Get SSD wear indicators SMART_OUTPUT=$(sudo smartctl -a disk0 2>/dev/null) @@ -69,35 +120,37 @@ if command -v smartctl &> /dev/null; then # Check for wear leveling count WEAR_COUNT=$(echo "$SMART_OUTPUT" | grep 'Wear_Leveling_Count' | awk '{print $10}') if [ ! -z "$WEAR_COUNT" ]; then - echo "Wear Leveling Count: $WEAR_COUNT" | tee -a "$LOG_FILE" + log "Wear Leveling Count: $WEAR_COUNT" if [ $WEAR_COUNT -lt 10 ]; then - echo "⚠️ WARNING: SSD wear leveling count is low" | tee -a "$LOG_FILE" + log "⚠️ WARNING: SSD wear leveling count is low" "$YELLOW" fi fi # Check for reallocated sectors REALLOCATED=$(echo "$SMART_OUTPUT" | grep 'Reallocated_Sector_Ct' | awk '{print $10}') if [ ! -z "$REALLOCATED" ] && [ $REALLOCATED -gt 0 ]; then - echo "⚠️ WARNING: Reallocated sectors detected ($REALLOCATED)" | tee -a "$LOG_FILE" + log "⚠️ WARNING: Reallocated sectors detected ($REALLOCATED)" "$YELLOW" fi # Check media wearout indicator MEDIA_WEAR=$(echo "$SMART_OUTPUT" | grep 'Media_Wearout_Indicator' | awk '{print $10}') if [ ! -z "$MEDIA_WEAR" ]; then - echo "Media Wearout Indicator: $MEDIA_WEAR" | tee -a "$LOG_FILE" + log "Media Wearout Indicator: $MEDIA_WEAR" if [ $MEDIA_WEAR -lt 10 ]; then - echo "⚠️ WARNING: SSD media wearout indicator is low" | tee -a "$LOG_FILE" + log "⚠️ WARNING: SSD media wearout indicator is low" "$YELLOW" fi fi else - echo "smartctl not available - install with: brew install smartmontools" | tee -a "$LOG_FILE" + log "ℹ️ smartctl not available - install with: brew install smartmontools" "$YELLOW" fi -echo "--- End of Health Check ---" | tee -a "$LOG_FILE" -echo "" | tee -a "$LOG_FILE" +log "--- End of Health Check ---" "$BOLD$BLUE" +log "" # Optional: Send notification if warnings were found -if grep -q "WARNING" "$LOG_FILE"; then - # Send macOS notification - osascript -e 'display notification "System health issues detected. Check logs for details." with title "Swap & SSD Health Check"' +if [ -f "$LOG_FILE" ] && grep -q "WARNING" "$LOG_FILE"; then + if command -v osascript &> /dev/null; then + # Send macOS notification + osascript -e 'display notification "System health issues detected. Check logs for details." with title "Swap & SSD Health Check"' + fi fi