Files
foxhunt/deployment/health_check.sh
jgrusewski 8cf9437c78 🔧 Partial fixes: S3 integration, SIMD improvements, field access corrections
- Restored S3 storage functionality with AWS SDK
- Fixed field access issues (removed underscore prefixes)
- Created Benzinga historical module
- Initial SIMD optimization (needs consolidation)
- Fixed multiple compilation errors

PENDING: SIMD consolidation, config centralization, shared libraries
2025-09-25 01:05:32 +02:00

769 lines
21 KiB
Bash
Executable File

#!/bin/bash
#============================================================================
# FOXHUNT HFT TRADING SYSTEM - COMPREHENSIVE HEALTH CHECK SCRIPT
#============================================================================
# Performs detailed health checks on all system components
# Supports continuous monitoring and alerting integration
#
# Usage:
# ./health_check.sh [OPTIONS]
#
# Options:
# --mode MODE Check mode: quick, comprehensive, continuous (default: quick)
# --format FORMAT Output format: text, json, prometheus (default: text)
# --timeout SECONDS Health check timeout per service (default: 10)
# --alert-webhook URL Webhook URL for alerts
# --nagios Output in Nagios-compatible format
# --verbose Enable verbose output
# --help Show this help message
#============================================================================
set -euo pipefail
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CHECK_MODE="${CHECK_MODE:-quick}"
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
TIMEOUT="${TIMEOUT:-10}"
ALERT_WEBHOOK="${ALERT_WEBHOOK:-}"
NAGIOS_MODE="${NAGIOS_MODE:-false}"
VERBOSE="${VERBOSE:-false}"
# Service endpoints
declare -A SERVICES=(
["trading"]="8080:50051:Trading Service"
["ml-training"]="8082:50052:ML Training Service"
["backtesting"]="8083:50053:Backtesting Service"
["tli"]="8081:50054:Terminal Line Interface"
)
# Database endpoints
declare -A DATABASES=(
["postgresql"]="5432:PostgreSQL Database"
["redis"]="6379:Redis Cache"
["influxdb"]="8086:InfluxDB Time Series"
)
# External services
declare -A EXTERNAL=(
["vault"]="8200:HashiCorp Vault"
["prometheus"]="9090:Prometheus Metrics"
["grafana"]="3000:Grafana Dashboard"
)
# Health check results
declare -A HEALTH_RESULTS=()
declare -A PERFORMANCE_METRICS=()
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
if [[ "$VERBOSE" == "true" ]] || [[ "$OUTPUT_FORMAT" == "text" ]]; then
echo -e "${BLUE}[INFO]${NC} $1"
fi
}
log_success() {
if [[ "$OUTPUT_FORMAT" == "text" ]]; then
echo -e "${GREEN}[OK]${NC} $1"
fi
}
log_warning() {
if [[ "$OUTPUT_FORMAT" == "text" ]]; then
echo -e "${YELLOW}[WARNING]${NC} $1"
fi
}
log_error() {
if [[ "$OUTPUT_FORMAT" == "text" ]]; then
echo -e "${RED}[ERROR]${NC} $1"
fi
}
show_help() {
cat << EOF
Foxhunt HFT Trading System - Health Check Script
Usage: $0 [OPTIONS]
OPTIONS:
--mode MODE Check mode (quick, comprehensive, continuous)
--format FORMAT Output format (text, json, prometheus)
--timeout SECONDS Health check timeout per service (default: 10)
--alert-webhook URL Webhook URL for critical alerts
--nagios Output in Nagios-compatible format
--verbose Enable verbose output
--help Show this help message
CHECK MODES:
quick Basic health checks (HTTP endpoints, process status)
comprehensive Deep health checks (performance, dependencies, security)
continuous Continuous monitoring with alerting
OUTPUT FORMATS:
text Human-readable console output
json JSON format for programmatic parsing
prometheus Prometheus metrics format
EXAMPLES:
$0 # Quick health check
$0 --mode comprehensive --verbose # Comprehensive check with details
$0 --format json > health.json # Export health status to JSON
$0 --mode continuous --timeout 5 # Continuous monitoring
EOF
}
# Parse arguments
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
--mode)
CHECK_MODE="$2"
shift 2
;;
--format)
OUTPUT_FORMAT="$2"
shift 2
;;
--timeout)
TIMEOUT="$2"
shift 2
;;
--alert-webhook)
ALERT_WEBHOOK="$2"
shift 2
;;
--nagios)
NAGIOS_MODE=true
OUTPUT_FORMAT="nagios"
shift
;;
--verbose)
VERBOSE=true
shift
;;
--help)
show_help
exit 0
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# HTTP health check
check_http_endpoint() {
local service="$1"
local port="$2"
local endpoint="${3:-/health}"
if command_exists curl; then
local response_time start_time end_time http_code
start_time=$(date +%s%N)
if http_code=$(curl -w "%{http_code}" -s -o /dev/null --max-time "$TIMEOUT" "http://localhost:$port$endpoint"); then
end_time=$(date +%s%N)
response_time=$(( (end_time - start_time) / 1000000 )) # Convert to milliseconds
PERFORMANCE_METRICS["${service}_response_time"]="$response_time"
if [[ "$http_code" == "200" ]]; then
HEALTH_RESULTS["${service}_http"]="OK"
return 0
else
HEALTH_RESULTS["${service}_http"]="FAIL:HTTP_$http_code"
return 1
fi
else
HEALTH_RESULTS["${service}_http"]="FAIL:TIMEOUT"
return 1
fi
else
HEALTH_RESULTS["${service}_http"]="FAIL:NO_CURL"
return 1
fi
}
# gRPC health check
check_grpc_endpoint() {
local service="$1"
local port="$2"
if command_exists grpcurl; then
if timeout "$TIMEOUT" grpcurl -plaintext "localhost:$port" grpc.health.v1.Health/Check >/dev/null 2>&1; then
HEALTH_RESULTS["${service}_grpc"]="OK"
return 0
else
HEALTH_RESULTS["${service}_grpc"]="FAIL:UNREACHABLE"
return 1
fi
else
# Fallback to TCP connection test
if timeout "$TIMEOUT" nc -z localhost "$port" >/dev/null 2>&1; then
HEALTH_RESULTS["${service}_grpc"]="OK"
return 0
else
HEALTH_RESULTS["${service}_grpc"]="FAIL:TCP_FAIL"
return 1
fi
fi
}
# Process health check
check_process_status() {
local service="$1"
local binary_name="$2"
if systemctl is-active "foxhunt-$service" >/dev/null 2>&1; then
HEALTH_RESULTS["${service}_process"]="OK"
# Get process metrics
local pid cpu_usage memory_usage
if pid=$(systemctl show "foxhunt-$service" --property=MainPID --value) && [[ "$pid" != "0" ]]; then
if command_exists ps; then
cpu_usage=$(ps -p "$pid" -o %cpu --no-headers | tr -d ' ' || echo "0.0")
memory_usage=$(ps -p "$pid" -o rss --no-headers | tr -d ' ' || echo "0")
PERFORMANCE_METRICS["${service}_cpu_percent"]="$cpu_usage"
PERFORMANCE_METRICS["${service}_memory_kb"]="$memory_usage"
fi
fi
return 0
else
HEALTH_RESULTS["${service}_process"]="FAIL:NOT_RUNNING"
return 1
fi
}
# Database connectivity check
check_database() {
local db_name="$1"
local port="$2"
case "$db_name" in
"postgresql")
if command_exists pg_isready; then
if pg_isready -h localhost -p "$port" >/dev/null 2>&1; then
HEALTH_RESULTS["${db_name}_connectivity"]="OK"
return 0
fi
fi
;;
"redis")
if command_exists redis-cli; then
if timeout "$TIMEOUT" redis-cli -p "$port" ping | grep -q "PONG"; then
HEALTH_RESULTS["${db_name}_connectivity"]="OK"
return 0
fi
fi
;;
"influxdb")
if check_http_endpoint "$db_name" "$port" "/health"; then
HEALTH_RESULTS["${db_name}_connectivity"]="OK"
return 0
fi
;;
esac
# Fallback to TCP connection test
if timeout "$TIMEOUT" nc -z localhost "$port" >/dev/null 2>&1; then
HEALTH_RESULTS["${db_name}_connectivity"]="OK"
return 0
else
HEALTH_RESULTS["${db_name}_connectivity"]="FAIL:UNREACHABLE"
return 1
fi
}
# System resource check
check_system_resources() {
log_info "Checking system resources..."
# CPU usage
if command_exists top; then
local cpu_usage
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 | cut -d'u' -f1)
PERFORMANCE_METRICS["system_cpu_percent"]="$cpu_usage"
fi
# Memory usage
if command_exists free; then
local mem_total mem_used mem_percent
mem_total=$(free -m | awk 'NR==2{print $2}')
mem_used=$(free -m | awk 'NR==2{print $3}')
if [[ "$mem_total" -gt 0 ]]; then
mem_percent=$((mem_used * 100 / mem_total))
PERFORMANCE_METRICS["system_memory_percent"]="$mem_percent"
fi
fi
# Disk usage
if command_exists df; then
local disk_usage
disk_usage=$(df / | tail -1 | awk '{print $5}' | cut -d'%' -f1)
PERFORMANCE_METRICS["system_disk_percent"]="$disk_usage"
fi
# Load average
if [[ -f /proc/loadavg ]]; then
local load_1min
load_1min=$(cat /proc/loadavg | awk '{print $1}')
PERFORMANCE_METRICS["system_load_1min"]="$load_1min"
fi
HEALTH_RESULTS["system_resources"]="OK"
}
# Network connectivity check
check_network_connectivity() {
log_info "Checking network connectivity..."
local dns_check=false
local internet_check=false
# DNS resolution check
if command_exists nslookup; then
if nslookup google.com >/dev/null 2>&1; then
dns_check=true
fi
fi
# Internet connectivity check
if command_exists ping; then
if ping -c 1 -W 5 8.8.8.8 >/dev/null 2>&1; then
internet_check=true
fi
fi
if [[ "$dns_check" == "true" ]] && [[ "$internet_check" == "true" ]]; then
HEALTH_RESULTS["network_connectivity"]="OK"
else
HEALTH_RESULTS["network_connectivity"]="FAIL:LIMITED"
fi
}
# Security check
check_security_status() {
log_info "Checking security status..."
local security_issues=()
# Check if services are running as non-root
for service in "${!SERVICES[@]}"; do
if systemctl is-active "foxhunt-$service" >/dev/null 2>&1; then
local service_user
service_user=$(systemctl show "foxhunt-$service" --property=User --value)
if [[ "$service_user" == "root" ]]; then
security_issues+=("$service running as root")
fi
fi
done
# Check file permissions
local data_dir="/opt/foxhunt"
if [[ -d "$data_dir" ]]; then
local perms
perms=$(stat -c "%a" "$data_dir" 2>/dev/null || echo "000")
if [[ "$perms" == "777" ]]; then
security_issues+=("Data directory has overly permissive permissions")
fi
fi
if [[ ${#security_issues[@]} -eq 0 ]]; then
HEALTH_RESULTS["security_status"]="OK"
else
HEALTH_RESULTS["security_status"]="WARN:${security_issues[*]}"
fi
}
# Performance benchmark
check_performance_benchmark() {
log_info "Running performance benchmark..."
# CPU benchmark (simple calculation)
local start_time end_time duration
start_time=$(date +%s%N)
# Simple CPU-intensive calculation
local result=0
for ((i=1; i<=100000; i++)); do
result=$((result + i))
done
end_time=$(date +%s%N)
duration=$(( (end_time - start_time) / 1000000 )) # Convert to milliseconds
PERFORMANCE_METRICS["cpu_benchmark_ms"]="$duration"
# Memory allocation test
if command_exists free; then
local mem_before mem_after
mem_before=$(free -m | awk 'NR==2{print $3}')
# Allocate some memory (create temporary file)
dd if=/dev/zero of=/tmp/health_check_mem_test bs=1M count=10 >/dev/null 2>&1
mem_after=$(free -m | awk 'NR==2{print $3}')
rm -f /tmp/health_check_mem_test
local mem_delta=$((mem_after - mem_before))
PERFORMANCE_METRICS["memory_allocation_mb"]="$mem_delta"
fi
HEALTH_RESULTS["performance_benchmark"]="OK"
}
# Quick health check
perform_quick_check() {
log_info "Performing quick health check..."
# Check core services
for service in "${!SERVICES[@]}"; do
IFS=':' read -r http_port grpc_port description <<< "${SERVICES[$service]}"
log_info "Checking $service ($description)..."
check_process_status "$service" "$service"
check_http_endpoint "$service" "$http_port"
check_grpc_endpoint "$service" "$grpc_port"
done
# Check databases
for db in "${!DATABASES[@]}"; do
IFS=':' read -r port description <<< "${DATABASES[$db]}"
log_info "Checking $db ($description)..."
check_database "$db" "$port"
done
}
# Comprehensive health check
perform_comprehensive_check() {
log_info "Performing comprehensive health check..."
perform_quick_check
check_system_resources
check_network_connectivity
check_security_status
if [[ "$CHECK_MODE" == "comprehensive" ]]; then
check_performance_benchmark
fi
# Check external services
for service in "${!EXTERNAL[@]}"; do
IFS=':' read -r port description <<< "${EXTERNAL[$service]}"
log_info "Checking $service ($description)..."
check_http_endpoint "$service" "$port"
done
}
# Send alert
send_alert() {
local message="$1"
local severity="$2"
if [[ -n "$ALERT_WEBHOOK" ]]; then
local payload
payload=$(cat << EOF
{
"text": "Foxhunt Health Alert",
"attachments": [
{
"color": "$( [[ "$severity" == "critical" ]] && echo "danger" || echo "warning" )",
"fields": [
{
"title": "Severity",
"value": "$severity",
"short": true
},
{
"title": "Message",
"value": "$message",
"short": false
},
{
"title": "Timestamp",
"value": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"short": true
}
]
}
]
}
EOF
)
if command_exists curl; then
curl -X POST -H "Content-Type: application/json" -d "$payload" "$ALERT_WEBHOOK" >/dev/null 2>&1 || true
fi
fi
}
# Output results in text format
output_text() {
local failed_checks=()
local warning_checks=()
local total_checks=0
local passed_checks=0
echo
echo "=================================="
echo "FOXHUNT HEALTH CHECK RESULTS"
echo "=================================="
echo "Timestamp: $(date)"
echo "Mode: $CHECK_MODE"
echo
# Service status
echo "SERVICE STATUS:"
for check_name in "${!HEALTH_RESULTS[@]}"; do
total_checks=$((total_checks + 1))
local result="${HEALTH_RESULTS[$check_name]}"
if [[ "$result" == "OK" ]]; then
log_success "$check_name: $result"
passed_checks=$((passed_checks + 1))
elif [[ "$result" == WARN:* ]]; then
log_warning "$check_name: $result"
warning_checks+=("$check_name")
else
log_error "$check_name: $result"
failed_checks+=("$check_name")
fi
done
# Performance metrics
if [[ ${#PERFORMANCE_METRICS[@]} -gt 0 ]]; then
echo
echo "PERFORMANCE METRICS:"
for metric in "${!PERFORMANCE_METRICS[@]}"; do
echo " $metric: ${PERFORMANCE_METRICS[$metric]}"
done
fi
# Summary
echo
echo "SUMMARY:"
echo " Total Checks: $total_checks"
echo " Passed: $passed_checks"
echo " Warnings: ${#warning_checks[@]}"
echo " Failed: ${#failed_checks[@]}"
if [[ ${#failed_checks[@]} -gt 0 ]]; then
echo
echo "FAILED CHECKS:"
for check in "${failed_checks[@]}"; do
echo "$check: ${HEALTH_RESULTS[$check]}"
done
send_alert "Health check failed: ${failed_checks[*]}" "critical"
return 2
elif [[ ${#warning_checks[@]} -gt 0 ]]; then
echo
echo "WARNING CHECKS:"
for check in "${warning_checks[@]}"; do
echo "$check: ${HEALTH_RESULTS[$check]}"
done
send_alert "Health check warnings: ${warning_checks[*]}" "warning"
return 1
else
log_success "All health checks passed!"
return 0
fi
}
# Output results in JSON format
output_json() {
local timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
echo "{"
echo " \"timestamp\": \"$timestamp\","
echo " \"mode\": \"$CHECK_MODE\","
echo " \"status\": {"
local first_result=true
for check_name in "${!HEALTH_RESULTS[@]}"; do
if [[ "$first_result" == "false" ]]; then
echo ","
fi
echo -n " \"$check_name\": \"${HEALTH_RESULTS[$check_name]}\""
first_result=false
done
echo
echo " },"
echo " \"metrics\": {"
local first_metric=true
for metric in "${!PERFORMANCE_METRICS[@]}"; do
if [[ "$first_metric" == "false" ]]; then
echo ","
fi
echo -n " \"$metric\": ${PERFORMANCE_METRICS[$metric]}"
first_metric=false
done
echo
echo " }"
echo "}"
}
# Output results in Prometheus format
output_prometheus() {
local timestamp=$(date +%s)
# Health check results (0 = OK, 1 = WARNING, 2 = CRITICAL)
for check_name in "${!HEALTH_RESULTS[@]}"; do
local result="${HEALTH_RESULTS[$check_name]}"
local value=0
if [[ "$result" == WARN:* ]]; then
value=1
elif [[ "$result" != "OK" ]]; then
value=2
fi
echo "foxhunt_health_status{check=\"$check_name\"} $value $timestamp"
done
# Performance metrics
for metric in "${!PERFORMANCE_METRICS[@]}"; do
echo "foxhunt_${metric} ${PERFORMANCE_METRICS[$metric]} $timestamp"
done
}
# Output results in Nagios format
output_nagios() {
local failed_checks=()
local warning_checks=()
for check_name in "${!HEALTH_RESULTS[@]}"; do
local result="${HEALTH_RESULTS[$check_name]}"
if [[ "$result" == WARN:* ]]; then
warning_checks+=("$check_name")
elif [[ "$result" != "OK" ]]; then
failed_checks+=("$check_name")
fi
done
if [[ ${#failed_checks[@]} -gt 0 ]]; then
echo "CRITICAL - Health check failed: ${failed_checks[*]}"
exit 2
elif [[ ${#warning_checks[@]} -gt 0 ]]; then
echo "WARNING - Health check warnings: ${warning_checks[*]}"
exit 1
else
echo "OK - All health checks passed"
exit 0
fi
}
# Continuous monitoring mode
run_continuous_monitoring() {
log_info "Starting continuous monitoring mode..."
local check_interval=60 # seconds
local consecutive_failures=0
local max_consecutive_failures=3
while true; do
log_info "Running health check cycle..."
# Clear previous results
HEALTH_RESULTS=()
PERFORMANCE_METRICS=()
perform_comprehensive_check
local exit_code=0
case "$OUTPUT_FORMAT" in
"json")
output_json > /tmp/foxhunt_health.json
;;
"prometheus")
output_prometheus > /tmp/foxhunt_health.prom
;;
*)
if ! output_text >/dev/null; then
exit_code=$?
fi
;;
esac
if [[ $exit_code -eq 0 ]]; then
consecutive_failures=0
else
consecutive_failures=$((consecutive_failures + 1))
if [[ $consecutive_failures -ge $max_consecutive_failures ]]; then
send_alert "Critical: $consecutive_failures consecutive health check failures" "critical"
fi
fi
log_info "Health check cycle complete. Waiting ${check_interval}s..."
sleep $check_interval
done
}
# Main execution
main() {
parse_args "$@"
case "$CHECK_MODE" in
"quick")
perform_quick_check
;;
"comprehensive")
perform_comprehensive_check
;;
"continuous")
run_continuous_monitoring
return 0
;;
*)
log_error "Unknown check mode: $CHECK_MODE"
exit 1
;;
esac
# Output results
case "$OUTPUT_FORMAT" in
"json")
output_json
;;
"prometheus")
output_prometheus
;;
"nagios")
output_nagios
;;
*)
output_text
;;
esac
}
# Handle interruption
trap 'log_error "Health check interrupted"; exit 130' INT TERM
# Run main function
main "$@"