Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
./castor-change-log-level.sh -d 192.168.8.84 -p admin:datacore -i 20 -t 10

Example 2: Set node(s) log level to debug (10) and keep it for 10 seconds

Code Block
./castor-change-log-level.sh -d 192.168.8.84,192.168.8.86 -p admin:datacore -i debug -t 10

Example 3: Run in background mode with verbose logging

Code Block
./castor-change-log-level.sh -d 192.168.8.84 -p admin:datacore -i 20 -t 30 --detach -v

Example

...

4: Running the Script at a Specific Time

The at command can used to schedule the scrip to run at a later time. This is useful when you need to start collecting debug logs at a specific hour.

Example 5: Schedule the script to run at 3:00 AM and collect debug logs for 1 hour

...

Code Block
languagebash
#!/bin/bash
# Written by Milton Suen (milton.suen@datacore.com) Oct 31, 2024
# Revision: Update to support running the script in a persistentdetachable session using screen or tmux.
# # Define colors
RED='\033[0;31m'
BOLD_Revision: 2025-02-20 Add support node(s) level log level change.
# Revision: 2025-02-26 SUPSCR-209 Auto detect CSN or SCS to adjust castor.log file path.

# Define colors
RED='\033[0;31m'
BOLD_RED='\033[1;31m'
GREEN='\033[0;32m'
BOLD_GREEN='\033[1;32m'
UNDERLINE_BOLD_GREEN='\033[4;32m'
YELLOW='\033[0;33m'
BOLD_YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD_BLUE='\033[1;34m'
MAGENTA='\033[0;35m'
BOLD_MAGENTA='\033[1;35m'
CYAN='\033[0;36m'
BOLD_CYAN='\033[1;36m'
RESET='\033[0m' # Reset color to default

# Function to display usage information
usage() {
    echo "Usage: $0./castor-change-log-level10.sh -d swarm_ip -p admin:password [-i new_log_level | -L new_node_log_level] [-t duration_in_seconds] [--persistent] [-v]D]"
    echo "  -d, --swarm_ip           IP address of the Swarm API endpoint. (or set SCSP_HOST environment variable)Supports single or multiple IPs separated by \",\", \";\" or \" \"."
    echo "    -p, --credentials        Credentials in the format admin:password"     echo "  -i, --log.level If multiple IPs are provided, the script will update Newthe log level to setfor all nodes."
    echo "  -t, --time               Duration in seconds to keep the new log level (optional)"Alternatively, set the SCSP_HOST environment echovariable "to the --persistent Swarm IP.)"
    echo "  -p, --credentials   Run the script in a detachedCredentials sessionin usingthe screen or tmuxformat admin:password"
    echo "  -vi, --verboselog.level          New cluster Enablelog verboselevel modeto forset debug messages"
    exit 1
}

# Default options
persistent=false
verbose=false
output_log="script_output.log"  # Log file for capturing persistent session output

# Function to display debug messages if verbose mode is enabled
debug() {
    if $verbose; then
   (5, 10, 15, 20, 30, 40, 50, chatter, debug, announce, info, error, critical, default)"
    echo "  -L, --node.log.level     New node log level to set (0, 5, 10, 15, 20, 30, 40, 50, chatter, debug, announce, info, error, critical, default)"
    echo "[DEBUG]  $1"     fi }  # Function to check if either 'screen' or 'tmux' is installed check_screen_or_tmux() {     if ! command**Either -i or -vL screen &>/dev/null && ! command -v tmux &>/dev/null; then
        echo "Error: Neither 'screen' nor 'tmux' is installed. Cannot run in persistent mode."
        persistent=false  # Disable persistent session
    fi
}

# Function to format file size
format_size() {
    local size=$1
    if (( size >= 1073741824 )); then
        echo "$(awk "BEGIN {printf \"%.1fGB\", $size/1073741824}")"
    elif (( size >= 1048576 )); then
        echo "$(awk "BEGIN {printf \"%.1fMB\", $size/1048576}")"
    elif (( size >= 1024 )); then
        echo "$(awk "BEGIN {printf \"%.1fKB\", $size/1024}")"
    else
        echo "${size}B"
    fi
}

# Function to format duration
format_duration() {
    local duration=$1
    local hours=$((duration / 3600))
    local minutes=$(( (duration % 3600) / 60 ))
    local seconds=$((duration % 60))
    printf "%02d:%02d:%02d" $hours $minutes $seconds
}

# Function to check if jq is available and set up JSON parsing method
check_jq() {
    if [[ -x "/usr/local/bin/jq" ]]; then
        echo "/usr/local/bin/jq"
    elif [[ -x "$(pwd)/jq" ]]; then
        echo "$(pwd)/jq"
    elif command -v jq &>/dev/null; then
        echo "jq"
    else
        echo "grep"
    fi
}

jq_or_grep=$(check_jq)

# Parse input arguments
while [[ "$#" -gt 0 ]]; do
    case $1 in
        -d|--swarm_ip) swarm_ip="$2"; shift 2 ;;
        -p|--credentials) credentials="$2"; shift 2 ;;
        -i|--log.level) new_log_level="$2"; shift 2 ;;
        -t|--time)
            if [[ -n "$2" && "$2" != -* ]]must be specified, but not both.**"
    echo "  -t, --time               (Optional) Duration in seconds to keep the new log level (must be greater than 0)"
    echo "  -D, --detach             (Optional) Detach the script from the current terminal and run in a detachable session using screen or tmux"
    exit 1
}

# Default options
detachable=false
debug=false
output_log="script_output.log"						# Log file for capturing detachable session output
log_level_type="cluster"									# Default log level type
default_log_level=30											# Default log level
log_file="/var/log/datacore/castor.log"		# Default log file location

# Global associative array for log levels
declare -A log_levels=(
    [5]="chatter"
    [10]="debug"
    [15]="audit"
    [20]="info"
    [30]="warning"
    [40]="error"
    [45]="defect"
    [50]="critical"
    [60]="announce"
    [30]="default"
)

# Global associative array for log level names
declare -A log_level_names=(
    ["chatter"]=5
    ["debug"]=10
    ["audit"]=15
    ["info"]=20
    ["warning"]=30
    ["error"]=40
    ["defect"]=45
    ["critical"]=50
    ["announce"]=60
    ["default"]=30
)

# Function to display debug messages if debug mode is enabled
debug_msg() {
    if $debug; then
        echo -e "[DEBUG] $1" >> "$output_log"
    fi
}

# Function to check if either 'screen' or 'tmux' is installed
check_screen_or_tmux() {
    if ! command -v screen &>/dev/null && ! command -v tmux &>/dev/null; then
        echo "Error: Neither 'screen' nor 'tmux' is installed. Cannot run in detachable mode."
        detachable=false  # Disable detachable session
        debug_msg "detachable=${YELLOW}false${RESET}"
    fi
}

# Function to format file size
format_size() {
    local size=$1
    if (( size >= 1073741824 )); then
        echo "$(awk "BEGIN {printf     duration="$2\"%.1fGB\", $size/1073741824}")"
    elif (( size >= 1048576 )); then
     shift 2  echo "$(awk "BEGIN {printf \"%.1fMB\", $size/1048576}")"
    elif else(( size >= 1024 )); then
        echo "$(awk "BEGIN read -p "Enter duration in seconds: " duration{printf \"%.1fKB\", $size/1024}")"
    else
        echo "${size}B"
  shift  fi
}

# Function to format duration
 format_duration() {
fi    local duration=$1
    local hours=$((duration / ;;3600))
    local minutes=$(( (duration % --persistent3600) persistent=true; shift ;;/ 60 ))
    local seconds=$((duration   -v|--verbose) verbose=true; shift ;;
        *) usage ;;
    esac
done

# Check if 'screen' or 'tmux' is installed
check_screen_or_tmux

# If swarm_ip is not provided, try using SCSP_HOST environment variable
% 60))
    printf "%02d:%02d:%02d" $hours $minutes $seconds
}

# Function to check if jq is available and set up JSON parsing method
check_jq() {
    if [[ -zx "$swarm_ip/usr/local/bin/jq" ]]; then
        ifecho "/usr/local/bin/jq"
    elif [[ -nx "$SCSP_HOST$(pwd)/jq" ]]; then
        swarm_ip="$SCSP_HOST" echo "$(pwd)/jq"
    elif command -v jq  debug "Using Swarm IP from SCSP_HOST: $swarm_ip&>/dev/null; then
        echo "jq"
    else
        echo "grep"Error:
swarm_ip not provided and SCSP_HOSTfi
is}
not set."
jq_or_grep=$(check_jq)

# Function to determine the log file usage
 path
determine_log_file() {
  fi fi if # Check if required arguments are provided
if [[ -z "$credentials" || -z "$new_log_level[[ -f "/var/log/datacore/castor.log" ]]; then
        echo "/var/log/datacore/castor.log"
    elif [[ -f "/var/log/caringo/castor.log" ]]; then
    usage  fi  # Retrieve cluster name and handle JSON parsing
debug "Retrieving the cluster name from Swarm API."
if [[ "$jq_or_grep" == "grep" ]]; then
    clusterName=$(curl --user "$credentials" -sS "http://$swarm_ip:91/api/storage/clusters" | grep -oP '"name":\s*"\K[^"]+')
else
    clusterName=$(curl --user "$credentials" -sS "http://$swarm_ip:91/api/storage/clusters" | "$jq_or_grep" -r '._embedded.clusters[0].name')
fi

if [[ -z "$clusterName" ]]; then
    echo "Failed to retrieve the cluster name. Please check your inputs."
    exit 1
fi
debug "Cluster Name: $clusterName"

# Main logic function to run the script tasks
main_script() {
    local swarm_ip="$1"
    local credentials="$2"
    local new_log_level="$3"
    local duration="$4"
    local clusterName="$5"
    local log_file="/var/log/datacore/castor.log"
    local initial_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)
    local current_log_level
    local jq_or_grep="$6"

    # Display initial information
    echo -e "Swarm IP: ${GREEN}$swarm_ip${RESET}"
    echo -e "Credentials: ${GREEN}[hidden for security]${RESET}"
    echo -e "Cluster Name: ${GREEN}$clusterName${RESET}"

    debug "Starting main_script function..."

    # Retrieve current log level
    if [[ "$jq_or_grep" == "grep" ]]; then
        current_log_level=$(curl --user "$credentials" -sS "http://$swarm_ip:91/api/storage/clusters/$clusterName/settings/log.level" | grep -oP '"value":\s*\K[0-9]+')
    elseecho "/var/log/caringo/castor.log"
    else
        echo "Error: Log file not found in /var/log/datacore/castor.log or /var/log/caringo/castor.log"
        exit 1
    fi
}

log_file=$(determine_log_file)

# Parse input arguments
while [[ "$#" -gt 0 ]]; do
    case $1 in
        -d|--swarm_ip) swarm_ip="$2"; debug_msg "Set swarm_ip to $swarm_ip"; shift 2 ;;
        -p|--credentials) credentials="$2"; debug_msg "Set credentials"; shift 2 ;;
        -i|--log.level)
            if [[ -n "$new_log_level" ]]; then
                echo "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together."
                usage
            fi
            if [[ ${log_level_names[$2]} ]]; then
                new_log_level=${log_level_names[$2]}
            elif [[ ${log_levels[$2]} ]]; then
                new_log_level=$2
            else
                echo "Invalid log level: $2"
                exit 1
            fi
            currentnew_log_level_name=$(curl --user "$credentials" -sS "http://$swarm_ip:91/api/storage/clusters/$clusterName/settings/log.level" | "$jq_or_grep" -r '.value')
    fi{log_levels[$new_log_level]}
            log_level_type="cluster"
     echo ""     echo -e "New log level: ${BOLD_GREEN}$new_log_level${RESET}" default_log_level=30
        echo -e "Current log level is ${GREEN}$currentdebug_msg "Set new_log_level to $new_log_level ($new_log_level.${RESET}"_name)"
            shift 2
            ;;
 #   Skip update if new level matches the current level -L|--node.log.level)
            if [[ "$current_log_level" -eq-n "$new_log_level" ]]; then
        echo ""       echo "Error: echoOptions -ei "Log(cluster levellog isleve) alreadyand set-L to ${BOLD_GREEN}$new_log_level${RESET}. No changes made."(node log level) cannot be used together."
                returnusage
    fi      # Update thefi
log level using PUT     echo -e "Updating log levelif to[[ ${GREEN}$newlog_log_level${RESET}..."
level_names[$2]} ]]; then
   response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \     new_log_level=${log_level_names[$2]}
   "http://$swarm_ip:91/api/storage/clusters/$clusterName/settings/log.level" \        elif -d[[ "{\"value\": $new_log_level}")

    if [[ "$jq_or_grep" == "grep" ]]; then${log_levels[$2]} || $2 -eq 0 ]]; then
                updatednew_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')$2
         else         updatednew_log_level_name=$(echo "$responsedefault"
| "$jq_or_grep" -r '.value')     fi    else
 if [[ "$updated_log_level" -eq "$new_log_level" ]]; then         echo -e "Log"Invalid log level changed successfully from ${GREEN}$current_log_level${RESET} → ${BOLD_GREEN}$new_log_level${RESET}.": $2"
              else  exit 1
     echo -e "Failed to update log level. Response: ${RED}$response${RESET}"fi
         exit 1  log_level_type="node"
  fi      # Countdown and revert default_log _level=0
    if [[ -n "$duration" && "$duration" -gt 0 ]]; then
    debug_msg "Set new_log_level to $new_log_level ($new_log_level_name)"
    echo -e "Keeping log level at ${YELLOW}$new_log_level${RESET} for ${YELLOW}$duration${RESET} second(s)..."
 shift 2
       echo -e ""   ;;
     for ((i=duration; i>0; i-t|--)time);
do            if printf[[ -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60))n "$2" && "$2" != -* && "$2" -gt 0 ]]; then
              echo -ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r"duration="$2"
               sleep 1debug_msg "Set duration to $duration"
    done         echo -e "\n\nTime's up! Reverting log level back to ${GREEN}$current_log_level${RESET}..."  shift 2
            else
   response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \    echo "Error: Duration must be a number greater  "http://$swarm_ip:91/api/storage/clusters/$clusterName/settings/log.level" \than 0."
               -d "{\"value\": $current_log_level}") exit 1
           if [[ "$jq_or_grep" == "grep" ]]; thenfi
            ;;
    reverted_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
        elseD|--detach) detachable=true; debug_msg "Set detachable to true"; shift ;;
        --debug) debug=true; debug_msg "Set  reverted_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')debug to true"; shift ;;
        *) usage fi;;
    esac
done

debug_msg "Set final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)
        size_diff=$(( final_size - initial_size ))
        size_diff_formatted=$(format_size "$size_diff")log_level_type to ${YELLOW}$log_level_type${RESET}"

# Set default values if not provided
if [[ -z "$new_log_level" ]]; then
    if [[ "$log_level_type" == "cluster" ]]; then
        durationnew_log_formatted=$(format_duration "$duration")level=$default_log_level
    else
     echo -e "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level${RESET}. Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after $duration_formatted."

        if [[ "$reverted_log_level" -eq "$current_log_level" ]]; then
   new_log_level=0
    fi
    new_log_level_name="default"
    new_log_level_name=${log_levels[$new_log_level]}
    debug_msg "Set default new_log_level to $new_log_level ($new_log_level_name)"
fi

# Check if 'screen' or 'tmux' is installed
check_screen_or_tmux

# If swarm_ip is not provided, try using SCSP_HOST environment variable
if [[ -z "$swarm_ip" ]]; then
    if [[ -n "$SCSP_HOST" ]]; then
        swarm_ip="$SCSP_HOST"
        debug "Using Swarm IP from SCSP_HOST: $swarm_ip"
    else
        echo "Error: swarm_ip not provided and SCSP_HOST is not set."
        usage
    fi
fi

# Check if required arguments are provided
if [[ -z "$credentials" || -z "$new_log_level" ]]; then
    usage
fi

# Split the swarm_ip into an array of IP addresses if it contains delimiters
IFS=';, ' read -r -a ip_array <<< "$swarm_ip"

# Retrieve cluster name and handle JSON parsing using the first IP address
debug_msg "Retrieving the cluster name from Swarm API using IP: ${ip_array[0]}"
if [[ "$jq_or_grep" == "grep" ]]; then
    clusterName=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters" | grep -oP '"name":\s*"\K[^"]+')
else
    clusterName=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters" | "$jq_or_grep" -r '._embedded.clusters[0].name')
fi

if [[ -z "$clusterName" ]]; then
    echo "Failed to retrieve the cluster name. Please check your inputs."
    exit 1
fi
debug_msg "Cluster Name: $clusterName"

# Main logic function to run the script tasks
main_script() {
    local swarm_ip="$1"
    local credentials="$2"
    local new_log_level="$3"
    local new_log_level_name="$4"
    local duration="$5"
    local log_level_type="$6"
    # local log_file="/var/log/datacore/castor.log"
    local initial_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)
    local current_log_level
    local clusterName="$7"
    local jq_or_grep="$8"
    local debug="$9"
    debug_msg "**********************************************************"
    debug_msg "local variables"
    debug_msg "Swarm IP: $swarm_ip"
    debug_msg "Credentials: $credentials"
    debug_msg "New Log Level: $new_log_level ($new_log_level_name)"
    debug_msg "Duration: $duration"
    debug_msg "Log Level Type: $log_level_type"
    debug_msg "Log File: $log_file"
    debug_msg "Initial Log File Size: $initial_size"
    debug_msg "Current Log Level: $current_log_level"
    debug_msg "Cluster Name: $clusterName"
    debug_msg "jq_or_grep: $jq_or_grep"
    debug_msg "Debug: $debug"
    debug_msg "**********************************************************"

    # Split the swarm_ip into an array of IP addresses
    IFS=';, ' read -r -a ip_array <<< "$swarm_ip"
    debug_msg "IP Array: ${ip_array[*]}"

    # Display initial information
    if [[ "$log_level_type" == "cluster" ]]; then
        echo -e "Swarm IP: ${GREEN}${ip_array[0]}${RESET}"
    else
        echo -e "Swarm IPs: ${GREEN}${ip_array[*]}${RESET}"
    fi
    # echo -e "Swarm IP: ${GREEN}$swarm_ip${RESET}"
    echo -e "Credentials: ${GREEN}[hidden for security]${RESET}"
    echo -e "Cluster Name: ${GREEN}$clusterName${RESET}"

    debug_msg "Starting main_script function..."
    debug_msg "Log level type: $log_level_type"

    # Store the original log levels
    declare -A original_log_levels
    declare -A original_log_level_names

    if [[ $log_level_type == "cluster" ]]; then
        debug_msg "Setting cluster log level to $new_log_level ($new_log_level_name)"
        # Retrieve current log level
        if [[ "$jq_or_grep" == "grep" ]]; then
            current_log_level=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" | grep -oP '"value":\s*\K[0-9]+')
        else
            current_log_level=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" | "$jq_or_grep" -r '.value')
        fi
        current_log_level_name=${log_levels[$current_log_level]}
        debug_msg "Current cluster log level: ${BOLD_GREEN}$current_log_level${RESET}"
        debug_msg "Current log level: ${BOLD_GREEN}$current_log_level_name${RESET}"
        echo -e ""
        echo -e "New cluster log level: ${BOLD_GREEN}$new_log_level_name${RESET}"
        echo -e "Current cluster log level is ${GREEN}$current_log_level_name.${RESET}"

        # Skip update if new level matches the current level
        if [[ "$current_log_level" -eq "$new_log_level" ]]; then
            echo ""
            echo -e "Cluster log level is already set to ${BOLD_GREEN}$new_log_level_name${RESET}. No changes made."
            return
        fi

        # Update the cluster log level using PUT
        debug_msg "Updating cluster log level to $new_log_level_name"
        debug_msg "Cluster Name: $clusterName"
        response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \
            "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \
            -d "{\"value\": $new_log_level}")
        debug_msg "Response: $response"

        if [[ "$jq_or_grep" == "grep" ]]; then
            updated_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
        else
            updated_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')
        fi
        debug_msg "Updated cluster log level: $updated_log_level"

        if [[ "$updated_log_level" -eq "$new_log_level" ]]; then
            echo -e "Log level changed successfully from ${GREEN}$current_log_level${RESET} → ${BOLD_GREEN}$new_log_level${RESET}."
        else
            echo -e "Failed to update log level. Response: ${RED}$response${RESET}"
            exit 1
        fi

        # Countdown and revert log level
        if [[ -n "$duration" && "$duration" -gt 0 ]]; then
            echo -e "Keeping log level at ${YELLOW}$new_log_level${RESET} for ${YELLOW}$duration${RESET} second(s)..."
            echo -e ""
            for ((i=duration; i>0; i--)); do
                printf -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60))
                echo -ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r"
                sleep 1
            done
            echo -e "\n\nTime's up! Reverting log level back to ${GREEN}$current_log_level${RESET}..."

            # Revert the log level back to the original value
            response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \
                "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \
                -d "{\"value\": $current_log_level}")
            debug_msg "Response: $response"

            if [[ "$jq_or_grep" == "grep" ]]; then
                reverted_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
            else
                reverted_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')
            fi
            debug_msg "Reverted cluster log level: $reverted_log_level"

            final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)
            size_diff=$(( final_size - initial_size ))
            size_diff_formatted=$(format_size "$size_diff")
            duration_formatted=$(format_duration "$duration")
            echo -e "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level${RESET}. Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after $duration_formatted."

            if [[ "$reverted_log_level" -eq "$current_log_level" ]]; then
                echo -e "Log level reverted successfully back to ${BOLD_GREEN}$current_log_level${RESET}."
            else
                echo -e "Failed to revert log level. Response: ${RED}$response${RESET}"
                exit 1
            fi
        else
            echo "Log level change is permanent until manually modified."
        fi
    elif [[ "$log_level_type" == "node" ]]; then
         # First loop: Change the node log level
        for ip in "${ip_array[@]}"; do
            # Retrieve current node log level
            debug_msg "Retrieving current node log level for IP: $ip"
            debug_msg "curl -s -u \"$credentials\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel\""
            current_log_level=$(curl -s -u "$credentials" "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel" | $jq_or_grep -r '.value')
            original_log_levels["$ip"]=$current_log_level
            if [[ "$current_log_level" == "0" ]]; then
                current_log_level_name="default"
            else
                current_log_level_name=${log_levels[$current_log_level]}
            fi
            original_log_level_names["$ip"]=$current_log_level_name
            if [[ "$new_log_level" == "0" ]]; then
                new_log_level_name="default"
            else
                new_log_level_name=${log_levels[$new_log_level]}
            fi
            debug_msg "Current log level for IP $ip: $current_log_level ($current_log_level_name)"
            echo ""
            echo -e "New node log level: ${BOLD_GREEN}$new_log_level_name${RESET}"
            echo -e "Current node log level for IP $ip is ${GREEN}$current_log_level_name.${RESET}"

            # Skip update if new level matches the current level
            if [[ "$current_log_level" -eq "$new_log_level" ]]; then
                echo ""
                echo -e "Node log level for IP $ip is already set to ${BOLD_GREEN}$new_log_level_name${RESET}. No changes made."
                continue
            fi

            # Update the node log level using PUT
            debug_msg "Updating node log level to $new_log_level_name for IP $ip..."
            debug_msg "curl --user \"$credentials\" -sS -X PUT -H \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level\""
            response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \
                "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level")
            debug_msg "Response: $response"

            if [[ "$jq_or_grep" == "grep" ]]; then
                updated_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
            else
                updated_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')
            fi
            debug_msg "Updated log level for IP $ip: $updated_log_level"

            if [[ "$updated_log_level" -eq "$new_log_level" ]]; then
                echo -e "Node log level for IP $ip changed successfully from ${GREEN}$current_log_level_name${RESET} → ${BOLD_GREEN}$new_log_level_name${RESET}."
            else
                echo -e "Failed to update node log level for IP $ip. Response: ${RED}$response${RESET}"
                exit 1
            fi
        done

        # Second loop: Countdown if duration is provided
        if [[ -n "$duration" && "$duration" -gt 0 ]]; then
            echo -e ""
            echo -e "Keeping node(s) log level at ${YELLOW}$new_log_level_name${RESET} for ${YELLOW}$duration${RESET} second(s)..."
            echo -e ""
            for ((i=duration; i>0; i--)); do
                printf -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60))
                echo -ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r"
                sleep 1
            done
            echo -e "\n\nTime's up! Reverting node log level back to original levels..."

            # Third loop: Revert the node log level
            for ip in "${ip_array[@]}"; do
                current_log_level=${original_log_levels["$ip"]}
                current_log_level_name=${original_log_level_names["$ip"]}
                debug_msg "Reverting node log level back to $current_log_level_name for IP $ip..."
                debug_msg "curl --user \"$credentials\" -sS -X PUT -H \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level\""
                response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \
                    "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level")
                debug_msg "Response: $response"

                if [[ "$jq_or_grep" == "grep" ]]; then
                    reverted_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
                else
                    reverted_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')
                fi

                final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)
                size_diff=$(( final_size - initial_size ))
                size_diff_formatted=$(format_size "$size_diff")
                duration_formatted=$(format_duration "$duration")

                if [[ "$reverted_log_level" -eq "$current_log_level" ]]; then
                    echo -e "Node log level for IP $ip reverted successfully back to ${BOLD_GREEN}${current_log_level_name}${RESET}."
                else
                    echo -e "Failed to revert node log level for IP $ip. Response: ${RED}$response${RESET}"
                    exit 1
                fi
            done

            # Combine the log output for multiple IP addresses into a single summary
            echo -e ""
            echo -e "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level_name${RESET} for IP ${ip_array[*]}. Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after $duration_formatted."
        else
            echo "Log level change is permanent until manually modified."
        fi
    fi
}

# Run in detachable or directly
if $detachable; then
    # Pass the main_script function to the screen session and store the output in a file
    if command -v screen &>/dev/null; then
        # screen -dmS indexer_script bash -c "$(declare -f main_script format_size format_duration check_jq debug); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" | tee \"$output_log\""
        screen -dmS indexer_script bash -c "$(declare -f main_script debug_msg format_size format_duration check_jq); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" \"$debug\" | tee \"$output_log\""
        echoscreen -e "Log level reverted successfully back to ${BOLD_GREEN}$current_log_level${RESET}."
r indexer_script
    elif command -v tmux &>/dev/null; then
       else # tmux new-session -d -s indexer_script "$(declare -f main_script format_size format_duration  echo -e "Failed to revert log level. Response: ${RED}$response${RESET}"
    check_jq debug); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" | tee \"$output_log\""
       exit 1tmux new-session -d -s indexer_script "$(declare -f main_script  fi
    else
        echo "Log level change is permanent until manually modified.debug_msg format_size format_duration check_jq); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" \"$debug\" | tee \"$output_log\""
    fi }  # Runtmux in persistent or directly
if $persistent; then
attach-session -t indexer_script
    else
   # Pass the main_script function to the screen session and store the output in a file
    if command -v screen &>/dev/null; thenecho "Error: Neither screen nor tmux available. Run without --detachable."
        exit 1
  screen -dmS indexer_scriptfi
bash
-c "$(declare -f main_script format_size format_duration check_jq debug); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$duration\" \"$clusterName\" \"$jq_or_grep\" | tee \"$output_log\""
   # Wait for the screen session to complete and then display the output log
    sleep 1
    while screen -r list | grep -q "indexer_script"; do
        sleep elif1
command -v tmux &>/dev/null; then done

    echo ""
 tmux new-session -d -scat indexer"$output_script log"$(declare
-felse
main_script format_size format_duration check_jq debug); # main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$duration\" \"$clusterName\" \"$jq_or_grep\" | tee \"$output_log\""
        tmux attach-session -t indexer_script
    else
        echo "Error: Neither screen nor tmux available. Run without --persistent.debug_msg "***********************************"
    debug_msg "Parameters passed to exitmain_script:"
1    debug_msg fi"Swarm IP: $swarm_ip"
   # Wait for the screen session to complete and then display the output logdebug_msg "Credentials: $credentials"
    debug_msg "New Log Level: $new_log_level"
    sleep 1
    while screen -list | grep -q "indexer_script"; do
debug_msg "Duration: $duration"
    debug_msg "Cluster Name: sleep$clusterName"
1     done
debug_msg "jq_or_grep: $jq_or_grep"
    echo "debug_msg "***********************************"
    catdebug_msg "$output_logRunning main_script function..."
else     main_script "$swarm_ip" "$credentials" "$new_log_level" "$new_log_level_name" "$duration" "$log_level_type" "$clusterName" "$jq_or_grep" | tee "$output_log"
fi

...