| #!/usr/bin/env bash |
| |
| # Copyright 2014 The Kubernetes Authors. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| # Controls verbosity of the script output and logging. |
| KUBE_VERBOSE="${KUBE_VERBOSE:-2}" |
| |
| # Handler for when we exit automatically on an error. |
| # Borrowed from https://gist.github.com/ahendrix/7030300 |
| kube::log::errexit() { |
| local err="${PIPESTATUS[*]}" |
| |
| # If the shell we are in doesn't have errexit set (common in subshells) then |
| # don't dump stacks. |
| set +o | grep -qe "-o errexit" || return |
| |
| set +o xtrace |
| local code="${1:-1}" |
| # Print out the stack trace described by $function_stack |
| if [ ${#FUNCNAME[@]} -gt 2 ] |
| then |
| kube::log::error "Call tree:" |
| for ((i=1;i<${#FUNCNAME[@]}-1;i++)) |
| do |
| kube::log::error " ${i}: ${BASH_SOURCE[${i}+1]}:${BASH_LINENO[${i}]} ${FUNCNAME[${i}]}(...)" |
| done |
| fi |
| kube::log::error_exit "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}' exited with status ${err}" "${1:-1}" 1 |
| } |
| |
| kube::log::install_errexit() { |
| # trap ERR to provide an error handler whenever a command exits nonzero this |
| # is a more verbose version of set -o errexit |
| trap 'kube::log::errexit' ERR |
| |
| # setting errtrace allows our ERR trap handler to be propagated to functions, |
| # expansions and subshells |
| set -o errtrace |
| } |
| |
| # Print out the stack trace |
| # |
| # Args: |
| # $1 The number of stack frames to skip when printing. |
| kube::log::stack() { |
| local stack_skip=${1:-0} |
| stack_skip=$((stack_skip + 1)) |
| if [[ ${#FUNCNAME[@]} -gt ${stack_skip} ]]; then |
| echo "Call stack:" >&2 |
| local i |
| for ((i=1 ; i <= ${#FUNCNAME[@]} - stack_skip ; i++)) |
| do |
| local frame_no=$((i - 1 + stack_skip)) |
| local source_file=${BASH_SOURCE[${frame_no}]} |
| local source_lineno=${BASH_LINENO[$((frame_no - 1))]} |
| local funcname=${FUNCNAME[${frame_no}]} |
| echo " ${i}: ${source_file}:${source_lineno} ${funcname}(...)" >&2 |
| done |
| fi |
| } |
| |
| # Log an error and exit. |
| # Args: |
| # $1 Message to log with the error |
| # $2 The error code to return |
| # $3 The number of stack frames to skip when printing. |
| kube::log::error_exit() { |
| local message="${1:-}" |
| local code="${2:-1}" |
| local stack_skip="${3:-0}" |
| stack_skip=$((stack_skip + 1)) |
| |
| if [[ ${KUBE_VERBOSE} -ge 4 ]]; then |
| local source_file=${BASH_SOURCE[${stack_skip}]} |
| local source_line=${BASH_LINENO[$((stack_skip - 1))]} |
| echo "!!! Error in ${source_file}:${source_line}" >&2 |
| [[ -z ${1-} ]] || { |
| echo " ${1}" >&2 |
| } |
| |
| kube::log::stack ${stack_skip} |
| |
| echo "Exiting with status ${code}" >&2 |
| fi |
| |
| exit "${code}" |
| } |
| |
| # Log an error but keep going. Don't dump the stack or exit. |
| kube::log::error() { |
| timestamp=$(date +"[%m%d %H:%M:%S]") |
| echo "!!! ${timestamp} ${1-}" >&2 |
| shift |
| for message; do |
| echo " ${message}" >&2 |
| done |
| } |
| |
| # Print an usage message to stderr. The arguments are printed directly. |
| kube::log::usage() { |
| echo >&2 |
| local message |
| for message; do |
| echo "${message}" >&2 |
| done |
| echo >&2 |
| } |
| |
| kube::log::usage_from_stdin() { |
| local messages=() |
| while read -r line; do |
| messages+=("${line}") |
| done |
| |
| kube::log::usage "${messages[@]}" |
| } |
| |
| # Print out some info that isn't a top level status line |
| kube::log::info() { |
| local V="${V:-0}" |
| if (( KUBE_VERBOSE < V )); then |
| return |
| fi |
| |
| for message; do |
| echo "${message}" |
| done |
| } |
| |
| # Just like kube::log::info, but no \n, so you can make a progress bar |
| kube::log::progress() { |
| for message; do |
| echo -e -n "${message}" |
| done |
| } |
| |
| kube::log::info_from_stdin() { |
| local messages=() |
| while read -r line; do |
| messages+=("${line}") |
| done |
| |
| kube::log::info "${messages[@]}" |
| } |
| |
| # Print a status line. Formatted to show up in a stream of output. |
| kube::log::status() { |
| local V="${V:-0}" |
| if (( KUBE_VERBOSE < V )); then |
| return |
| fi |
| |
| timestamp=$(date +"[%m%d %H:%M:%S]") |
| echo "+++ ${timestamp} ${1}" |
| shift |
| for message; do |
| echo " ${message}" |
| done |
| } |