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