Chr*_*ngs 7 linux bash logging
我正在努力调整我编写的日志功能,并在许多脚本中重复使用多年,以实现日志记录级别.
简而言之,我想使用单个全局变量来仅打印那些匹配所需日志记录级别详细程度的日志.
我当前的代码如下:
#################################################################################
# SCRIPT LOGGING CONFIGURATION
#
# The following is used by the script to output log data. Depending upon the log
# level indicated, more or less data may be output, with a "lower" level
# providing more detail, and the "higher" level providing less verbose output.
#################################################################################
DATETIME="`date +%Y-%m-%d` `date +%T%z`" # Date format at beginning of log entries to match RFC
DATE_FOR_FILENAME=`date +%Y%m%d`
#
SCRIPT_LOG_DIR="/var/log/company/${APP_NAME}/"
SCRIPT_LOGFILE="${SCRIPT_LOG_DIR}-APPNAME-${DATE_FOR_FILENAME}.log"
#
# Logging Level configuration works as follows:
# DEBUG - Provides all logging output
# INFO - Provides all but debug messages
# WARN - Provides all but debug and info
# ERROR - Provides all but debug, info and warn
#
# SEVERE and CRITICAL are also supported levels as extremes of ERROR
#
SCRIPT_LOGGING_LEVEL="DEBUG"
#################################################################################
# ## END OF GLOBAL VARIABLE CONFIGURATION ##
#################################################################################
# LOGGING
#
# Calls to the logThis() function will determine if an appropriate log file
# exists. If it does, then it will use it, if not, a call to openLog() is made,
# if the log file is created successfully, then it is used.
#
# All log output is comprised of
# [+] An RFC 3339 standard date/time stamp
# [+] The declared level of the log output
# [+] The runtime process ID (PID) of the script
# [+] The log message
#################################################################################
function openLog {
echo -e "${DATETIME} : PID $$ : INFO : New log file (${logFile}) created." >> "${SCRIPT_LOGFILE}"
if ! [[ "$?" -eq 0 ]]
then
echo "${DATETIME} - ERROR : UNABLE TO OPEN LOG FILE - EXITING SCRIPT."
exit 1
fi
}
function logThis() {
DATETIME=$(date --rfc-3339=seconds)
if [[ -z "${1}" || -z "${2}" ]]
then
echo "${DATETIME} - ERROR : LOGGING REQUIRES A DESTINATION FILE, A MESSAGE AND A PRIORITY, IN THAT ORDER."
echo "${DATETIME} - ERROR : INPUTS WERE: ${1} and ${2}."
exit 1
fi
LOG_MESSAGE="${1}"
LOG_PRIORITY="${2}"
# Determine if logging level is supported and desired
#
# This seems more complex than may be necessary
if [[ ${LOG_PRIORITY} -eq "DEBUG" ]] && [[ ${SCRIPT_LOGGING_LEVEL} -eq "DEBUG" ]]
then
LOG_PRIORITY_SUPPORTED=true
elif [[ ${LOG_PRIORITY} -eq "INFO" ]] && [[ ${SCRIPT_LOGGING_LEVEL} -eq "DEBUG"||"INFO" ]]
then
LOG_PRIORITY_SUPPORTED=true
elif [[ ${LOG_PRIORITY} -eq "WARN" ]] && [[ ${SCRIPT_LOGGING_LEVEL} -eq "DEBUG"||"INFO"||"WARN" ]]
then
LOG_PRIORITY_SUPPORTED=true
elif [[ ${LOG_PRIORITY} -eq "ERROR"||"SEVERE"||"CRITICAL" ]] && [[ ${SCRIPT_LOGGING_LEVEL} -eq "DEBUG"||"INFO"||"WARN"||"ERROR"||"SEVERE"||"CRITICAL" ]]
then
LOG_PRIORITY_SUPPORTED=true
else
echo -e "CRITICAL: Declared log priority is not supported."
exit 1
fi
# If logging level NOT supported, dump it
if ! [ ${LOG_PRIORITY_SUPPORTED} ]
then
echo "priority unsupported"
break
fi
# No log file, create it.
if ! [[ -f ${SCRIPT_LOGFILE} ]]
then
echo -e "INFO : No log file located, creating new log file (${SCRIPT_LOGFILE})."
echo "${DATETIME} : PID $$ :INFO : No log file located, creating new log file (${SCRIPT_LOGFILE})." >> "${SCRIPT_LOGFILE}"
openLog
fi
# Write log details to file
echo -e "${LOG_PRIORITY} : ${LOG_MESSAGE}"
echo -e "${DATETIME} : PID $$ : ${LOG_PRIORITY} : ${LOG_MESSAGE}" >> "${SCRIPT_LOGFILE}"
# Reset log level support flag
LOG_PRIORITY_SUPPORTED=false
}
Run Code Online (Sandbox Code Playgroud)
当使用该功能时,将使用如下:
logThis "This is my log message" "DEBUG"
Run Code Online (Sandbox Code Playgroud)
要么
logThis "This is my log message" "ERROR"
Run Code Online (Sandbox Code Playgroud)
要么
logThis "This is my log message" "INFO"
Run Code Online (Sandbox Code Playgroud)
您可以在上面的代码中看到,我已尝试(无论多么复杂)使用传入的消息上的case select过滤消息.
这不起作用.无论提供的值如何,所有消息都将通过LOG_PRIORITY.
即使它不是受支持的值.例如,以下内容仍允许处理日志消息:
SCRIPT_LOGGING_LEVEL="FARCE"
Run Code Online (Sandbox Code Playgroud)
或者即使我像这样设置给定消息的值:
logThis "This is my log message" "FARCE"
Run Code Online (Sandbox Code Playgroud)
我不打算完全重构我的功能.我使用所涉及的功能在野外有太多的脚本,如果我改变标准化,它也需要重新编写.
我不一定需要有人像他们所说的那样"为我做工作",但考虑到我的约束,在一个有效的方向上轻推就足够了.我将很高兴在未来的编辑中发布最终实现.
我认识到现在有更新更好的方法来处理BASH脚本中的日志记录功能,但这些函数在这么多脚本中的普及意味着对正在运行的函数的简单更新将产生非常广泛的影响.
===
要关闭这个问题的循环,最终解决方案包括一些最初不在范围内的更改,但为了满足一些更好的实践,我做了以下操作:
if和elif语句.我之前尝试过一个案例选择,但是因为沮丧而已经转移到了维持沉重且难以辨认的if + elif选项.此解决方案符合我的要求,因为它需要对现有脚本代码进行最少的更改,并允许现有的调用函数的方法工作.
在接受解决方案时,我的帖子有三个推荐选项.所有这三个选项都很有用,但我最终选择的只需要三行代码即可实现.
如上所述,我确实做了一些不在范围内的更改,但这些更改不会影响本文提供的代码之外的功能.
另外一个注意事项:我在目标环境中验证了这些功能,并且在编辑时它们在Ubuntu 16.04上按预期工作.
#################################################################################
# SCRIPT LOGGING CONFIGURATION
#
# The following is used by the script to output log data. Depending upon the log
# level indicated, more or less data may be output, with a "lower" level
# providing more detail, and the "higher" level providing less verbose output.
#################################################################################
dateTime="`date +%Y-%m-%d` `date +%T%z`" # Date format at beginning of log entries to match RFC
dateForFileName=`date +%Y%m%d`
#
scriptLogDir="/var/log/company/${appName}/"
scriptLogPath="${scriptLogDir}${appName}-${dateForFileName}.log"
#
# Logging Level configuration works as follows:
# DEBUG - Provides all logging output
# INFO - Provides all but debug messages
# WARN - Provides all but debug and info
# ERROR - Provides all but debug, info and warn
#
# SEVERE and CRITICAL are also supported levels as extremes of ERROR
#
scriptLoggingLevel="DEBUG"
#################################################################################
# ## END OF GLOBAL VARIABLE CONFIGURATION ##
#################################################################################
# LOGGING
#
# Calls to the logThis() function will determine if an appropriate log file
# exists. If it does, then it will use it, if not, a call to openLog() is made,
# if the log file is created successfully, then it is used.
#
# All log output is comprised of
# [+] An RFC 3339 standard date/time stamp
# [+] The declared level of the log output
# [+] The runtime process ID (PID) of the script
# [+] The log message
#################################################################################
function openLog {
echo -e "${dateTime} : PID $$ : INFO : New log file (${scriptLogPath}) created." >> "${scriptLogPath}"
if ! [[ "$?" -eq 0 ]]
then
echo "${dateTime} - ERROR : UNABLE TO OPEN LOG FILE - EXITING SCRIPT."
exit 1
fi
}
function logThis() {
dateTime=$(date --rfc-3339=seconds)
if [[ -z "${1}" || -z "${2}" ]]
then
echo "${dateTime} - ERROR : LOGGING REQUIRES A DESTINATION FILE, A MESSAGE AND A PRIORITY, IN THAT ORDER."
echo "${dateTime} - ERROR : INPUTS WERE: ${1} and ${2}."
exit 1
fi
logMessage="${1}"
logMessagePriority="${2}"
declare -A logPriorities=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3 [SEVERE]=4 [CRITICAL]=5)
[[ ${logPriorities[$logMessagePriority]} ]] || return 1
(( ${logPriorities[$logMessagePriority]} < ${logPriorities[$scriptLoggingLevel]} )) && return 2
# No log file, create it.
if ! [[ -f ${scriptLogPath} ]]
then
echo -e "INFO : No log file located, creating new log file (${scriptLogPath})."
echo "${dateTime} : PID $$ :INFO : No log file located, creating new log file (${scriptLogPath})." >> "${scriptLogPath}"
openLog
fi
# Write log details to file
echo -e "${logMessagePriority} : ${logMessage}"
echo -e "${dateTime} : PID $$ : ${logMessagePriority} : ${logMessage}" >> "${scriptLogPath}"
}
Run Code Online (Sandbox Code Playgroud)
解决此问题的一种方法是创建所有级别的关联数组.为每个级别分配一个数字,然后比较这些数字以决定是否应该记录.想象一下,您想要添加另一个日志记录级别.if你的那些陈述会失控:
#!/usr/bin/env bash
declare -A levels=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3)
script_logging_level="INFO"
logThis() {
local log_message=$1
local log_priority=$2
#check if level exists
[[ ${levels[$log_priority]} ]] || return 1
#check if level is enough
(( ${levels[$log_priority]} < ${levels[$script_logging_level]} )) && return 2
#log here
echo "${log_priority} : ${log_message}"
}
logThis "This will log" "WARN"
logThis "This will not log" "DEBUG"
logThis "This will not log" "OUCH"
Run Code Online (Sandbox Code Playgroud)
LOG_PRIORITY_SUPPORTED=false这是为什么你应该local在函数中使用变量的一个例子.此外,您不应使用大写变量,因为它们可能与环境或内部shell变量冲突.