0

我正在开发一个“包装脚本”以在 Bash 中用作“日志记录辅助”。

它应该在调用它时打印出有关调用堆栈的信息。

我已经完成了这方面的工作,但仍有几个问题/疑问,我想从这里的专家那里得到最好的答案。


我的代码:

################################################################################
# Formats a logging message.                                                    

function my_function_format_logging_message() {                                 

  local -r TIMESTAMP="$(date '+%H:%M:%S')"                                      
  local -r PROCESS="$$" # Deliberately not using $BASHPID, focus: parent process

  local -r CALLER="${FUNCNAME[1]}"                                              

  local -i call_stack_position=1                                                
  if [[ "${CALLER}" == 'my_function_log_trace' ||                               
        "${CALLER}" == 'my_function_log_debug' ||                               
        "${CALLER}" == 'my_function_log_info' ||                                
        "${CALLER}" == 'my_function_log_warning' ||                             
        "${CALLER}" == 'my_function_log_error' ||                               
        "${CALLER}" == 'my_function_log_critical' ]]                            
  then                                                                          
    call_stack_position=$((call_stack_position++))                              
  fi                                                                            

  local -r SOURCE="$(basename "${BASH_SOURCE[$call_stack_position]}")"          
  local -r FUNCTION="${FUNCNAME[$call_stack_position]}"                         
  local -r LINE="${BASH_LINENO[$call_stack_position-1]}" # Previous function    

  local -r SEVERITY="$1"                                                        
  local -r MESSAGE="$2"                                                         

  # TODO: perform argument validation                                           

  printf '%s [PID %s] %s %s %s:%s - %s\n' \                                     
         "${TIMESTAMP}" \                                                       
         "${PROCESS}" \                                                         
         "${SEVERITY}" \                                                        
         "${SOURCE}" \                                                          
         "${FUNCTION}" \                                                        
         "${LINE}" \                                                            
         "${MESSAGE}"                                                           

}                                                                               
################################################################################

使用示例:

my_function_format_logging_message CRITICAL Temporarily increasing energy level to 9001

或者:

my_function_log_info Dropping back to power level 42

我的疑惑:

  • call_stack_position=$((call_stack_position++))

我想不出更好的方法来增加这个变量,有没有更好/更易读的形式?

  • 我可以使用更好的构造来检测调用是否是由日志记录方法进行的吗?(例如跟踪、调试、信息......)。所有这些if陈述都让我的眼睛受伤。

  • 我是在重新发明轮子/滥用我想学习的工具吗?(即shell脚本)

当然,我可能是在重新发明轮子,但这是自我训练……有一天不再是收费站的夜班工人。


笔记

我正在寻找与指定的 my_function_log_* 名称匹配的名称,而没有其他名称。假设我有那种自由度是不对的(很多ifs 正是出于这个原因,我正在寻找一些语法糖或更好地使用语言特性来进行那种类型的“集合成员资格”测试)。

4

3 回答 3

2

我可以为你的前两个问题提出这个建议:

if [[ "${CALLER}" == my_function_log_* ]]
then 
  let call_stack_position++
fi

如果您只想要 log_ 之后的一组值:

if [[ "${CALLER}" =~ my_function_log_(trace|debug|info|warning|error|critical) ]]
then 
  let call_stack_position++
fi      
于 2013-03-15T01:12:42.350 回答
1

一种更易读的增量方法是在数字上下文中递增它:

(( call_stack_position++ ))

对于匹配,您可以在 bash 中使用 glob:

[[ $CALLER == my_function_log_* ]]

至于重新发明轮子,您可以使用logger命令从 bash 中使用 syslog 日志记录。本地 syslog 守护进程将处理格式化日志消息并将其写入文件。

logger -p local0.info "CRITICAL Temporarily increasing energy level to 9001"

更新,基于评论。您可以使用关联数组更明确地说明您要查找的内容。它需要 bash v4 或更高版本。

declare -A arr=(
    ['my_function_log_trace']=1
    ['my_function_log_debug']=1
    ['my_function_log_info']=1
    ['my_function_log_warning']=1
    ['my_function_log_error']=1
    ['my_function_log_critical']=1
);

if [[ ${arr[CALLER]} ]]; then
    ...
fi

您还可以使用扩展通配符进行模式匹配,类似于 perreal 答案中的正则表达式,但没有正则表达式:

shopt -s extglob
if [[ $CALLER == my_function_log_@(trace|debug|info|warning|error|critical) ]]; then
    ...
fi
于 2013-03-15T01:17:18.913 回答
1

Bash 的类型系统,如果你甚至想这么称呼它,它是非常初级的:字符串和整数是它唯一的一等公民,数组是事后才想到的,其功能远不及 Python 集合或 Ruby 数组。in话虽如此,对于依赖字符串匹配的数组来说,有一个穷人的运算符。给定一个函数名数组:

log_functions=(my_function_log_trace my_function_log_debug my_function_log_info my_function_log_warning my_function_log_error my_function_log_critical)

这:

[[ ${log_functions[*]} =~ \\b$CALLER\\b ]]

将仅匹配数组的成员。当我们谈论穷人的构造时,您可以将上述模式与布尔控制运算符结合到穷人的三元赋值中,以完全跳过数值评估:

local -i call_stack_position=$([[ ${log_functions[*]} =~ \\b$CALLER\\b ]] && echo 1 || echo 2)

警告:不支持 GNU 扩展的系统上regcomp()(特别是 OS X 和 Cygwin),字边界匹配需要使用更详细的字符类形式,即

[[ ${log_functions[*]} =~ [[:\<:]]$CALLER[[:\>:]] ]]

注意:看到你的代码并注意到你提到你正在学习 shell 脚本,我会提供两个与问题无关的观察:

  1. 变量扩展的大括号符号仅在数组访问、扩展操作和消除字符串连接中的变量名称歧义时需要。在其他情况下不需要它,即在您的测试和您的printf命令中。
  2. 使用扩展字符串操作比使用外部操作要快得多,因此建议尽可能使用。而不是使用basename,使用${var##*/}
于 2013-03-15T04:04:26.917 回答