From 3c3aabd21d265af99a8d13a455a5f3d0490a384e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 14:28:16 +0200 Subject: [PATCH] feat: expand logger with subshell-safe error handling --- misc/alpine-install.func | 1 + misc/api.func | 10 +++++ misc/build.func | 1 + misc/core.func | 19 ++++++++- misc/create_lxc.sh | 1 + misc/install.func | 1 + misc/logger.func | 88 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 misc/logger.func diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 450e9209..660a3227 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -32,6 +32,7 @@ error_handler() { local exit_code="$?" local line_number="$1" local command="$2" + log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" } diff --git a/misc/api.func b/misc/api.func index 08bdc914..58ea56e0 100644 --- a/misc/api.func +++ b/misc/api.func @@ -2,6 +2,16 @@ # Author: michelroegl-brunner # License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE +if [[ -f "$(dirname "${BASH_SOURCE[0]}")/logger.func" ]]; then + source "$(dirname "${BASH_SOURCE[0]}")/logger.func" +else + if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) + elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) + fi +fi + get_error_description() { local exit_code="$1" case "$exit_code" in diff --git a/misc/build.func b/misc/build.func index 8a58664f..c2e78b58 100644 --- a/misc/build.func +++ b/misc/build.func @@ -43,6 +43,7 @@ error_handler() { local line_number="$1" local command="$2" printf "\e[?25h" + log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" post_update_to_api "failed" "$command" echo -e "\n$error_message\n" diff --git a/misc/core.func b/misc/core.func index 5df50efd..6ed1faf3 100644 --- a/misc/core.func +++ b/misc/core.func @@ -9,6 +9,16 @@ [[ -n "${_CORE_FUNC_LOADED:-}" ]] && return _CORE_FUNC_LOADED=1 +if [[ -f "$(dirname "${BASH_SOURCE[0]}")/logger.func" ]]; then + source "$(dirname "${BASH_SOURCE[0]}")/logger.func" +else + if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) + elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) + fi +fi + load_functions() { [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return __FUNCTIONS_LOADED=1 @@ -96,9 +106,9 @@ _tool_error_hint() { # exit 143 # } +# logger.func now sets strict modes and traps globally catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR + : } # ------------------------------------------------------------------------------ @@ -339,6 +349,7 @@ stop_spinner() { msg_info() { local msg="$1" [[ -z "$msg" ]] && return + log_info "$msg" if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then declare -gA MSG_INFO_SHOWN=() @@ -365,6 +376,7 @@ msg_info() { msg_ok() { local msg="$1" [[ -z "$msg" ]] && return + log_info "$msg" stop_spinner clear_line printf "%s %b\n" "$CM" "${GN}${msg}${CL}" >&2 @@ -374,12 +386,14 @@ msg_ok() { msg_error() { stop_spinner local msg="$1" + log_error "$msg" echo -e "${BFR:-} ${CROSS:-✖️} ${RD}${msg}${CL}" } msg_warn() { stop_spinner local msg="$1" + log_warn "$msg" echo -e "${BFR:-} ${INFO:-ℹ️} ${YWB}${msg}${CL}" } @@ -395,6 +409,7 @@ msg_custom() { function msg_debug() { if [[ "${var_full_verbose:-0}" == "1" ]]; then [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 + log_debug "$*" echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" fi } diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 7a5d18cf..3ada6c96 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -36,6 +36,7 @@ function error_handler() { local line_number="$1" local command="$2" printf "\e[?25h" + log_error "line $line_number: exit code $exit_code while executing command $command" echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" exit "$exit_code" } diff --git a/misc/install.func b/misc/install.func index e3751c29..f7ee6745 100644 --- a/misc/install.func +++ b/misc/install.func @@ -36,6 +36,7 @@ error_handler() { local exit_code="$?" local line_number="$1" local command="$2" + log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message" diff --git a/misc/logger.func b/misc/logger.func new file mode 100644 index 00000000..0b929011 --- /dev/null +++ b/misc/logger.func @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# Centralized logging utilities for Proxmox helper scripts +# Provides log_* functions, stdout/stderr capture, and robust error handling. + +# Prevent multiple sourcing +[[ -n "${_LOGGER_FUNC_LOADED:-}" ]] && return +_LOGGER_FUNC_LOADED=1 + +# ------------------------------------------------------------------------------ +# Shell options – fail fast and propagate ERR through subshells +# ------------------------------------------------------------------------------ +set -o errexit -o nounset -o pipefail -o errtrace + +# ------------------------------------------------------------------------------ +# Logfile preparation +# ------------------------------------------------------------------------------ +LOGDIR=${LOGDIR:-/var/log/proxmoxve} +mkdir -p "$LOGDIR" 2>/dev/null || true + +SCRIPT_NAME="${SCRIPT_NAME:-$(basename "$0")}"; +RUN_ID="${RUN_ID:-$(date +%Y%m%d_%H%M%S)_$$}"; +LOGFILE="${LOGFILE:-$LOGDIR/${SCRIPT_NAME%.sh}_$RUN_ID.log}" + +LOG_LEVEL="${LOG_LEVEL:-INFO}" +declare -A LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3) + +# Preserve original stdout/stderr for terminal output +exec 3>&1 4>&2 + +log_msg() { + local level="$1"; shift + local msg="$*" + local ts + ts="$(date '+%Y-%m-%d %H:%M:%S')" + echo "[$ts] [$SCRIPT_NAME] [$level] $msg" >>"$LOGFILE" + if (( ${LEVELS[$level]} >= ${LEVELS[$LOG_LEVEL]} )); then + case "$level" in + DEBUG) + [[ "${var_full_verbose:-0}" -eq 1 ]] && echo -e "\033[36m[DEBUG]\033[0m $msg" >&3 ;; + INFO) + echo -e "\033[34m[INFO]\033[0m $msg" >&3 ;; + WARN) + echo -e "\033[33m[WARN]\033[0m $msg" >&3 ;; + ERROR) + echo -e "\033[31m[ERROR]\033[0m $msg" >&4 ;; + esac + fi +} + +log_debug() { log_msg DEBUG "$*"; } +log_info() { log_msg INFO "$*"; } +log_warn() { log_msg WARN "$*"; } +log_error() { log_msg ERROR "$*"; } + +# Backward compatible wrappers +msg_info() { log_info "ℹ️ $*"; } +msg_ok() { log_info "✅ $*"; } +msg_warn() { log_warn "⚠️ $*"; } +msg_error() { log_error "❌ $*"; } +msg_debug() { log_debug "$*"; } + +# ------------------------------------------------------------------------------ +# Capture arbitrary stdout/stderr (including from subshells) +# ------------------------------------------------------------------------------ +log_stream() { + local level="$1" + while IFS= read -r line; do + log_msg "$level" "$line" + done +} + +# Redirect script output through logger +exec > >(log_stream INFO) 2> >(log_stream ERROR) + +# ------------------------------------------------------------------------------ +# Error handler – logs failing command, line, and exits +# ------------------------------------------------------------------------------ +error_handler() { + local code="$?" + local cmd="${BASH_COMMAND:-unknown}" + local line="${BASH_LINENO[0]:-unknown}" + local file="${BASH_SOURCE[1]:-unknown}" + log_error "command '$cmd' failed in $file:$line with exit code $code" + exit "$code" +} + +trap error_handler ERR +