Mejorando un poco más la idea de @Fred, podríamos construir una pequeña biblioteca de registro de esta manera:
declare -A _log_levels=([FATAL]=0 [ERROR]=1 [WARN]=2 [INFO]=3 [DEBUG]=4 [VERBOSE]=5)
declare -i _log_level=3
set_log_level() {
level="${1:-INFO}"
_log_level="${_log_levels[$level]}"
}
log_execute() {
level=${1:-INFO}
if (( $1 >= ${_log_levels[$level]} )); then
"${@:2}" >/dev/null
else
"${@:2}"
fi
}
log_fatal() { (( _log_level >= ${_log_levels[FATAL]} )) && echo "$(date) FATAL $*"; }
log_error() { (( _log_level >= ${_log_levels[ERROR]} )) && echo "$(date) ERROR $*"; }
log_warning() { (( _log_level >= ${_log_levels[WARNING]} )) && echo "$(date) WARNING $*"; }
log_info() { (( _log_level >= ${_log_levels[INFO]} )) && echo "$(date) INFO $*"; }
log_debug() { (( _log_level >= ${_log_levels[DEBUG]} )) && echo "$(date) DEBUG $*"; }
log_verbose() { (( _log_level >= ${_log_levels[VERBOSE]} )) && echo "$(date) VERBOSE $*"; }
# functions for logging command output
log_debug_file() { (( _log_level >= ${_log_levels[DEBUG]} )) && [[ -f $1 ]] && echo "=== command output start ===" && cat "$1" && echo "=== command output end ==="; }
log_verbose_file() { (( _log_level >= ${_log_levels[VERBOSE]} )) && [[ -f $1 ]] && echo "=== command output start ===" && cat "$1" && echo "=== command output end ==="; }
Digamos que la fuente anterior está en un archivo de biblioteca llamado logging_lib.sh, podríamos usarlo en un script de shell regular de esta manera:
#!/bin/bash
source /path/to/lib/logging_lib.sh
set_log_level DEBUG
log_info "Starting the script..."
# method 1 of controlling a command's output based on log level
log_execute INFO date
# method 2 of controlling the output based on log level
date &> date.out
log_debug_file date.out
log_debug "This is a debug statement"
...
log_error "This is an error"
...
log_warning "This is a warning"
...
log_fatal "This is a fatal error"
...
log_verbose "This is a verbose log!"
Dará como resultado esta salida:
Fri Feb 24 06:48:18 UTC 2017 INFO Starting the script...
Fri Feb 24 06:48:18 UTC 2017
=== command output start ===
Fri Feb 24 06:48:18 UTC 2017
=== command output end ===
Fri Feb 24 06:48:18 UTC 2017 DEBUG This is a debug statement
Fri Feb 24 06:48:18 UTC 2017 ERROR This is an error
Fri Feb 24 06:48:18 UTC 2017 ERROR This is a warning
Fri Feb 24 06:48:18 UTC 2017 FATAL This is a fatal error
Como podemos ver, log_verbose
no produjo ningún resultado ya que el nivel de registro está en DEBUG, un nivel por debajo de VERBOSE. Sin embargo, log_debug_file date.out
produjo la salida y también lo hizo log_execute INFO
, ya que el nivel de registro está establecido en DEBUG, que es>=INFO.
Usando esto como base, también podríamos escribir envoltorios de comandos si necesitamos un ajuste aún más fino:
git_wrapper() {
# run git command and print the output based on log level
}
Con estos en su lugar, la secuencia de comandos podría mejorarse para tomar un argumento --log-level level
que puede determinar el nivel de detalle del registro con el que debe ejecutarse.
Aquí hay una implementación completa de registro para Bash, rica en múltiples registradores:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
Si alguien tiene curiosidad acerca de por qué algunas variables se nombran con un guión bajo en el código anterior, consulte esta publicación:
- Corregir mayúsculas y minúsculas variables de Bash y script de shell
Ya tiene lo que parece ser la idea más limpia en su pregunta (una función de contenedor), pero parece pensar que sería complicado. Te sugiero que lo reconsideres. Podría parecerse a lo siguiente (no necesariamente una solución completa, solo para darle una idea básica):
#!/bin/bash
# Argument 1 : Logging level for that command
# Arguments 2... : Command to execute
# Output suppressed if command level >= current logging level
log()
{
if
(($1 >= logging_level))
then
"${@:2}" >/dev/null 2>&1
else
"${@:2}"
fi
}
logging_level=2
log 1 command1 and its args
log 2 command2 and its args
log 3 command4 and its args
Puede organizar cualquier redirección necesaria (con descriptores de archivo si lo desea) para que se maneje en la función contenedora, de modo que el resto de la secuencia de comandos permanezca legible y libre de redirecciones y condiciones según el nivel de registro seleccionado.
Solución 1. Considere usar descriptores de archivo adicionales. Redirija los descriptores de archivo requeridos a STDOUT o /dev/null dependiendo de la verbosidad seleccionada. Redireccione la salida de cada declaración en su script a un descriptor de archivo correspondiente a su importancia. Eche un vistazo a https:// unix.stackexchange.com/a/218355.