GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie verwalte ich die Protokollausführlichkeit in einem Shell-Skript?

Wenn wir die Idee von @Fred ein wenig weiter verbessern, könnten wir auf diese Weise eine kleine Protokollierungsbibliothek erstellen:

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 ==="; }

Nehmen wir an, die obige Quelle befindet sich in einer Bibliotheksdatei namens "logging_lib.sh", wir könnten sie auf diese Weise in einem regulären Shell-Skript verwenden:

#!/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!"

Führt zu dieser Ausgabe:

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

Wie wir sehen können, log_verbose hat keine Ausgabe erzeugt, da die Protokollebene DEBUG ist, eine Ebene unter VERBOSE. Jedoch log_debug_file date.out hat die Ausgabe erzeugt und log_execute INFO auch , da die Protokollebene auf DEBUG gesetzt ist, was>=INFO.

ist

Auf dieser Grundlage könnten wir auch Befehls-Wrapper schreiben, wenn wir noch mehr Feinabstimmung benötigen:

git_wrapper() {
  # run git command and print the output based on log level
}

Wenn diese vorhanden sind, könnte das Skript erweitert werden, um ein Argument --log-level level zu akzeptieren die die Ausführlichkeit des Protokolls bestimmen kann, mit der es ausgeführt werden sollte.

Hier ist eine vollständige Implementierung des Loggings für Bash, reich an mehreren Loggern:

https://github.com/codeforester/base/blob/master/lib/stdlib.sh

Wenn jemand neugierig ist, warum einige Variablen im obigen Code mit einem führenden Unterstrich benannt sind, siehe diesen Beitrag:

  • Korrigieren Sie die Großschreibung von Bash- und Shell-Skriptvariablen

Sie haben bereits die scheinbar sauberste Idee in Ihrer Frage (eine Wrapper-Funktion), aber Sie scheinen zu denken, dass dies chaotisch wäre. Ich würde vorschlagen, dass Sie es sich noch einmal überlegen. Es könnte wie folgt aussehen (nicht unbedingt eine vollwertige Lösung, nur um Ihnen die Grundidee zu vermitteln):

#!/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

Sie können dafür sorgen, dass jede erforderliche Umleitung (mit Dateideskriptoren, wenn Sie möchten) in der Wrapper-Funktion behandelt wird, sodass der Rest des Skripts lesbar und frei von Umleitungen und Bedingungen bleibt, abhängig von der ausgewählten Protokollierungsstufe.


Lösung 1. Erwägen Sie die Verwendung zusätzlicher Dateideskriptoren. Leiten Sie erforderliche Dateideskriptoren je nach ausgewählter Ausführlichkeit auf STDOUT oder /dev/null um. Leiten Sie die Ausgabe jeder Anweisung in Ihrem Skript auf einen Dateideskriptor um, der ihrer Wichtigkeit entspricht. unix.stackexchange.com/a/218355 .


Linux
  1. Wie weist man die Ausgabe eines Befehls einer Shell-Variablen zu?

  2. Wie behandelt man Schalter in einem Shell-Skript?

  3. Wie erstelle ich eine temporäre Datei im Shell-Skript?

  4. Wie kann man feststellen, ob die Ausgabe eines Befehls oder Shell-Skripts Stdout oder Stderr ist?

  5. Crontab-Protokoll:So protokollieren Sie die Ausgabe meines Cron-Skripts

So leiten Sie die Shell-Befehlsausgabe um

So schreiben Sie ein Shell-Skript in Ubuntu

So speichern Sie einen Linux-Befehl als Variable im Shell-Skript

So führen Sie Shell-Skript als SystemD-Dienst in Linux aus

Wie führe ich einen Befehl in einem Shell-Skript aus?

Farbige Shell-Script-Ausgabebibliothek