github.com/verrazzano/verrazzano@v1.7.0/platform-operator/scripts/install/logging.sh (about) 1 # 2 # Copyright (c) 2020, 2021, Oracle and/or its affiliates. 3 # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 4 # 5 6 # The absolute file and directory path of the calling script. 7 _LOGGING_CALLER_NAME="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}" 8 _LOGGING_CALLER_PATH="$(cd $(dirname ${_LOGGING_CALLER_NAME}); pwd -P)/$(basename ${_LOGGING_CALLER_NAME})" 9 _LOGGING_CALLER_DIR="$(cd $(dirname ${_LOGGING_CALLER_PATH}); pwd -P)" 10 11 # Determine the log file location. 12 export LOG_FILE="${LOG_FILE:-${_LOGGING_CALLER_DIR}/build/logs/$(basename ${_LOGGING_CALLER_NAME}).log}" 13 export LOGFILE="${LOG_FILE}" 14 # Ensure log dir exists. 15 if [ ! -d "$(dirname ${LOG_FILE})" ] ; then 16 mkdir -p "$(dirname ${LOG_FILE})" 17 fi 18 19 # Duplicate stdout and stderr file descriptors. 20 # Only do this if they have not already been duplicated. 21 if [ -z "${_LOGGING_CONSOLE_STDOUT:-}" ]; then 22 export _LOGGING_CONSOLE_STDOUT=4 #"/dev/fd/5" 23 export _LOGGING_CONSOLE_STDERR=5 #"/dev/fd/6" 24 exec 4>&1 5<&2 25 26 # Redirect stdout and stderr for this shell to a log file. 27 echo "Output redirected to ${LOG_FILE}" 28 if [ ${DEBUG:-0} -ge 4 ]; then 29 exec 1>> "$LOG_FILE" 2>&1 30 31 # Enable full shell trace logging. 32 set -x 33 else 34 # The >(...) portion prefixes a timestamp to every line of the merged stdout/stderr. 35 exec &> >( while IFS= read -r l; do printf '[%s] %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S %Z')" "$l"; done 1>> ${LOG_FILE} ) 36 fi 37 echo "Output captured for ${_LOGGING_CALLER_PATH}" 38 fi 39 40 ################################################################################################### 41 # The functions below deals writing output to both the console and the log file. 42 ################################################################################################### 43 44 _LOGGING_ACTION_BEGLINE_CODES='\033[0G' 45 _LOGGING_ACTION_SUCCESS_CODES='\033[1;32m' 46 _LOGGING_ACTION_FAILURE_CODES='\033[1;31m' 47 _LOGGING_ACTION_SECTION_CODES='\033[1m' 48 _LOGGING_ACTION_NORMAL_CODES='\033[0;39m' 49 _LOGGING_ACTION_FORMAT='%-77s[%6s]' 50 51 # Determine if the action spinner should be enabled. 52 # The spinner will not be enabled when: 53 # - DISABLE_SPINNER is set, or 54 # - standard input is not a terminal, or 55 # - DEBUG is set to a value greater than 0 56 # Internal function 57 function _logging_action_spinner_enabled() { 58 if [ -t 0 ] && [ -z "${DISABLE_SPINNER:-}" ] && [ ${DEBUG:-0} -le 0 ]; then 59 return 0 60 else 61 return 1 62 fi 63 } 64 65 # Determine if the color control codes should be enabled. 66 # The control codes will not be enabled when any of the below are true: 67 # - DISABLE_SPINNER is set 68 # - standard input is not a terminal (i.e. ![ -t 0 ]) 69 # Internal function 70 function _logging_action_color_enabled() { 71 if [ -t 0 ] && [ -z "${DISABLE_SPINNER:-}" ]; then 72 return 0 73 else 74 return 1 75 fi 76 } 77 78 # Write action sart message to log file and display the action to the console. 79 # Internal function 80 function _logging_action_started() { 81 local msg="${_logging_action_msg:-}" 82 local status=" .... " 83 local endlin="\n" 84 # Remember the current shell pid to prevent sub scripts from killing spinner. 85 export _logging_action_pid=$$ 86 # Remember the logging action message for use in success/failure/cleanup functions. 87 export _logging_action_msg="${msg}" 88 89 printf "${_LOGGING_ACTION_FORMAT}\n" "${msg}" "${status}" 90 if _logging_action_spinner_enabled; then 91 status=' ' 92 endlin='' 93 fi 94 printf "${_LOGGING_ACTION_FORMAT}${endlin}" "${msg}" "${status}" >&${_LOGGING_CONSOLE_STDOUT} 95 96 if _logging_action_spinner_enabled; then 97 # Execute the spinner function is an asynchronous sub-shell. 98 _logging_action_spinner & 99 export _logging_action_spinner_pid=$! 100 # Disown spinner pid to prevent bash's job control from emitting kill message. 101 disown ${_logging_action_spinner_pid} 102 fi 103 } 104 105 # Write action success message to log file and update console action status with success. 106 # Internal function 107 function _logging_action_success() { 108 local msg="${_logging_action_msg:-}" 109 local status=' OK ' 110 local beglin='' 111 local format="${_LOGGING_ACTION_FORMAT}" 112 113 if _logging_action_spinner_enabled; then 114 beglin="${_LOGGING_ACTION_BEGLINE_CODES}" 115 fi 116 if _logging_action_color_enabled; then 117 format="${beglin}%-77s[${_LOGGING_ACTION_SUCCESS_CODES}%6s${_LOGGING_ACTION_NORMAL_CODES}]" 118 fi 119 120 printf "${_LOGGING_ACTION_FORMAT}\n" "${msg}" "${status}" 121 printf "${format}\n" "${msg}" "${status}" >&${_LOGGING_CONSOLE_STDOUT} 122 } 123 124 # Write action failure message to log file and update console action status with failure. 125 # Internal function 126 function _logging_action_failure() { 127 local msg="${_logging_action_msg:-}" 128 local status='FAILED' 129 local beglin='' 130 local format="${_LOGGING_ACTION_FORMAT}" 131 132 if _logging_action_spinner_enabled; then 133 beglin="${_LOGGING_ACTION_BEGLINE_CODES}" 134 fi 135 if _logging_action_color_enabled; then 136 format="${beglin}%-77s[${_LOGGING_ACTION_FAILURE_CODES}%6s${_LOGGING_ACTION_NORMAL_CODES}]" 137 fi 138 139 printf "${_LOGGING_ACTION_FORMAT}\n" "${msg}" "${status}" 140 printf "${format}\n" "${msg}" "${status}" >&${_LOGGING_CONSOLE_STDOUT} 141 } 142 143 # Update console action status for a completsed action. 144 # Internal function 145 function _logging_action_complete() { 146 # If this shell is the same one that started the action, then update the action status. 147 if [ "${_logging_action_pid:-}" = "$$" ]; then 148 if _logging_action_spinner_enabled; then 149 _logging_action_spinner_kill 150 fi 151 local rc="${_logging_action_rc:-}" 152 if [ "${rc}" = "0" ]; then 153 _logging_action_success 154 elif [ -n "${rc}" ]; then 155 _logging_action_failure 156 fi 157 export _logging_action_rc="" 158 export _logging_action_pid="" 159 fi 160 # If there is a failure message set output the message. 161 if [ -n "${_logging_failure_msg:-}" ]; then 162 printf "\n${_logging_failure_msg}\n" 163 printf "\n${_logging_failure_msg}\n" >&${_LOGGING_CONSOLE_STDERR} 164 export _logging_failure_msg="" 165 fi 166 } 167 168 # Loop until killed updating the console action status with a spinner. 169 # Will only be called if stdin is open on a terminal. 170 # Internal function 171 function _logging_action_spinner() { 172 local msg="${_logging_action_msg:-}" 173 local spinner_chars='\|/-' 174 local status='' 175 while : 176 do 177 for i in `seq 0 3` 178 do 179 status=" ${spinner_chars:$i:1} " 180 printf "${_LOGGING_ACTION_BEGLINE_CODES}${_LOGGING_ACTION_FORMAT}" "${msg}" "${status}" >&${_LOGGING_CONSOLE_STDOUT} 181 sleep .1 182 done 183 done 184 } 185 186 # Kill the spinner process if the pod it set. 187 # Internal function 188 function _logging_action_spinner_kill() { 189 local pid="${_logging_action_spinner_pid:-}" 190 if [ -n "${pid}" ]; then 191 if kill -0 ${pid} 2>/dev/null ; then 192 kill -9 ${pid} 2>/dev/null || true 193 wait ${pid} 2>/dev/null || true 194 export _logging_action_spinner_pid="" 195 fi 196 fi 197 } 198 199 # Execute a command with a status output. 200 # The return code of the command is used to populate the final status field. 201 # If a terminal is used, the status will be updated with a "spinner" while the command is executing. 202 # $1: The message to be written to the console's stdout and the log file. 203 # $@: The command or function to execute. Executed as `eval "$@"`. 204 # Returns the result of the executed `eval "$@"` command. 205 function action() { 206 local msg="$1" 207 shift 208 local cmd="$@" 209 local rc 210 211 export _logging_action_msg="${msg}" 212 _logging_action_started 213 export _logging_action_rc="?" 214 eval $cmd && rc=0 || rc=$? 215 export _logging_action_rc="${rc}" 216 _logging_action_complete 217 218 return $rc 219 } 220 221 # Log a section (i.e. bold) message to the console's standard output and the log file. 222 # This should not be invoked within an action command as this disrupts console output formatting. 223 # $@: The message to be written to the console's stdout and the log file. Written as "${@}". 224 # Returns 0 225 function section() { 226 local msg="$@" 227 echo "${msg}" 228 if [ -n "${_logging_action_rc:-}" ] && _logging_action_spinner_enabled; then 229 echo -en "\n" >&${_LOGGING_CONSOLE_STDOUT} 230 fi 231 if _logging_action_color_enabled; then 232 printf "${_LOGGING_ACTION_SECTION_CODES}%s${_LOGGING_ACTION_NORMAL_CODES}\n" "${msg}" >&${_LOGGING_CONSOLE_STDOUT} 233 else 234 printf "${msg}\n" >&${_LOGGING_CONSOLE_STDOUT} 235 fi 236 } 237 238 # Log a message to the console's standard output and the log file. 239 # This should not be invoked within an action command as this disrupts console output formatting. 240 # $@: The message to be written to the console's stdout and the log file. Written as "${@}". 241 # Returns 0 242 function status() { 243 local msg="$@" 244 echo "${msg}" 245 if [ -n "${_logging_action_rc:-}" ] && _logging_action_spinner_enabled; then 246 echo -e "\n${msg}" >&${_LOGGING_CONSOLE_STDOUT} 247 else 248 echo "${msg}" >&${_LOGGING_CONSOLE_STDOUT} 249 fi 250 } 251 252 # Log a message to the console's standard error and the log file. 253 # This should not be invoked within an action command as this disrupts console output formatting. 254 # The console's standard output and standard error are not synchronized. 255 # Messages written to standard error may appear out of order from those written to standard output. 256 # $@: The message to be written to the console's standard error and the log file. Written as "${@}". 257 # Returns 0 258 function error() { 259 local msg="$@" 260 echo "${msg}" 261 if [ -n "${_logging_action_rc:-}" ] && _logging_action_spinner_enabled; then 262 echo -e "\n${msg}" >&${_LOGGING_CONSOLE_STDERR} 263 else 264 echo "${msg}" >&${_LOGGING_CONSOLE_STDERR} 265 fi 266 } 267 268 # Log a message to the console's standard error and the log file and exit with a status code. 269 # May be invoke with no parameters. When invoked with single parameter that parameter must be a message. 270 # Default exit code is 1. 271 # $1: The message to be written to the console's standard output and the log file. Written as "$1". Defaults to "". 272 # $2: The exit code. Defaults to 1. 273 # Exits with the $2 exit code. 274 function fail() { 275 local msg="${1:-}" 276 local rc=${2:-1} 277 export _logging_failure_msg="${msg}" 278 exit ${rc} 279 } 280 281 ################################################################################################### 282 # The functions below deal with writing output to the log file. 283 ################################################################################################### 284 285 # Write a message to the log file if DEBUG >= 0. 286 # Also write a message to the console's stdout if DEBUG >=1. 287 # $@: The message to write. Written as "${@}". 288 # Returns 0 289 function log() { 290 local msg="$@" 291 if [ ${DEBUG:-0} -ge 0 ]; then 292 echo -e "$(_logging_timestamp) ${msg}" 293 fi 294 if [ ${DEBUG:-0} -ge 1 ]; then 295 echo -e "$(_logging_timestamp) ${msg}" >&${_LOGGING_CONSOLE_STDOUT} 296 fi 297 } 298 299 # Write a message to the log file and the console's stdout if DEBUG >= 2. 300 # $@: The message to write. Written as "${@}". 301 # Returns 0 302 function debug() { 303 local msg="$@" 304 if [ ${DEBUG:-0} -ge 2 ]; then 305 echo -e "${msg}" >&${_LOGGING_CONSOLE_STDOUT} 306 echo -e "${msg}" 307 fi 308 } 309 310 # Write a message to the log file and the console's stdout if DEBUG >= 3. 311 # $@: The message to write. Written as "${@}". 312 # Returns 0 313 function trace() { 314 local msg="$@" 315 if [ ${DEBUG:-0} -ge 3 ]; then 316 echo -e "${msg}" >&${_LOGGING_CONSOLE_STDOUT} 317 echo -e "${msg}" 318 fi 319 } 320 321 ################################################################################################### 322 # The functions below deal with trapping signals to cleanup and complete logging. 323 ################################################################################################### 324 325 # Generate a timestamp for logs. 326 # Internal function 327 function _logging_timestamp() { 328 date -u '+%Y-%m-%d %H:%M:%S %Z' 329 } 330 331 # Function used for interrupt (ie ^C) trap. 332 # Internal function 333 function _logging_interrupt_handler() { 334 local rc=$1 335 _logging_action_spinner_kill 336 # If this is a real interrupt and not a call from the exit handler then log a message. 337 if [ $rc -ge 128 ]; then 338 # Pause briefly to help ensure stdout and stderr don't overlap. 339 sleep 0 340 msg="An interrupt occurred. Exiting with code $rc." 341 if [ -n "${rc}" ] && _logging_action_spinner_enabled; then 342 echo "" >&${_LOGGING_CONSOLE_STDOUT} 343 fi 344 echo -e "\n$msg See ${LOG_FILE} for details." >&${_LOGGING_CONSOLE_STDERR} 345 echo -e "\n${msg}" 346 # Remove the EXIT trap to avoid double logging of the exit message. 347 trap - INT EXIT 348 exit $rc 349 fi 350 } 351 352 # Function used for EXIT (ie ^C) trap. 353 # Internal function 354 function _logging_exit_handler() { 355 local rc=$1 356 _logging_action_complete 357 # If the exit code indicates a script error then output a message. 358 # Return codes >= 128 usually indicate system exits (e.g. ^C) 359 if [ $rc -gt 0 ] && [ $rc -lt 128 ]; then 360 # Pause briefly to help ensure stdout and stderr don't overlap. 361 sleep 0 362 msg="A failure occurred. Exiting with code $rc." 363 echo "" >&${_LOGGING_CONSOLE_STDERR} 364 echo "${msg} See ${LOG_FILE} for details." >&${_LOGGING_CONSOLE_STDERR} 365 echo "" 366 echo "${msg}" 367 # Remove the EXIT trap to avoid double logging of the exit message. 368 trap - EXIT 369 fi 370 exit $rc 371 } 372 373 # Add handlers for interrupt and exit signals. 374 trap '_logging_interrupt_handler $?' INT 375 trap '_logging_exit_handler $?' EXIT