From b55e8f5f34c3e6a4381f9c21554cb1edb6591d53 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:59:02 +0100 Subject: [PATCH] Improve logging and error handling with session IDs Introduces a SESSION_ID variable for log file naming and tracking, updates log file paths to include timestamps and session IDs, and enhances error handling output to use custom message functions when available. Also improves log file management and user guidance for viewing logs, and refactors error handler to better support containerized environments. --- misc/build.func | 4 +- misc/core.func | 8 +- misc/error_handler.func | 237 +++++++++++++++++++++++----------------- 3 files changed, 144 insertions(+), 105 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0dea49bd3..a2270e845 100644 --- a/misc/build.func +++ b/misc/build.func @@ -27,7 +27,8 @@ variables() { DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}"} # Get Proxmox VE version and kernel version if command -v pveversion >/dev/null 2>&1; then @@ -2144,6 +2145,7 @@ build_container() { fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" + export SESSION_ID="$SESSION_ID" export CACHER="$APT_CACHER" export CACHER_IP="$APT_CACHER_IP" export tz="$timezone" diff --git a/misc/core.func b/misc/core.func index 699ac1168..dfa6b2e7e 100644 --- a/misc/core.func +++ b/misc/core.func @@ -108,7 +108,7 @@ set_std_mode() { fi } -SILENT_LOGFILE="/tmp/silent.$$.log" +SILENT_LOGFILE="/tmp/install-$(date +%Y%m%d_%H%M%S)_${SESSION_ID:-$(date +%s)}.log" silent() { local cmd="$*" @@ -133,8 +133,8 @@ silent() { explanation="$(explain_exit_code "$rc")" printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation})" - echo -e "${RD}Command:${CL} ${YWB}${cmd}${CL}\n" + msg_error "in line ${caller_line}: exit code ${rc} (${explanation})" + msg_custom "→" "${YWB}" "${cmd}" if [[ -s "$SILENT_LOGFILE" ]]; then local log_lines=$(wc -l <"$SILENT_LOGFILE") @@ -144,7 +144,7 @@ silent() { # Show how to view full log if there are more lines if [[ $log_lines -gt 10 ]]; then - echo -e "${YW}View full log (${log_lines} lines):${CL} cat $SILENT_LOGFILE" + msg_custom "📋" "${YW}" "View full log (${log_lines} lines): /tmp/install-*_${SESSION_ID:-*}.log" fi fi diff --git a/misc/error_handler.func b/misc/error_handler.func index d2f21d087..2599d59b0 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -8,141 +8,178 @@ # ------------------------------------------------------------------------------ explain_exit_code() { - local code="$1" - case "$code" in - # --- Generic / Shell --- - 1) echo "General error / Operation not permitted" ;; - 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; - 126) echo "Command invoked cannot execute (permission problem?)" ;; - 127) echo "Command not found" ;; - 128) echo "Invalid argument to exit" ;; - 130) echo "Terminated by Ctrl+C (SIGINT)" ;; - 137) echo "Killed (SIGKILL / Out of memory?)" ;; - 139) echo "Segmentation fault (core dumped)" ;; - 143) echo "Terminated (SIGTERM)" ;; + local code="$1" + case "$code" in + # --- Generic / Shell --- + 1) echo "General error / Operation not permitted" ;; + 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; + 126) echo "Command invoked cannot execute (permission problem?)" ;; + 127) echo "Command not found" ;; + 128) echo "Invalid argument to exit" ;; + 130) echo "Terminated by Ctrl+C (SIGINT)" ;; + 137) echo "Killed (SIGKILL / Out of memory?)" ;; + 139) echo "Segmentation fault (core dumped)" ;; + 143) echo "Terminated (SIGTERM)" ;; - # --- Package manager / APT / DPKG --- - 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; - 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; - 255) echo "DPKG: Fatal internal error" ;; + # --- Package manager / APT / DPKG --- + 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; + 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; + 255) echo "DPKG: Fatal internal error" ;; - # --- Node.js / npm / pnpm / yarn --- - 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; - 245) echo "Node.js: Invalid command-line option" ;; - 246) echo "Node.js: Internal JavaScript Parse Error" ;; - 247) echo "Node.js: Fatal internal error" ;; - 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; - 249) echo "Node.js: Inspector error" ;; - 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; + # --- Node.js / npm / pnpm / yarn --- + 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; + 245) echo "Node.js: Invalid command-line option" ;; + 246) echo "Node.js: Internal JavaScript Parse Error" ;; + 247) echo "Node.js: Fatal internal error" ;; + 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; + 249) echo "Node.js: Inspector error" ;; + 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; - # --- Python / pip / uv --- - 210) echo "Python: Virtualenv / uv environment missing or broken" ;; - 211) echo "Python: Dependency resolution failed" ;; - 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; + # --- Python / pip / uv --- + 210) echo "Python: Virtualenv / uv environment missing or broken" ;; + 211) echo "Python: Dependency resolution failed" ;; + 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; - # --- PostgreSQL --- - 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; - 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; - 233) echo "PostgreSQL: Database does not exist" ;; - 234) echo "PostgreSQL: Fatal error in query / syntax" ;; + # --- PostgreSQL --- + 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; + 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; + 233) echo "PostgreSQL: Database does not exist" ;; + 234) echo "PostgreSQL: Fatal error in query / syntax" ;; - # --- MySQL / MariaDB --- - 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; - 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; - 243) echo "MySQL/MariaDB: Database does not exist" ;; - 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; + # --- MySQL / MariaDB --- + 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; + 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; + 243) echo "MySQL/MariaDB: Database does not exist" ;; + 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; - # --- MongoDB --- - 251) echo "MongoDB: Connection failed (server not running)" ;; - 252) echo "MongoDB: Authentication failed (bad user/password)" ;; - 253) echo "MongoDB: Database not found" ;; - 254) echo "MongoDB: Fatal query error" ;; + # --- MongoDB --- + 251) echo "MongoDB: Connection failed (server not running)" ;; + 252) echo "MongoDB: Authentication failed (bad user/password)" ;; + 253) echo "MongoDB: Database not found" ;; + 254) echo "MongoDB: Fatal query error" ;; - # --- Proxmox Custom Codes --- - 200) echo "Custom: Failed to create lock file" ;; - 203) echo "Custom: Missing CTID variable" ;; - 204) echo "Custom: Missing PCT_OSTYPE variable" ;; - 205) echo "Custom: Invalid CTID (<100)" ;; - 209) echo "Custom: Container creation failed" ;; - 210) echo "Custom: Cluster not quorate" ;; - 214) echo "Custom: Not enough storage space" ;; - 215) echo "Custom: Container ID not listed" ;; - 216) echo "Custom: RootFS entry missing in config" ;; - 217) echo "Custom: Storage does not support rootdir" ;; - 220) echo "Custom: Unable to resolve template path" ;; - 222) echo "Custom: Template download failed after 3 attempts" ;; - 223) echo "Custom: Template not available after download" ;; - 231) echo "Custom: LXC stack upgrade/retry failed" ;; + # --- Proxmox Custom Codes --- + 200) echo "Custom: Failed to create lock file" ;; + 203) echo "Custom: Missing CTID variable" ;; + 204) echo "Custom: Missing PCT_OSTYPE variable" ;; + 205) echo "Custom: Invalid CTID (<100)" ;; + 209) echo "Custom: Container creation failed" ;; + 210) echo "Custom: Cluster not quorate" ;; + 214) echo "Custom: Not enough storage space" ;; + 215) echo "Custom: Container ID not listed" ;; + 216) echo "Custom: RootFS entry missing in config" ;; + 217) echo "Custom: Storage does not support rootdir" ;; + 220) echo "Custom: Unable to resolve template path" ;; + 222) echo "Custom: Template download failed after 3 attempts" ;; + 223) echo "Custom: Template not available after download" ;; + 231) echo "Custom: LXC stack upgrade/retry failed" ;; - # --- Default --- - *) echo "Unknown error" ;; - esac + # --- Default --- + *) echo "Unknown error" ;; + esac } # === Error handler ============================================================ error_handler() { - local exit_code=${1:-$?} - local command=${2:-${BASH_COMMAND:-unknown}} - local line_number=${BASH_LINENO[0]:-unknown} + local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} + local line_number=${BASH_LINENO[0]:-unknown} - command="${command//\$STD/}" + command="${command//\$STD/}" - if [[ "$exit_code" -eq 0 ]]; then - return 0 - fi + if [[ "$exit_code" -eq 0 ]]; then + return 0 + fi - local explanation - explanation="$(explain_exit_code "$exit_code")" + local explanation + explanation="$(explain_exit_code "$exit_code")" - printf "\e[?25h" + printf "\e[?25h" + + # Use msg_error if available, fallback to echo + if declare -f msg_error >/dev/null 2>&1; then + msg_error "in line ${line_number}: exit code ${exit_code} (${explanation}): while executing command ${command}" + else echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" + fi - if [[ -n "${DEBUG_LOGFILE:-}" ]]; then - { - echo "------ ERROR ------" - echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" - echo "Exit Code : $exit_code ($explanation)" - echo "Line : $line_number" - echo "Command : $command" - echo "-------------------" - } >>"$DEBUG_LOGFILE" + if [[ -n "${DEBUG_LOGFILE:-}" ]]; then + { + echo "------ ERROR ------" + echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" + echo "Exit Code : $exit_code ($explanation)" + echo "Line : $line_number" + echo "Command : $command" + echo "-------------------" + } >>"$DEBUG_LOGFILE" + fi + + if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then + echo "--- Last 20 lines of silent log ---" + tail -n 20 "$SILENT_LOGFILE" + echo "-----------------------------------" + + if [[ -n "${CTID:-}" ]]; then + local HOST_LOG="/tmp/install-$(date +%Y%m%d_%H%M%S)_${SESSION_ID:-error}.log" + if pct push $CTID "$SILENT_LOGFILE" "$HOST_LOG" 2>/dev/null; then + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "✓" "${GN}" "Full log saved to host: ${HOST_LOG}" + else + echo -e "${GN}✓ Full log saved to host:${CL} ${BL}${HOST_LOG}${CL}" + fi + else + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "📋" "${YW}" "Full log path in container: ${SILENT_LOGFILE}" + else + echo -e "${YW}Full log path in container:${CL} ${BL}${SILENT_LOGFILE}${CL}" + fi + fi + else + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "📋" "${YW}" "Full log: ${SILENT_LOGFILE}" + else + echo -e "${YW}Full log:${CL} ${BL}${SILENT_LOGFILE}${CL}" + fi fi + fi - if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then - echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" - tail -n 20 "$SILENT_LOGFILE" - echo "---------------------------------------------------" - fi - - exit "$exit_code" + exit "$exit_code" } # === Exit handler ============================================================= on_exit() { - local exit_code=$? - [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" - exit "$exit_code" + local exit_code=$? + [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" + exit "$exit_code" } # === Signal handlers ========================================================== on_interrupt() { + if declare -f msg_error >/dev/null 2>&1; then + msg_error "Interrupted by user (SIGINT)" + else echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" - exit 130 + fi + exit 130 } on_terminate() { + if declare -f msg_error >/dev/null 2>&1; then + msg_error "Terminated by signal (SIGTERM)" + else echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" - exit 143 + fi + exit 143 } # === Init traps =============================================================== catch_errors() { - set -Ee -o pipefail - if [ "${STRICT_UNSET:-0}" = "1" ]; then - set -u - fi - trap 'error_handler' ERR - trap on_exit EXIT - trap on_interrupt INT - trap on_terminate TERM + set -Ee -o pipefail + if [ "${STRICT_UNSET:-0}" = "1" ]; then + set -u + fi + trap 'error_handler' ERR + trap on_exit EXIT + trap on_interrupt INT + trap on_terminate TERM }