суббота, 27 февраля 2016 г.

Logging library for Unix Shell

This library is similar to Log library for Python or Java. Here its code:

#!/bin/sh

# Simple logging facility for Unix /bin/sh
# ========================================
#
# Configuration
# -------------
#
#   _LOGGING_DATEFMT, _LOGGING_FMT variables are used for format of datetime
#   and log record
#
#   * _LOGGING_DATEFMT : uses the same format as date(1) and Python Lib
#
#   * _LOGGING_FMT : uses format similar to Python logging Lib
#
#       Available fields for _LOGGING_FMT are:
#         asctime, filename, levelname, levelno, message, name, pathname,
#         process, processName, user
#
# Example
# -------
#
#     getLogger prg1
#     getLogger prg2
#     
#     addLogHandler prg1 FileHandler /dev/stderr
#     addLogHandler prg2 ConsoleHandler
#     addLogHandler prg2 FileHandler /var/log/somefile
#     addLogHandler prg2 FileHandler /var/log/somefile_copy
#
#     setLogLevel prg1 DEBUG
#
#     prg1_debug DebugMessage
#     prg1_error ErrorMessage
#     echo InfoMessage|prg2_info
#
#   will produces on STDERR console:
#     ...
#     2014-07-18T17:09:52+0300 prg1 DEBUG DebugMessage
#     ...
#
#   will produces on console:
#     ...
#     2014-07-18T17:09:52+0300 prg1 ERROR ErrorMessage
#     2014-07-18T17:09:52+0300 prg2 INFO InfoMessage
#     ...
#
#   and in /var/log/somefile:
#     ...
#     2014-07-18T17:09:52+0300 prg2 INFO InfoMessage
#     ...
#
#   and in /var/log/somefile_copy:
#     ...
#     2014-07-18T17:09:52+0300 prg2 INFO InfoMessage
#     ...

# TODO: log levels per handlers


############################## Settings #####################################

# Global log record format and date format:
_LOGGING_DATEFMT="%Y-%m-%dT%H:%M:%S%z"
_LOGGING_FMT="%asctime %name %levelname %message"



################################### Code #####################################

_log_SCRIPT=`readlink -f $0`

_log_getLogAttr() {
  name=$1; attr=${1}_$2
  eval echo $`echo $attr`
}

################################### Handlers #################################

# SYNOPSIS: ...
#
# Nothing to do, all args are ignored
_log_NullHandler() {
  :
}

# SYNOPSIS: $name $level $msg
#
# Logs $msg with $level via logger(1) utility
_log_SyslogHandler() {
  local name level msg syslog_level
  name="$1"; level="$2"; msg="$3"
  syslog_level=`_log_syslogLevel $level`
  logger -p "local0.${syslog_level}" -t $name "$msg"
}

# SYNOPSIS: $name $level $msg
#
# Logs message $msg on console
_log_ConsoleHandler() {
  local name level msg
  name="$1"; level="$2"; msg="$3"
  echo "$msg"
}

# SYNOPSIS: $subject $address $msg
#
# If mail(1) exists, call it to send $msg via EMail
_log_MailHandler() {
  command -v mail >/dev/null 2>&1 || return 1
  local subj addr name level msg
  subj="$1"; addr="$2"; name="$3"; level="$4"; msg="$5"
  echo "$msg"|mail -s "${subj}${level}: $name" $addr
}

# SYNOPSIS: $filename $msg
#
# Append message $msg to file $filename
_log_FileHandler() {
  local filename name level msg
  filename="$1"; name="$2"; level="$3"; msg="$4"
  touch "$filename"
  echo "$msg" >> "$filename"
}

_log_syslogLevel() {
  case $1 in
    CRITICAL)
      echo crit;;
    FATAL)
      echo panic;;
    ERROR)
      echo error;;
    WARNING)
      echo warning;;
    INFO)
      echo info;;
    DEBUG)
      echo debug;;
    *)
      echo notice;;
  esac
}

_log_levelNo() {
  case $1 in
    CRITICAL|FATAL)
      echo 50;;
    ERROR)
      echo 40;;
    WARNING)
      echo 30;;
    INFO)
      echo 20;;
    DEBUG)
      echo 10;;
    *)
      echo 0;;
  esac
}

_log() {
  local name level msg handlers h closure_args skip no_closure_args levelno enabled_levelno
  name="$1"; level="$2"; msg="$3"

  levelno=`_log_levelNo $level`
  enabled_levelno=`_log_getLogAttr $name level`
  [ $levelno -lt $enabled_levelno ] && return

  handlers=`_log_getLogAttr $name handlers`
  handlers=`echo "$handlers"|sed 's/__COMMA__/\\ /g'`
  for h in $handlers; do
    echo $skip|grep $h >/dev/null && continue
    closure_args=`_log_getLogAttr $name ${h}_handler_args`
    closure_args=`echo "$closure_args"|sed 's/__COMMA__/\\ /g'`
    no_closure_args=1
    for ca in $closure_args; do
      eval "$h $ca $name $level \"$msg\""
      no_closure_args=0
    done
    [ $no_closure_args ] && eval "$h $name $level \"$msg\""
    skip="$h $skip"
  done
}

# SYNOPSIS: $name $h [$args...]
#
# Adds handler $h for logger $name with possible arguments
# (see handlers' implementations for concrete args)
addLogHandler() {
  local name handler oldhandlers exthandler args oldargs
  name=$1; exthandler="$2"; handler=_log_"$exthandler"; shift 2; args="$*"
  oldhandlers=`_log_getLogAttr $name handlers`
  oldargs=`_log_getLogAttr $name ${handler}_handler_args`
  # __COMMA__ is used to separate items in lists
  eval "${name}_handlers=${handler}__COMMA__${oldhandlers:-_log_NullHandler}"
  eval "${name}_${handler}_handler_args=\"$args\"__COMMA__$oldargs"
}

# SYNOPSIS: $name | $name $longname
#
# No return and no output but setups logger for this $name. In 2nd form $longname
# is used in message substitution
getLogger() {
  local asctime filename levelname levelno message name pathname process processName _lv user longname

  asctime=$_LOGGING_DATEFMT
  filename=`getScriptName`
  message='${1:-`tee`}'
  name="$1"
  longname=${2:-$name}
  pathname=`echo "$_log_SCRIPT"|sed 's/\\//\\\\\//g'`
  process='$$'
  processName=`ps -A -opid,comm | awk -v PID=$$ '$1 == PID { print $2 }'`
  processName=`echo "$processName"|sed 's/\\//\\\\\//g'`
  user="$USER"

  for _lv in debug info warning error fatal critical; do
    levelname=`echo -n $_lv|tr '[:lower:]' '[:upper:]'`
    levelno=`_log_levelNo $levelname`

    fmt=`echo $_LOGGING_FMT | \
      sed 's/%asctime/\`date +$_LOGGING_DATEFMT\`/g' | \
      sed "s/%filename/$filename/g" | \
      sed "s/%levelname/$levelname/g" | \
      sed "s/%levelno/$levelno/g" | \
      sed "s/%message/$message/g" | \
      sed "s/%pathname/$pathname/g" | \
      sed "s/%processName/$processName/g" | \
      sed "s/%process/$process/g" | \
      sed "s/%user/$user/g" | \
      sed "s/%name/$longname/g"`
    eval "${name}_${_lv} () { _log $name $levelname \"$fmt\"; }"
  done
  setLogLevel $name INFO
}

# SYNOPSIS: $name $level
#
# Sets the threshold for $name logger to $level (INFO, ERROR, etc.). Logging
# messages which are less severe than $level will be ignored.
# Default is INFO
setLogLevel() {
  local name level levelno
  name="$1"; level=`echo -n $2|tr '[:lower:]' '[:upper:]'`
  levelno=`_log_levelNo $level`
  eval "${name}_level=$levelno"
}

# SYNOPSIS:
#
# Echos name of script
getScriptName() {
  echo `basename "$_log_SCRIPT"`
}

Example of usage:

#!/bin/sh
. ./logging.sh

getLogger prg1 `getScriptName`
addLogHandler prg1 ConsoleHandler
addLogHandler prg1 FileHandler ./lll1.log
addLogHandler prg1 FileHandler ./lll2.log
setLogLevel prg1 DEBUG

prg1_debug Debugggg
prg1_error Errrrror
echo 'Message-INFO!!!!
1111
2222'|prg1_info

Комментариев нет:

Отправить комментарий

Thanks for your posting!