Add a logging interface

This commit is contained in:
Nick Chambers 2021-07-04 06:25:08 -05:00 committed by Nick Chambers
parent d2965c9233
commit dece4203b4
1 changed files with 186 additions and 120 deletions

306
rowbot
View File

@ -6,111 +6,6 @@
shopt -s dotglob extglob lastpipe nullglob
###
# configure rowbot
###
# default config
declare -A config=(
# connection settings
[server]=irc.libera.chat [port]="" [tls]=no [client-cert]=""
# irc registration settings
[nick]=rowbot-dev [ident]=rowbot [realname]=rowbot [chan]=""
# bot control settings
[owner]="${USER:-uplime}" [trigger]=\` [dev]=yes
# log settings
[log]="" [overwrite]=no [log-level]=info
)
# parse command line arguments
declare -A opts
# This code is not used yet, but will be later.
# shellcheck disable=SC2034
cmd_line=( "${@:0}" )
while (( $# )); do
case $1 in
--*=*)
key=${1#--} key=${key%%=*}
opts[$key]=${1#--*=}
;;
--no-*)
key=${1#--no-}
opts[$key]=no
;;
--)
shift
break
;;
--*)
key=${1#--}
opts[$key]=yes
;;
*)
break
esac
shift
done
# apply custom configuration files
for file do
if [[ -f $file ]]; then
# These files (if any) are provided dynamically at run-time.
# shellcheck disable=SC1090
. "$file" # ha, dot file
else
die "could not locate config file %s" "$file"
fi
done
for setting in "${!config[@]}"; do
if [[ -v ${setting//-/_} ]]; then
declare -n setting_var=${setting//-/_}
config[$setting]=$setting_var
fi
done
# apply command line arguments
for setting in "${!config[@]}"; do
# This is a false positive.
# shellcheck disable=SC2102
if [[ -v opts[$setting] ]]; then
config[$setting]=${opts[$setting]}
fi
done
# apply default port if one isn't provided
if [[ -z ${config[port]} ]]; then
if [[ ${config[tls]} = yes ]]; then
config[port]=6697
else
config[port]=6667
fi
fi
# apply any reloaded values
for setting in "${!config[@]}"; do
env_name=${setting//-/_} env_name=${env_name^^}
declare -n env_var=$env_name
if [[ -v $env_name ]]; then
# This variable is only used for assignment.
# shellcheck disable=SC2034
config[$setting]=$env_var
fi
done
# cleanup
unset key file setting setting_var env_name env_var
###
# utility and helper functions
###
@ -126,7 +21,12 @@ any_file() {
}
die() {
# error "$@"
local fmt=$1
shift
# This is a wrapper around printf so the format string isn't known ahead of
# time.
# shellcheck disable=SC2059
printf "$fmt\n" "$@" >&2
exit "${STATUS:-42}"
}
@ -135,15 +35,17 @@ get_option() {
return 1
fi
local option_name=${1//-/_}
declare -n option_var=$option_name
local var_name=${1//-/_}
local env_var=${var_name^^}
if [[ -v opts[$1] ]]; then
option_var=${opts[log-level]}
elif [[ ! -v $option_name ]]; then
# This variable is only used for assignment.
# shellcheck disable=SC2034
option_var=$2
if [[ -v $env_var ]]; then
config[$1]=${!env_var}
elif [[ -v opts[$1] ]]; then
config[$1]=${opts[$1]}
elif [[ -v $var_name ]]; then
config[$1]=${!var_name}
else
config[$1]=$2
fi
}
@ -183,25 +85,187 @@ run_callbacks() {
return 0
}
###
# configure rowbot's environment
###
# parse command line arguments
declare -A opts
cmd_line=( "${@:0}" )
while (( $# )); do
case $1 in
--*=*)
key=${1#--} key=${key%%=*}
opts[$key]=${1#--*=}
;;
--no-*)
key=${1#--no-}
opts[$key]=no
;;
--)
shift
break
;;
--*)
key=${1#--}
opts[$key]=yes
;;
*)
break
esac
shift
done
# load custom configuration files
for file do
if [[ -f $file ]]; then
# These files (if any) are provided dynamically at run-time.
# shellcheck disable=SC1090
. "$file" # ha, dot file
else
die "could not locate config file %s" "$file"
fi
done
# cleanup
unset key file
###
# load default config
###
declare -A config
# connection settings
get_option server irc.libera.chat
get_option tls no
if [[ ${config[tls]} = no ]]; then
get_option port 6667
else
get_option client-cert ""
get_option port 6697
fi
# irc registration settings
get_option nick rowbot-dev
get_option ident rowbot
get_option realname rowbot
get_option chan ""
# bot control settings
get_option owner "${USER:-uplime}"
get_option trigger \`
get_option dev yes
###
# logger
###
log() {
if [[ -v LOG_LEVEL ]] && (( log_levels[$log_level] <= log_levels[$LOG_LEVEL] )); then
printf "%s: $1\n" "${LOG_LEVEL^^}" "${@:2}" >&"$log_fd"
fi
}
log_debug() {
LOG_LEVEL=debug log "$@"
}
log_info() {
LOG_LEVEL=info log "$@"
}
log_warn() {
LOG_LEVEL=warn log "$@"
}
log_error() {
LOG_LEVEL=error log "$@"
}
log_has_level() {
local level
for level in "${!log_levels[@]}"; do
if [[ ${1,,} = "$level" ]]; then
return 0
fi
done
return 1
}
on_init_001_log() {
declare -gA log_levels=( [debug]=1 [info]=2 [warn]=3 [error]=4 )
get_option log-level info
if ! log_has_level "${config[log-level]}"; then
die "%s is not a valid logging level" "${config[log-level]}"
fi
get_option log ""
get_option overwrite no
log_level=${config[log-level]}
if [[ ${config[log]} ]]; then
if [[ ${config[overwrite]} = yes ]]; then
exec {log_fd}>"${config[log]}"
else
exec {log_fd}>>"${config[log]}"
fi
else
log_fd=1
fi
}
on_exit_log() {
if [[ -v log_fd ]] && (( log_fd != 1 )); then
exec {log_fd}>&-
fi
}
###
# cleanup
###
cleanup() {
run_callbacks on_exit_
}
trap cleanup EXIT
###
# live code reloader
###
reload() {
run_callbacks on_before_reload_
local setting setting_name
run_callbacks on_before_
for setting in "${!config[@]}"; do
setting_name=${setting//-/_}
export "${setting_name^^}"="${config[$setting]}"
done
exec "${cmd_line[@]}"
}
hup_reload() {
log_info "received reload signal (HUP)"
reload
}
trap hup_reload HUP
on_init_debug() {
declare -p config
}
###
# initialization sequence
###
@ -209,7 +273,9 @@ on_init_debug() {
run_callbacks on_init_
if is_reloaded; then
run_callbacks on_after_reload_
run_callbacks on_after_
else
run_callbacks on_first_
fi
declare -p config