【发布时间】:2018-06-13 16:10:36
【问题描述】:
问题
我正在努力调整我多年来编写并在许多脚本中重复使用的日志记录功能,以尊重日志记录级别。
简而言之,我想使用单个全局变量仅打印与所需日志记录级别详细程度匹配的日志。
目前
我当前的代码如下所示:
#################################################################################
# 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
}
当使用函数时变成这样使用:
logThis "This is my log message" "DEBUG"
或
logThis "This is my log message" "ERROR"
或
logThis "This is my log message" "INFO"
尝试
您可以在上面的代码中看到,我已经尝试(无论多么复杂)在传入的消息上使用案例选择来过滤消息。
这不起作用。无论为LOG_PRIORITY 提供的值如何,所有消息都会通过。
即使它不是受支持的值。 例如,以下仍然允许处理日志消息:
SCRIPT_LOGGING_LEVEL="FARCE"
或者即使我像这样为给定消息设置值:
logThis "This is my log message" "FARCE"
您的帮助
我不打算完全重构我拥有的功能。我有太多的脚本在使用所涉及的函数,如果我改变标准化,也需要对这些脚本进行返工。
我不一定需要有人像他们所说的那样“为我做这项工作”,但考虑到我的限制,朝一个有效的方向轻推就足够了。 我很高兴在以后的编辑中发布最终实现。
明白
我认识到现在有更新更好的方法来处理 BASH 脚本中的日志记录功能,但是这些功能在如此多的脚本中的流行意味着对正在使用的功能的简单更新将产生非常广泛的影响。
===
最终解决方案
为了结束这个问题的循环,最终的解决方案包括一些最初不在范围内的更改,但为了满足一些更好的做法,我做了以下操作:
- 将所有变量名称转换为大小写混合,而不是为系统和环境变量保留的所有大写字母。一些评论者(@PesaThe 和 @CharlesDuffy)注意到了这一点。
- 我的原始帖子指出我最初使用了案例选择,但显示了
if和elif语句的集合。我之前确实尝试过选择案例,但出于沮丧,我转向了维护繁重且难以辨认的 if + elif 选项。 - 代码更改允许删除(如 @PesaThe 建议的那样)在函数末尾重置的不干净变量。
解决方案详情
此解决方案符合我的要求,因为它需要对现有脚本代码的更改最少,并允许调用函数的现有方法工作。
在接受解决方案时,针对我的帖子推荐了三个选项。这三个选项都很有帮助而且很重要,但我最终选择的那个只需要三行代码即可实现。
如上所述,我确实做了一些不在范围内但不影响本文提供的代码之外的功能的更改。
另外一点:我确实在我的目标环境中验证了这些功能,并且在编辑时它们在 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}"
}
【问题讨论】:
-
顺便说一句,全大写名称用于对 shell 和操作系统有意义的变量,而具有至少一个小写字符的名称保证应用程序使用安全。请参阅相关的 POSIX 规范 @pubs.opengroup.org/onlinepubs/9699919799/basedefs/…,第四段。
-
另外,
echo -e通常最好避免使用。请参阅the POSIX spec forecho,尤其是应用程序用法和基本原理部分:支持-e明显违反标准(不允许使用-n以外的选项,这会使行为未定义)和printf明确推荐用于新开发。 -
@CharlesDuffy 我将实现您的第一个表示法(很好),但不是第二个。
logThis()函数的其他部分的重写不在我定义的工作范围内,目前使用的平台支持-e参数。虽然不像标准机构所期望的那样跨 POSIX 平台的可移植性,但对于目标环境来说,可移植性已经足够了。 -
请注意,即使是 bash 也不总是支持
echo -e;它是否可用取决于xpg_echo和posix运行时标志的状态(以及它们是否默认打开取决于编译时标志)。 -
@CharlesDuffy 明白了。谢谢。