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.
This commit is contained in:
CanbiZ 2025-11-04 16:59:02 +01:00
parent a6cdb474a1
commit b55e8f5f34
3 changed files with 144 additions and 105 deletions

View File

@ -27,7 +27,8 @@ variables() {
DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. 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. 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. 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 # Get Proxmox VE version and kernel version
if command -v pveversion >/dev/null 2>&1; then if command -v pveversion >/dev/null 2>&1; then
@ -2144,6 +2145,7 @@ build_container() {
fi fi
export DIAGNOSTICS="$DIAGNOSTICS" export DIAGNOSTICS="$DIAGNOSTICS"
export RANDOM_UUID="$RANDOM_UUID" export RANDOM_UUID="$RANDOM_UUID"
export SESSION_ID="$SESSION_ID"
export CACHER="$APT_CACHER" export CACHER="$APT_CACHER"
export CACHER_IP="$APT_CACHER_IP" export CACHER_IP="$APT_CACHER_IP"
export tz="$timezone" export tz="$timezone"

View File

@ -108,7 +108,7 @@ set_std_mode() {
fi fi
} }
SILENT_LOGFILE="/tmp/silent.$$.log" SILENT_LOGFILE="/tmp/install-$(date +%Y%m%d_%H%M%S)_${SESSION_ID:-$(date +%s)}.log"
silent() { silent() {
local cmd="$*" local cmd="$*"
@ -133,8 +133,8 @@ silent() {
explanation="$(explain_exit_code "$rc")" explanation="$(explain_exit_code "$rc")"
printf "\e[?25h" printf "\e[?25h"
echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation})" msg_error "in line ${caller_line}: exit code ${rc} (${explanation})"
echo -e "${RD}Command:${CL} ${YWB}${cmd}${CL}\n" msg_custom "→" "${YWB}" "${cmd}"
if [[ -s "$SILENT_LOGFILE" ]]; then if [[ -s "$SILENT_LOGFILE" ]]; then
local log_lines=$(wc -l <"$SILENT_LOGFILE") local log_lines=$(wc -l <"$SILENT_LOGFILE")
@ -144,7 +144,7 @@ silent() {
# Show how to view full log if there are more lines # Show how to view full log if there are more lines
if [[ $log_lines -gt 10 ]]; then 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
fi fi

View File

@ -8,141 +8,178 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
explain_exit_code() { explain_exit_code() {
local code="$1" local code="$1"
case "$code" in case "$code" in
# --- Generic / Shell --- # --- Generic / Shell ---
1) echo "General error / Operation not permitted" ;; 1) echo "General error / Operation not permitted" ;;
2) echo "Misuse of shell builtins (e.g. syntax error)" ;; 2) echo "Misuse of shell builtins (e.g. syntax error)" ;;
126) echo "Command invoked cannot execute (permission problem?)" ;; 126) echo "Command invoked cannot execute (permission problem?)" ;;
127) echo "Command not found" ;; 127) echo "Command not found" ;;
128) echo "Invalid argument to exit" ;; 128) echo "Invalid argument to exit" ;;
130) echo "Terminated by Ctrl+C (SIGINT)" ;; 130) echo "Terminated by Ctrl+C (SIGINT)" ;;
137) echo "Killed (SIGKILL / Out of memory?)" ;; 137) echo "Killed (SIGKILL / Out of memory?)" ;;
139) echo "Segmentation fault (core dumped)" ;; 139) echo "Segmentation fault (core dumped)" ;;
143) echo "Terminated (SIGTERM)" ;; 143) echo "Terminated (SIGTERM)" ;;
# --- Package manager / APT / DPKG --- # --- Package manager / APT / DPKG ---
100) echo "APT: Package manager error (broken packages / dependency problems)" ;; 100) echo "APT: Package manager error (broken packages / dependency problems)" ;;
101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;;
255) echo "DPKG: Fatal internal error" ;; 255) echo "DPKG: Fatal internal error" ;;
# --- Node.js / npm / pnpm / yarn --- # --- Node.js / npm / pnpm / yarn ---
243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;;
245) echo "Node.js: Invalid command-line option" ;; 245) echo "Node.js: Invalid command-line option" ;;
246) echo "Node.js: Internal JavaScript Parse Error" ;; 246) echo "Node.js: Internal JavaScript Parse Error" ;;
247) echo "Node.js: Fatal internal error" ;; 247) echo "Node.js: Fatal internal error" ;;
248) echo "Node.js: Invalid C++ addon / N-API failure" ;; 248) echo "Node.js: Invalid C++ addon / N-API failure" ;;
249) echo "Node.js: Inspector error" ;; 249) echo "Node.js: Inspector error" ;;
254) echo "npm/pnpm/yarn: Unknown fatal error" ;; 254) echo "npm/pnpm/yarn: Unknown fatal error" ;;
# --- Python / pip / uv --- # --- Python / pip / uv ---
210) echo "Python: Virtualenv / uv environment missing or broken" ;; 210) echo "Python: Virtualenv / uv environment missing or broken" ;;
211) echo "Python: Dependency resolution failed" ;; 211) echo "Python: Dependency resolution failed" ;;
212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;;
# --- PostgreSQL --- # --- PostgreSQL ---
231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;;
232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;;
233) echo "PostgreSQL: Database does not exist" ;; 233) echo "PostgreSQL: Database does not exist" ;;
234) echo "PostgreSQL: Fatal error in query / syntax" ;; 234) echo "PostgreSQL: Fatal error in query / syntax" ;;
# --- MySQL / MariaDB --- # --- MySQL / MariaDB ---
241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;;
242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;;
243) echo "MySQL/MariaDB: Database does not exist" ;; 243) echo "MySQL/MariaDB: Database does not exist" ;;
244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;;
# --- MongoDB --- # --- MongoDB ---
251) echo "MongoDB: Connection failed (server not running)" ;; 251) echo "MongoDB: Connection failed (server not running)" ;;
252) echo "MongoDB: Authentication failed (bad user/password)" ;; 252) echo "MongoDB: Authentication failed (bad user/password)" ;;
253) echo "MongoDB: Database not found" ;; 253) echo "MongoDB: Database not found" ;;
254) echo "MongoDB: Fatal query error" ;; 254) echo "MongoDB: Fatal query error" ;;
# --- Proxmox Custom Codes --- # --- Proxmox Custom Codes ---
200) echo "Custom: Failed to create lock file" ;; 200) echo "Custom: Failed to create lock file" ;;
203) echo "Custom: Missing CTID variable" ;; 203) echo "Custom: Missing CTID variable" ;;
204) echo "Custom: Missing PCT_OSTYPE variable" ;; 204) echo "Custom: Missing PCT_OSTYPE variable" ;;
205) echo "Custom: Invalid CTID (<100)" ;; 205) echo "Custom: Invalid CTID (<100)" ;;
209) echo "Custom: Container creation failed" ;; 209) echo "Custom: Container creation failed" ;;
210) echo "Custom: Cluster not quorate" ;; 210) echo "Custom: Cluster not quorate" ;;
214) echo "Custom: Not enough storage space" ;; 214) echo "Custom: Not enough storage space" ;;
215) echo "Custom: Container ID not listed" ;; 215) echo "Custom: Container ID not listed" ;;
216) echo "Custom: RootFS entry missing in config" ;; 216) echo "Custom: RootFS entry missing in config" ;;
217) echo "Custom: Storage does not support rootdir" ;; 217) echo "Custom: Storage does not support rootdir" ;;
220) echo "Custom: Unable to resolve template path" ;; 220) echo "Custom: Unable to resolve template path" ;;
222) echo "Custom: Template download failed after 3 attempts" ;; 222) echo "Custom: Template download failed after 3 attempts" ;;
223) echo "Custom: Template not available after download" ;; 223) echo "Custom: Template not available after download" ;;
231) echo "Custom: LXC stack upgrade/retry failed" ;; 231) echo "Custom: LXC stack upgrade/retry failed" ;;
# --- Default --- # --- Default ---
*) echo "Unknown error" ;; *) echo "Unknown error" ;;
esac esac
} }
# === Error handler ============================================================ # === Error handler ============================================================
error_handler() { error_handler() {
local exit_code=${1:-$?} local exit_code=${1:-$?}
local command=${2:-${BASH_COMMAND:-unknown}} local command=${2:-${BASH_COMMAND:-unknown}}
local line_number=${BASH_LINENO[0]:-unknown} local line_number=${BASH_LINENO[0]:-unknown}
command="${command//\$STD/}" command="${command//\$STD/}"
if [[ "$exit_code" -eq 0 ]]; then if [[ "$exit_code" -eq 0 ]]; then
return 0 return 0
fi fi
local explanation local explanation
explanation="$(explain_exit_code "$exit_code")" 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" 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 if [[ -n "${DEBUG_LOGFILE:-}" ]]; then
{ {
echo "------ ERROR ------" echo "------ ERROR ------"
echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')"
echo "Exit Code : $exit_code ($explanation)" echo "Exit Code : $exit_code ($explanation)"
echo "Line : $line_number" echo "Line : $line_number"
echo "Command : $command" echo "Command : $command"
echo "-------------------" echo "-------------------"
} >>"$DEBUG_LOGFILE" } >>"$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
fi
if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then exit "$exit_code"
echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---"
tail -n 20 "$SILENT_LOGFILE"
echo "---------------------------------------------------"
fi
exit "$exit_code"
} }
# === Exit handler ============================================================= # === Exit handler =============================================================
on_exit() { on_exit() {
local exit_code=$? local exit_code=$?
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
exit "$exit_code" exit "$exit_code"
} }
# === Signal handlers ========================================================== # === Signal handlers ==========================================================
on_interrupt() { 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}" echo -e "\n${RD}Interrupted by user (SIGINT)${CL}"
exit 130 fi
exit 130
} }
on_terminate() { 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}" echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}"
exit 143 fi
exit 143
} }
# === Init traps =============================================================== # === Init traps ===============================================================
catch_errors() { catch_errors() {
set -Ee -o pipefail set -Ee -o pipefail
if [ "${STRICT_UNSET:-0}" = "1" ]; then if [ "${STRICT_UNSET:-0}" = "1" ]; then
set -u set -u
fi fi
trap 'error_handler' ERR trap 'error_handler' ERR
trap on_exit EXIT trap on_exit EXIT
trap on_interrupt INT trap on_interrupt INT
trap on_terminate TERM trap on_terminate TERM
} }