#!/usr/bin/env bash # ============================================================================== # NDD Log4B -- A simple logger library for Bash. # # Copyright 2020 David DIDIER - All Rights Reserved # Released under the MIT license (https://opensource.org/licenses/MIT) # # Author - David DIDIER # Repository - https://gitlab.com/ddidier/bash-ndd-log4b # Version - 0.3.0 # ------------------------------------------------------------------------------ # # See https://gitlab.com/ddidier/bash-ndd-log4b for more details. # # ============================================================================== set -o pipefail # trace ERR through pipes set -o errtrace # trace ERR through 'time command' and other functions set -o nounset # set -u : exit the script if you try to use an uninitialised variable set -o errexit # set -e : exit the script if any statement returns a non-true return value # ------------------------------------------------------------------------------ # Avoid sourcing this library more than one time if [[ -n "${NDD_LOG4B_SOURCED+x}" ]] && [[ ${NDD_LOG4B_SOURCED} ]]; then return 0 fi NDD_LOG4B_SOURCED=true # =============================================================== settings ===== # ------------------------------------------------------------------------------ # The color palette # _NDD_LOG4B_COLOR_RED=255,0,0 _NDD_LOG4B_COLOR_ORANGE=255,127,0 _NDD_LOG4B_COLOR_YELLOW=255,255,0 _NDD_LOG4B_COLOR_GREEN=0,255,0 _NDD_LOG4B_COLOR_BLUE=0,128,255 # ------------------------------------------------------------------------------ # The ordered logger levels. declare -a NDD_LOG4B_LEVEL_NAMES # ------------------------------------------------------------------------------ # The logger levels display names. declare -A NDD_LOG4B_LEVEL_DISPLAY_NAMES # ------------------------------------------------------------------------------ # The logger levels synonyms. declare -A NDD_LOG4B_LEVEL_SYNONYMS # ------------------------------------------------------------------------------ # The logger levels priorities (note that DISABLED = -1). declare -A NDD_LOG4B_LEVEL_VALUES # ------------------------------------------------------------------------------ # The logger levels look and feels as 'ansi' arguments. declare -A NDD_LOG4B_LEVEL_ANSI_ARGS # ------------------------------------------------------------------------------ # The settings of the stdout logger. # The default level of the stdout appender NDD_LOG4B_STDOUT_DEFAULT_LEVEL="WARNING" # The default date format of the stdout appender ('date' command format) NDD_LOG4B_STDOUT_DEFAULT_DATE_FORMAT="+%F %T" # The message format of the stdout appender ('printf' command format) # The accepted arguments are: date-time (%s), level (%s), message (%s) NDD_LOG4B_STDOUT_DEFAULT_LOG_FORMAT="%s [%-5s] %s" # The level of the stdout appender. # This is a private variable. Use ndd::logger::set_stdout_level instead. _NDD_LOG4B_STDOUT_LEVEL="${NDD_LOG4B_STDOUT_DEFAULT_LEVEL}" # The date format of the stdout appender ('date' command format). # This is a private variable. Use ndd::logger::set_stdout_date_format instead. _NDD_LOG4B_STDOUT_DATE_FORMAT="${NDD_LOG4B_STDOUT_DEFAULT_DATE_FORMAT}" # The message format of the stdout appender ('printf' command format). # This is a private variable. Use ndd::logger::set_stdout_log_format instead. _NDD_LOG4B_STDOUT_LOG_FORMAT="${NDD_LOG4B_STDOUT_DEFAULT_LOG_FORMAT}" # ------------------------------------------------------------------------------ # The settings of the file logger. # The default level of the stdout appender NDD_LOG4B_FILE_DEFAULT_LEVEL="DISABLED" # The default date format of the stdout appender ('date' command format) NDD_LOG4B_FILE_DEFAULT_DATE_FORMAT="+%F %T" # The default message format of the file appender ('printf' command format) # The accepted arguments are: date-time (%s), level (%s), message (%s) NDD_LOG4B_FILE_DEFAULT_LOG_FORMAT="%s [%-5s] %s" # The level of the file appender. # This is a private variable. Use ndd::logger::set_file_level instead. _NDD_LOG4B_FILE_LEVEL="${NDD_LOG4B_FILE_DEFAULT_LEVEL}" # The date format of the file appender ('date' command format). # This is a private variable. Use ndd::logger::set_file_date_format instead. _NDD_LOG4B_FILE_DATE_FORMAT="${NDD_LOG4B_FILE_DEFAULT_DATE_FORMAT}" # The message format of the file appender ('printf' command format). # The accepted arguments are: date-time (%s), level (%s), message (%s). # This is a private variable. Use ndd::logger::set_file_log_format instead. _NDD_LOG4B_FILE_LOG_FORMAT="${NDD_LOG4B_FILE_DEFAULT_LOG_FORMAT}" # The file path of the file appender. # This is a private variable. Use ndd::logger::set_file_path instead. _NDD_LOG4B_FILE_PATH= # ------------------------------------------------------------------------------ # A profile supporting RFC 5424: # # 0 - Emergency - system is unusable # 1 - Alert - action must be taken immediately # 2 - Critical - critical conditions # 3 - Error - error conditions # 4 - Warning - warning conditions # 5 - Notice - normal but significant condition # 6 - Informational - informational messages # 7 - Debug - debug-level messages # function ndd::logger::profiles::use_rfc_5424() { ndd::logger::profiles::_reset_levels # The ordered logger levels. NDD_LOG4B_LEVEL_NAMES[0]=EMERGENCY NDD_LOG4B_LEVEL_NAMES[1]=ALERT NDD_LOG4B_LEVEL_NAMES[2]=CRITICAL NDD_LOG4B_LEVEL_NAMES[3]=ERROR NDD_LOG4B_LEVEL_NAMES[4]=WARNING NDD_LOG4B_LEVEL_NAMES[5]=NOTICE NDD_LOG4B_LEVEL_NAMES[6]=INFORMATION NDD_LOG4B_LEVEL_NAMES[7]=DEBUG # The logger levels display names. NDD_LOG4B_LEVEL_DISPLAY_NAMES["EMERGENCY"]="EMERG" NDD_LOG4B_LEVEL_DISPLAY_NAMES["ALERT"]="ALERT" NDD_LOG4B_LEVEL_DISPLAY_NAMES["CRITICAL"]="CRIT" NDD_LOG4B_LEVEL_DISPLAY_NAMES["ERROR"]="ERROR" NDD_LOG4B_LEVEL_DISPLAY_NAMES["WARNING"]="WARN" NDD_LOG4B_LEVEL_DISPLAY_NAMES["NOTICE"]="NOTIC" NDD_LOG4B_LEVEL_DISPLAY_NAMES["INFORMATION"]="INFO" NDD_LOG4B_LEVEL_DISPLAY_NAMES["DEBUG"]="DEBUG" # The logger levels synonyms. NDD_LOG4B_LEVEL_SYNONYMS["EMERG"]="EMERGENCY" NDD_LOG4B_LEVEL_SYNONYMS["CRIT"]="CRITICAL" NDD_LOG4B_LEVEL_SYNONYMS["WARN"]="WARNING" NDD_LOG4B_LEVEL_SYNONYMS["INFO"]="INFORMATION" # The logger levels priorities (note that DISABLED = -1). NDD_LOG4B_LEVEL_VALUES["DISABLED"]=-1 NDD_LOG4B_LEVEL_VALUES["EMERGENCY"]=0 NDD_LOG4B_LEVEL_VALUES["ALERT"]=1 NDD_LOG4B_LEVEL_VALUES["CRITICAL"]=2 NDD_LOG4B_LEVEL_VALUES["ERROR"]=3 NDD_LOG4B_LEVEL_VALUES["WARNING"]=4 NDD_LOG4B_LEVEL_VALUES["NOTICE"]=5 NDD_LOG4B_LEVEL_VALUES["INFORMATION"]=6 NDD_LOG4B_LEVEL_VALUES["DEBUG"]=7 # The logger levels look and feels as ansi arguments. NDD_LOG4B_LEVEL_ANSI_ARGS["EMERGENCY"]=" --black --bg-rgb=${_NDD_LOG4B_COLOR_RED} --bold" NDD_LOG4B_LEVEL_ANSI_ARGS["ALERT"]=" --black --bg-rgb=${_NDD_LOG4B_COLOR_ORANGE} --bold" NDD_LOG4B_LEVEL_ANSI_ARGS["CRITICAL"]=" --black --bg-rgb=${_NDD_LOG4B_COLOR_YELLOW} --bold" NDD_LOG4B_LEVEL_ANSI_ARGS["ERROR"]=" --rgb=${_NDD_LOG4B_COLOR_RED}" NDD_LOG4B_LEVEL_ANSI_ARGS["WARNING"]=" --rgb=${_NDD_LOG4B_COLOR_ORANGE}" NDD_LOG4B_LEVEL_ANSI_ARGS["NOTICE"]=" --rgb=${_NDD_LOG4B_COLOR_YELLOW}" NDD_LOG4B_LEVEL_ANSI_ARGS["INFORMATION"]="--rgb=${_NDD_LOG4B_COLOR_GREEN}" NDD_LOG4B_LEVEL_ANSI_ARGS["DEBUG"]=" --rgb=${_NDD_LOG4B_COLOR_BLUE}" } # ------------------------------------------------------------------------------ # A profile supporting Log4J levels: # # - Fatal # - Error # - Warning # - Information # - Debug # - Trace # function ndd::logger::profiles::use_log4j() { ndd::logger::profiles::_reset_levels # The ordered logger levels. NDD_LOG4B_LEVEL_NAMES[0]=FATAL NDD_LOG4B_LEVEL_NAMES[1]=ERROR NDD_LOG4B_LEVEL_NAMES[2]=WARNING NDD_LOG4B_LEVEL_NAMES[3]=INFORMATION NDD_LOG4B_LEVEL_NAMES[4]=DEBUG NDD_LOG4B_LEVEL_NAMES[5]=TRACE # The logger levels display names. NDD_LOG4B_LEVEL_DISPLAY_NAMES["FATAL"]="FATAL" NDD_LOG4B_LEVEL_DISPLAY_NAMES["ERROR"]="ERROR" NDD_LOG4B_LEVEL_DISPLAY_NAMES["WARNING"]="WARN" NDD_LOG4B_LEVEL_DISPLAY_NAMES["INFORMATION"]="INFO" NDD_LOG4B_LEVEL_DISPLAY_NAMES["DEBUG"]="DEBUG" NDD_LOG4B_LEVEL_DISPLAY_NAMES["TRACE"]="TRACE" # The logger levels synonyms. NDD_LOG4B_LEVEL_SYNONYMS["WARN"]="WARNING" NDD_LOG4B_LEVEL_SYNONYMS["INFO"]="INFORMATION" # The logger levels priorities (note that DISABLED = -1). NDD_LOG4B_LEVEL_VALUES["DISABLED"]=-1 NDD_LOG4B_LEVEL_VALUES["FATAL"]=0 NDD_LOG4B_LEVEL_VALUES["ERROR"]=1 NDD_LOG4B_LEVEL_VALUES["WARNING"]=2 NDD_LOG4B_LEVEL_VALUES["INFORMATION"]=3 NDD_LOG4B_LEVEL_VALUES["DEBUG"]=4 NDD_LOG4B_LEVEL_VALUES["TRACE"]=5 # The logger levels look and feels as ansi arguments. NDD_LOG4B_LEVEL_ANSI_ARGS["FATAL"]=" --black --bold --bg-red" NDD_LOG4B_LEVEL_ANSI_ARGS["ERROR"]=" --red --bold" NDD_LOG4B_LEVEL_ANSI_ARGS["WARNING"]=" --yellow --bold" NDD_LOG4B_LEVEL_ANSI_ARGS["INFORMATION"]="--white-intense --bold" NDD_LOG4B_LEVEL_ANSI_ARGS["DEBUG"]="" NDD_LOG4B_LEVEL_ANSI_ARGS["TRACE"]="" # The old look and feel with too many colors: # NDD_LOG4B_LEVEL_ANSI_ARGS["FATAL"]=" --black --bg-rgb=${_NDD_LOG4B_COLOR_RED} --bold" # NDD_LOG4B_LEVEL_ANSI_ARGS["ERROR"]=" --rgb=${_NDD_LOG4B_COLOR_RED}" # NDD_LOG4B_LEVEL_ANSI_ARGS["WARNING"]=" --rgb=${_NDD_LOG4B_COLOR_ORANGE}" # NDD_LOG4B_LEVEL_ANSI_ARGS["INFORMATION"]="--rgb=${_NDD_LOG4B_COLOR_YELLOW}" # NDD_LOG4B_LEVEL_ANSI_ARGS["DEBUG"]=" --rgb=${_NDD_LOG4B_COLOR_GREEN}" # NDD_LOG4B_LEVEL_ANSI_ARGS["TRACE"]=" --rgb=${_NDD_LOG4B_COLOR_BLUE}" } # ------------------------------------------------------------------------------ # A profile enabling JSON output on stdout. # # Globals: # _NDD_LOG4B_STDOUT_DATE_FORMAT # _NDD_LOG4B_STDOUT_LOG_FORMAT # function ndd::logger::profile::use_json_for_stdout() { _NDD_LOG4B_STDOUT_DATE_FORMAT="+%s" _NDD_LOG4B_STDOUT_LOG_FORMAT='{"timestamp":"%s","level":"%s","message":"%s"}' } # ------------------------------------------------------------------------------ # A profile enabling JSON output to the file. # # Globals: # _NDD_LOG4B_FILE_DATE_FORMAT # _NDD_LOG4B_FILE_LOG_FORMAT # function ndd::logger::profile::use_json_for_file() { _NDD_LOG4B_FILE_DATE_FORMAT="+%s" _NDD_LOG4B_FILE_LOG_FORMAT='{"timestamp":"%s","level":"%s","message":"%s"}' } # ------------------------------------------------------------------------------ # Reset the current profile. # Must be called before redefining the levels. # function ndd::logger::profiles::_reset_levels() { for i in "${!NDD_LOG4B_LEVEL_NAMES[@]}"; do unset NDD_LOG4B_LEVEL_NAMES["$i"] done for i in "${!NDD_LOG4B_LEVEL_DISPLAY_NAMES[@]}"; do unset NDD_LOG4B_LEVEL_DISPLAY_NAMES["$i"] done for i in "${!NDD_LOG4B_LEVEL_SYNONYMS[@]}"; do unset NDD_LOG4B_LEVEL_SYNONYMS["$i"] done for i in "${!NDD_LOG4B_LEVEL_VALUES[@]}"; do unset NDD_LOG4B_LEVEL_VALUES["$i"] done for i in "${!NDD_LOG4B_LEVEL_ANSI_ARGS[@]}"; do unset NDD_LOG4B_LEVEL_ANSI_ARGS["$i"] done } # ------------------------------------------------------------------------------ # This is the default profile ndd::logger::profiles::use_log4j # ======================================================= public functions ===== # ------------------------------------------------------------------------------ # Log the given message at the given level if active. # See ndd::logger::log() # function log() { ndd::logger::log "${@}" } # ------------------------------------------------------------------------------ # Log the given message at the given level if active. # # Globals: # NDD_LOG4B_LEVEL_VALUES # # Arguments: # @level_name - the logger level to use # @varargs... - the format and arguments in the 'printf' style # # Example: # log error "This is a %-11s log message" "ERROR" # function ndd::logger::log() { local level_name="${1}" level_name="$(ndd::logger::_normalize_level "${level_name}")" level_value=${NDD_LOG4B_LEVEL_VALUES[$level_name]} ndd::logger::_log_to_stdout "${level_name}" ${level_value} "${@:2}" ndd::logger::_log_to_file "${level_name}" ${level_value} "${@:2}" } # ------------------------------------------------------------------------------ # Conveniently print all the logging levels with their look and feel. # # Globals: # NDD_LOG4B_LEVEL_NAMES # NDD_LOG4B_LEVEL_VALUES # # Arguments: # None # # Outputs: # Print all the logging levels with their look and feel to stdout # function ndd::logger::print_levels() { if ! command -v ansi::isAnsiSupported > /dev/null; then echo "Source the 'ansi' library (https://github.com/fidian/ansi/) to add colors!" fi for level_name in "${NDD_LOG4B_LEVEL_NAMES[@]}"; do local level_value="${NDD_LOG4B_LEVEL_VALUES[$level_name]}" local message message="$(ndd::logger::_colorize_for_level "${level_name}" " Some text as example ")" printf "%s %-11s %s\n" "${level_value}" "${level_name}" "${message}" done } # ------------------------------------------------------------------------------ # Set the date format of the file appender. # # Globals: # _NDD_LOG4B_FILE_DATE_FORMAT # # Arguments: # @date_format - the date format to set # function ndd::logger::set_file_date_format() { local date_format="${1}" _NDD_LOG4B_FILE_DATE_FORMAT="${date_format}" } # ------------------------------------------------------------------------------ # Set the level of the file appender. The level name is normalized. # # Globals: # _NDD_LOG4B_FILE_LEVEL # # Arguments: # @level_name - the logger level to set # function ndd::logger::set_file_level() { local level_name="${1}" _NDD_LOG4B_FILE_LEVEL="$(ndd::logger::_normalize_level "${level_name}")" } # ------------------------------------------------------------------------------ # Set the level of the file appender. # # Globals: # _NDD_LOG4B_FILE_LOG_FORMAT # # Arguments: # @log_format - the log format to set # function ndd::logger::set_file_log_format() { local log_format="${1}" _NDD_LOG4B_FILE_LOG_FORMAT="${log_format}" } # ------------------------------------------------------------------------------ # Set the path of the file appender. # # Globals: # _NDD_LOG4B_FILE_PATH # # Arguments: # @log_format - the log format to set # function ndd::logger::set_file_path() { local file_path="${1}" _NDD_LOG4B_FILE_PATH="${file_path}" } # ------------------------------------------------------------------------------ # Set the date format of the stdout appender. # # Globals: # _NDD_LOG4B_STDOUT_DATE_FORMAT # # Arguments: # @date_format - the date format to set # function ndd::logger::set_stdout_date_format() { local date_format="${1}" _NDD_LOG4B_STDOUT_DATE_FORMAT="${date_format}" } # ------------------------------------------------------------------------------ # Set the level of the stdout appender. The level name is normalized. # # Globals: # _NDD_LOG4B_STDOUT_LEVEL # # Arguments: # @level_name - the logger level to set # function ndd::logger::set_stdout_level() { local level_name="${1}" _NDD_LOG4B_STDOUT_LEVEL="$(ndd::logger::_normalize_level "${level_name}")" } # ------------------------------------------------------------------------------ # Set the level of the stdout appender. The level name is normalized. # # Globals: # _NDD_LOG4B_STDOUT_LOG_FORMAT # # Arguments: # @log_format - the log format to set # function ndd::logger::set_stdout_log_format() { local log_format="${1}" _NDD_LOG4B_STDOUT_LOG_FORMAT="${log_format}" } # ====================================================== private functions ===== # ------------------------------------------------------------------------------ # Colorize the given message according to the given level configuration. # This is a private function. # # Globals: # NDD_LOG4B_LEVEL_ANSI_ARGS # # Arguments: # @level_name - the logger level to use (see NDD_LOG4B_LEVEL_NAMES) # @message - the message to colorize # # Outputs: # Print the colorized (or not) message to stdout # function ndd::logger::_colorize_for_level() { local level_name="${1}" local message="${2}" local ansi_args="${NDD_LOG4B_LEVEL_ANSI_ARGS[$level_name]}" ndd::logger::_colorize_with_ansi "${ansi_args}" "${message}" } # ------------------------------------------------------------------------------ # Colorize the given message according to the given 'ansi' arguments if 'ansi" # is present and supported. # This is a private function. # # Arguments: # @ansi_args - the ansi arguments to apply # @message - the message to colorize # # Outputs: # Print the colorized (or not) message to stdout # function ndd::logger::_colorize_with_ansi() { local ansi_args="${1}" local message="${2}" # check if 'ansi' is present and if this is a TTY if command -v ansi::isAnsiSupported > /dev/null && ansi::isAnsiSupported; then # shellcheck disable=2086 ansi ${ansi_args} "${message}" else echo "${message}" fi } # ------------------------------------------------------------------------------ # Log the given message to a text file at the given level if active. # This is a private function. # # Globals: # NDD_LOG4B_LEVEL_VALUES # _NDD_LOG4B_FILE_DATE_FORMAT # _NDD_LOG4B_FILE_LEVEL # _NDD_LOG4B_FILE_PATH # # Arguments: # @level_name - the logger level to use # @level_value - the logger level to use # @varargs... - the format and arguments in the 'printf' style # # Example: # log error "This is a %-11s log message" "ERROR" # function ndd::logger::_log_to_file() { local level_name="${1}" local level_value="${2}" local maximum_level_value=${NDD_LOG4B_LEVEL_VALUES[$_NDD_LOG4B_FILE_LEVEL]} if [[ "${level_value}" -le ${maximum_level_value} ]]; then if [[ -z "${_NDD_LOG4B_FILE_PATH}" ]]; then ndd::logger::_print_error "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" ndd::logger::_print_error "┃ The output text file is undefined (please set 'NDD_LOG4B_FILE_PATH')" ndd::logger::_print_error "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" return 1 fi local log_date local log_level_name local log_message local log_line log_date="$(date "${_NDD_LOG4B_FILE_DATE_FORMAT}")" log_level_name="${NDD_LOG4B_LEVEL_DISPLAY_NAMES[$level_name]}" # printf chokes on special characters like '-' which is rather annoying # so we explicitly make the distinction between 'printf' and 'echo' using the number of arguments if [[ "${#}" -ge 4 ]]; then # shellcheck disable=2059 log_message="$(printf "${@:3}")" else log_message="${*:3}" fi # shellcheck disable=2059 log_line="$(printf "${_NDD_LOG4B_FILE_LOG_FORMAT}" "${log_date}" "${log_level_name}" "${log_message}")" echo -e "${log_line}" >> "${_NDD_LOG4B_FILE_PATH}" fi } # ------------------------------------------------------------------------------ # Log the given message to stdout at the given level if active. # This is a private function. # # Globals: # NDD_LOG4B_LEVEL_VALUES # _NDD_LOG4B_STDOUT_DATE_FORMAT # _NDD_LOG4B_STDOUT_LEVEL # # Arguments: # @level_name - the logger level to use # @level_value - the logger level to use # @varargs... - the format and arguments in the 'printf' style # # Example: # log error "This is a %-11s log message" "ERROR" # function ndd::logger::_log_to_stdout() { local level_name="${1}" local level_value="${2}" local maximum_level_value=${NDD_LOG4B_LEVEL_VALUES[$_NDD_LOG4B_STDOUT_LEVEL]} if [[ "${level_value}" -le ${maximum_level_value} ]]; then local log_date local log_level_name local log_message local log_line log_date="$(date "${_NDD_LOG4B_STDOUT_DATE_FORMAT}")" log_level_name="${NDD_LOG4B_LEVEL_DISPLAY_NAMES[$level_name]}" # printf chokes on special characters like '-' which is rather annoying # so we explicitly make the distinction between 'printf' and 'echo' using the number of arguments if [[ "${#}" -ge 4 ]]; then # shellcheck disable=2059 log_message="$(printf "${@:3}")" else log_message="${*:3}" fi # shellcheck disable=2059 log_line="$(printf "${_NDD_LOG4B_STDOUT_LOG_FORMAT}" "${log_date}" "${log_level_name}" "${log_message}")" ndd::logger::_colorize_for_level "${level_name}" "${log_line}" fi } # ------------------------------------------------------------------------------ # Normalize the given logger level name, taking the synonyms into account. # Print an error message and exit with 1 if the log level cannot be normalized. # This is a private function. # # Globals: # NDD_LOG4B_LEVEL_SYNONYMS # NDD_LOG4B_LEVEL_NAMES # # Arguments: # @level_name - the logger level name to use (see NDD_LOG4B_LEVEL_NAMES) # # Outputs: # Print the normalized logger level to stdout # function ndd::logger::_normalize_level() { local level_name="${1}" level_name="$(ndd::logger::_to_upper "${level_name}")" # search for a level synonym for level_synonym in "${!NDD_LOG4B_LEVEL_SYNONYMS[@]}"; do if [[ "${level_name}" == "${level_synonym}" ]]; then level_name="${NDD_LOG4B_LEVEL_SYNONYMS[$level_synonym]}" fi done # print DISABLED and return if disabled (because DISABLED is not in NDD_LOG4B_LEVEL_NAMES) if [[ "${level_name}" == "DISABLED" ]]; then echo "${level_name}" return 0 fi # print the logger level name and return if valid for valid_level_name in "${NDD_LOG4B_LEVEL_NAMES[@]}"; do if [[ "${level_name}" == "${valid_level_name}" ]]; then echo "${level_name}" return 0 fi done # print an error message then exit with 1 ndd::logger::_print_error "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" ndd::logger::_print_error "┃ Invalid log level: ${level_name}" ndd::logger::_print_error "┃ Stacktrace:" local first_trace=0 local stack_depth=${#FUNCNAME[@]} for ((i=first_trace; i /dev/null && ansi::isAnsiSupported; then # shellcheck disable=2086 echo -e "$(ansi --bold --red "${message}")" >&2 else echo -e "${message}" >&2 fi } # ------------------------------------------------------------------------------ # Convert the given string to uppercase. # This is a private function. # # Arguments: # @string - the string to upper case # # Outputs: # Print the uppercase string to stdout # function ndd::logger::_to_upper() { local string="${1}" echo "${string}" | awk '{print toupper($0)}' }