格式化 shell 脚本日志并写入文件。

3

我需要将一个shell脚本的所有输出/错误日志记录到一个标准格式的文件中。

例如,对于文件script.sh:

echo "I want to be a json message!"
echo "I also want to be a json message!"

以下是执行的方式:

./script.sh >> jsonmessages.log

应该给我一个文件内容,如下:
{timestamp: '2020-12-31 00:00:00', message: 'I want to be a json message!'}
{timestamp: '2020-12-31 00:00:00', message: 'I also want to be a json message!'}
3个回答

4

可靠地将shell字符串翻译成有效的JSON字符串的最佳选择是使用JSON解析器/格式化程序。最流行的是jq

下面是使用jq实现的带有时间戳的JSON消息记录器:

#!/usr/bin/env bash

json_logger() {
  while read -r msg || [ -n "$msg" ]; do
    jq \
      -nc \
      --arg msg "$msg" \
      '{ "timestamp": (now | strftime("%Y-%m-%d %H:%M:%S")), "message": $msg }'
  done
}


----------


编辑:

正如KamilCuk所写:

我们可以在没有缓慢的bash循环的情况下做得更好 - 只需使用jq --raw-input '{ "timestamp": (now | strftime("%Y-%m-%d %H:%M:%S")), "message": . }'即可。

因此,这里是改进后的json_logger

json_logger() {
  jq \
    --raw-input \
    --compact-output \
    '{ "timestamp": (now | strftime("%Y-%m-%d %H:%M:%S")), "message": . }'
}

附录:

Harshit Kushwaha 写到:

例如,我需要在 json 中打印调用 json_logger 的方法名。 我该如何修改 json_logger 并如何在echo中使用它?

这里是一个实现方式,如果提供了方法名,则将其添加为参数传递给 json_logger 函数:

#!/usr/bin/env bash

IFS= read -r -d '' __JQ_LOGGER_SCRIPT <<'JQSCRIPT'
{
  "timestamp": now | strftime("%Y-%m-%d %H:%M:%S"),
  "message": .
} |
  if ($name | length) != 0
  then
    . + { "method": $name }
  else
    .
  end
JQSCRIPT

json_logger() {
  jq \
    --raw-input \
    --compact-output \
    --arg name "$1" \
    "$__JQ_LOGGER_SCRIPT"
}

echo "I want to be a json message!" | json_logger
echo "I also want to be a json message!" | json_logger echo

printf %s $'I am a message with "quoted text" and some special characters: \'\t\7\42\\\'; that can only be properly converted to JSON with a JSON formatter and parser.' | json_logger printf

产生这个JSON输出:
{"timestamp":"2021-01-29 14:02:46","message":"I want to be a json message!"}
{"timestamp":"2021-01-29 14:02:46","message":"I also want to be a json message!","method":"echo"}
{"timestamp":"2021-01-29 14:02:46","message":"I am a message with \"quoted text\" and some special characters: '\t\u0007\"\\'; that can only be properly converted to JSON with a JSON formatter and parser.","method":"printf"}

谢谢@Léa Gris。但是,使用这个方法,我只能记录echo或printf消息。我们是否可以以某种方式配置该函数,使其也可用于在运行脚本时生成的错误消息? - Harshit Kushwaha
@HarshitKushwaha 请查看:如何将stderr导入,而不是stdout? - Léa Gris
1
我们可以做得更好,而不需要缓慢的bash循环 - 只需使用jq --raw-input '{ "timestamp": (now | strftime("%Y-%m-%d %H:%M:%S")), "message": . }'即可。 - KamilCuk
感谢 @KamilCuk 和 Léa Gris,我现在能够使用2>&1打印stderr和stdout消息了。 我可以在json中添加一些参数吗?例如,我需要从调用json_logger的方法中打印方法名。 我该如何修改json_logger以及如何在echo中使用它? - Harshit Kushwaha
@HarshitKushwaha 我刚刚添加了一个可选的方法名称实现。虽然下次请不要在得到答案后扩展您的问题。JQ是一个很好的工具,但它有自己的脚本语法,可能很难使用,但我鼓励您尝试,反复进行自己的虚拟测试。这真的值得努力,因为这将节省您在处理来自shell的JSON内容时的时间。 - Léa Gris

2

感谢提供的解决方案。

我通过以下实现方式,成功将脚本执行的日志写入日志文件:

  1. Creating a json logger

      json_logger() {
             # Values for json
             current_timestamp="$(date +\"%Y-%m-%dT%H:%M:%S\")"
             param1="$param1value"
             param2="$param2value"
             log_level=$1
             script_name=$(basename $2 .sh)
    
             # Create json and re-direct to log file
             jq --raw-input --compact-output \
             '{
                     "@timestamp": '$current_timestamp',
                     "parameter1": "'$param1'",
                     "parameter2": "'$param2'",
                     "log-level": "'$log_level'",
                     "logger": "'$script_name'",
                     "message": .
             }' >> /var/log/app.log 2>&1 
         }
    
  2. And then executing scripts as below

       "script.sh" 2>&1 | json_logger "INFO" {job_name or script_name}
    

1
./script.sh | awk -v squot="'" '{ print "{\"timestamp\": \""strftime("%Y-%m-%d %H:%M:%S")"\", \"message\": \""$0"\"}" }' mess | jq >> jsonmessages.log

使用GNU awk,使用awk的strftime函数打印所需格式的时间戳,以及双引号和所需格式中的其他数据。通过管道传输到jq以确保有效的JSON。

awk 无法可靠地生成有效的 JSON 字符串。 - Léa Gris
然后,您可以通过管道传递到 jq,以确保您拥有有效的 JSON。 - Raman Sailopal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接