使用AWS CLI写入CloudWatch日志传JSON格式message报错如何解决
问题根因
报错核心是两点:
- AWS CLI 对
--log-events参数的输入格式有严格校验,逗号分隔的简写传参模式下,参数值内的特殊字符如果没有正确转义,会被CLI直接判定为格式非法 - 拼接命令时裸传
$MESSAGE变量,变量中存储的JSON字符串包含双引号、换行符,Shell会把这些特殊字符识别为参数分隔符,最终传给CLI的参数是被截断的碎块——从报错信息里message="{的截断位置就能直接确认,CLI拿到的message值在第一个大括号后就中断了,根本没接收到完整的JSON内容。
解决方法
方案1:直接生成合规JSON参数(最稳妥,无转义问题)
不要用逗号拼接的简写格式,直接通过jq生成完全符合AWS CLI要求的--log-events参数结构,全程不需要手动拼接字符串,从根源避免转义坑:
#!/bin/bash EVENT_TIME=$(date +%s%3N) LOG_LEVEL=6 EVENT_SOURCE=myapp MESSAGE=1 CURRENT_TS=$(date +%s%3N) # 直接生成符合AWS接口要求的log-events结构 LOG_EVENTS=$(jq -n \ --argjson EventTime "$EVENT_TIME" \ --argjson LogLevel "$LOG_LEVEL" \ --arg EventSource "$EVENT_SOURCE" \ --arg Message "$MESSAGE" \ --argjson timestamp "$CURRENT_TS" \ '[{"timestamp": $timestamp, "message": {EventTime:$EventTime,LogLevel:$LogLevel,EventSource:$EventSource,Message:$Message} | tostring}]') aws logs put-log-events \ --log-group-name test \ --log-stream-name local \ --log-events "$LOG_EVENTS"
注意几个关键细节:
- CloudWatch Logs要求
timestamp字段是毫秒级时间戳的数字类型,不能传字符串,因此用--argjson传数值类型,避免jq把它转成带引号的字符串触发类型错误 - message字段的值如果是JSON结构,要先用
tostring转成字符串类型,符合CloudWatch的接口要求 - 整个
--log-events的值用双引号包裹,保证Shell把它作为完整参数传给CLI,不会被空格/特殊字符拆分
方案2:简写格式转义传参(不推荐)
如果一定要用简写的键值对格式,需要手动对JSON内容的特殊字符做转义后再传入:
# 转义JSON内部的双引号 ESCAPED_MESSAGE=$(echo "$MESSAGE" | sed 's/"/\\"/g') aws logs put-log-events \ --log-group-name test \ --log-stream-name local \ --log-events "timestamp=$(date +%s%3N),message=\"$ESCAPED_MESSAGE\""
该方案需要手动处理转义逻辑,一旦内容包含其他特殊字符很容易触发解析错误,仅作为原理演示,不推荐生产使用
排查思路
遇到这类Shell调用AWS CLI的参数解析错误,按以下步骤定位即可:
- 在脚本开头加
set -x开启命令执行打印,运行脚本就能看到Shell实际传给CLI的完整参数内容,一眼就能看到参数被截断、拆分的位置 - 所有包含空格、双引号、换行的变量值,传递时必须用双引号包裹,禁止裸传变量
- 涉及JSON格式的参数,优先用jq生成完整的合规结构,不要手动用字符串拼接的方式组装参数,从根源规避转义问题
补充提示:生产环境连续调用
put-log-events接口时,需要携带上一次接口返回的nextSequenceToken值作为--sequence-token参数传入,否则会触发序列令牌校验错误,本地单次测试可暂时忽略该参数。
内容的提问来源于stack exchange,提问作者Meraj Kashi
相关产品推荐
相关产品推荐

