doc-public/bin/ndd-log4b-0.3.0/ndd-log4b-0.3.0.sh
ddidier/sphinx-doc d654ae6731 2024-02-11
2024-02-11 22:27:52 +00:00

714 lines
24 KiB
Bash

#!/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<stack_depth; i++)); do
local function_name="${FUNCNAME[$i]}"
local line_number="${BASH_LINENO[$((i - 1))]}"
local file_path="${BASH_SOURCE[$((i))]}"
ndd::logger::_print_error "┃ in ${function_name} (${file_path}:${line_number})"
done
ndd::logger::_print_error "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
return 1
}
# ------------------------------------------------------------------------------
# Print the given message to stderr, in red if 'ansi' if present.
# This is a private function.
#
# Arguments:
# @message - the message to print
#
# Outputs:
# Print the colorized (or not) message to stderr
#
function ndd::logger::_print_error() {
local message="${1}"
if command -v ansi::isAnsiSupported > /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)}'
}