Amazon CloudWatch Logs 工具手册
log group 才是治理起点
Amazon CloudWatch Logs 的基本对象并不复杂:log event 进入 log stream,多个 stream 归入 log group。真正影响团队治理的是后半句——同一 log group 共享保留、监控和访问控制设置。把所有主机和环境都塞进一个 group,会让查询方便一点,却把权限、成本归属、删除保护和保留期绑死在一起。
AWS 服务可以直接向 CloudWatch Logs 发送事件,EC2 与本地主机可以使用统一 CloudWatch Agent,程序也能调用 PutLogEvents。这些写入方式不应共用管理权限。应用角色只写目标 ARN,查询者只读,修改保留、KMS、订阅和删除交给平台角色。官方的 log group 与 log stream 说明 应作为资源语义基线。
用唯一实验资源证明写入与查询
下面的实验要求 AWS CLI v2 已登录到明确的测试账户。实验 ID 同时进入 group、stream、trace 和 owner tag;创建前还要做精确同名检查,避免接管已有资源。
export AWS_REGION=ap-southeast-1
LAB_ID="loglab-$(date -u +%Y%m%dT%H%M%SZ)-$(openssl rand -hex 4)"
GROUP="/tool-lab/checkout/dev/$LAB_ID"
STREAM="manual-$LAB_ID"
TRACE_ID="TE-AWS-$LAB_ID"
CLOUD_TMP=$(mktemp -d "${TMPDIR:-/tmp}/cloud-log-lab.XXXXXXXX")
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
EXISTING_GROUP=$(aws logs describe-log-groups --region "$AWS_REGION" \
--log-group-name-prefix "$GROUP" \
--query "logGroups[?logGroupName=='$GROUP'].logGroupName | [0]" --output text)
test "$EXISTING_GROUP" = "None" || { echo "refuse: log group already exists" >&2; exit 1; }
aws logs create-log-group --region "$AWS_REGION" --log-group-name "$GROUP"
aws logs tag-log-group --region "$AWS_REGION" --log-group-name "$GROUP" \
--tags "log-lab-owner=$LAB_ID"
aws logs put-retention-policy --region "$AWS_REGION" --log-group-name "$GROUP" --retention-in-days 7
aws logs create-log-stream --region "$AWS_REGION" --log-group-name "$GROUP" --log-stream-name "$STREAM"CloudWatch Logs 默认保留不会自动过期,因此测试 group 创建后就设置保留。生产环境的保留期应来自数据分级与调查窗口,不是从开发示例复制一个数字。
事件数组由 jq 生成到文件,再交给 CLI。这样可以避开 shell 对嵌套 JSON 的二次解释:
NOW_MS=$(($(date +%s) * 1000))
jq -n --argjson timestamp "$NOW_MS" --arg trace_id "$TRACE_ID" \
'[{timestamp:$timestamp,message:({service:"checkout",trace_id:$trace_id,message:"payment timeout"}|tojson)}]' \
> "$CLOUD_TMP/aws-log-events.json"
aws logs put-log-events --region "$AWS_REGION" \
--log-group-name "$GROUP" --log-stream-name "$STREAM" \
--log-events "file://$CLOUD_TMP/aws-log-events.json"
aws logs filter-log-events --region "$AWS_REGION" \
--log-group-name "$GROUP" --filter-pattern "$TRACE_ID" --limit 20查询结果应包含固定 TRACE_ID。刚写完短暂为空时先考虑摄取延迟,再核对 region、account、group、事件时间戳和调用身份。AccessDeniedException 则先用 aws sts get-caller-identity 确认真实身份,再检查 IAM policy、permission boundary、SCP、KMS key policy 与资源 ARN。给调用者临时加管理员权限只会掩盖最小权限缺口。
sequence token 已经不是并发协议
旧客户端常把每个 stream 的写入串行化,等待前一次响应中的 nextSequenceToken。现行 PutLogEvents API 已忽略 sequence token,同一 stream 可以并行调用;响应字段即使还出现,也不应继续充当发送器的锁。
并发放开并不等于批次没有约束。事件时间顺序、批次大小、单条大小和可接受时间范围仍要由 SDK 或发送器处理。API 返回成功也只证明服务接纳批次,长期验证还要确认查询可见、拒绝事件信息为空,并记录发送失败率与最老缓冲年龄。
统一 CloudWatch Agent 的责任边界
旧 CloudWatch Logs agent 已弃用,新接入应使用统一 CloudWatch Agent。EC2 instance profile 可以先按官方托管策略验证,再收敛为目标 group 上的写权限。若保留期由基础设施流水线管理,就不要给每台业务主机 logs:PutRetentionPolicy,也不要允许它创建任意 group。
将以下片段放入 /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json,固定 group 并用实例 ID 区分 stream:
{
"agent": {
"run_as_user": "cwagent"
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/checkout/app.jsonl",
"log_group_name": "/tool-lab/checkout/dev",
"log_stream_name": "{instance_id}",
"timezone": "UTC",
"retention_in_days": 7
}
]
}
}
}
}sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config -m ec2 -s \
-c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a status状态应为 running,但这还不够。cwagent 用户必须能穿过目录并读取目标文件;不能为了采集方便把应用日志设为全员可读。路径、group、stream、时区和 multiline 起始规则都会改变最终事件。错误 multiline 可能把一条堆栈拆成多条计费事件,也可能把后续正常日志吞进一条超大事件。
Agent 自身日志、目标文件权限和 API 错误是第一证据。配置中若同一 group 出现不一致的 retention_in_days,Agent 会拒绝设置并停止,这类问题应在配置发布前验证。更完整的字段语义可查 CloudWatch Agent 配置参考。
保留、订阅和成本要放在一张图上
CloudWatch Logs 的费用不只来自归档存储。摄取、Logs Insights 扫描、数据保护、跨账号集中、订阅输出和目标服务都可能计费。每个 group 应能关联 workload、环境、owner 和成本归属,并持续观察每日摄取字节、查询扫描字节、订阅失败与保留变化。
订阅到 Kinesis、Firehose 或 Lambda 时,CloudWatch Logs 需要调用目标资源,目标侧还要有容量和失败处理。跨账户、跨区域和互联网出口会一起改变 IAM、延迟与费用。若需求只是长期归档,先评估批量导出对象存储,不要把实时订阅当默认答案。
服务配额按区域、账户、API 和订阅类型变化。容量设计应从目标账户的 Service Quotas 读取实际额度,为节流准备有限缓冲和降级,而不是把文档中的某个默认值永久写进架构。
清理必须重新证明所有权
删除 log group 会删除其中全部 stream 与事件,因此它只适合这个唯一命名、合成数据的实验。执行前重新读取账户、owner tag 和目标 stream;任一条件不一致都停止:
test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_ACCOUNT_ID"
test "$(aws logs list-tags-log-group --region "$AWS_REGION" --log-group-name "$GROUP" \
--query 'tags."log-lab-owner"' --output text)" = "$LAB_ID"
test "$(aws logs describe-log-streams --region "$AWS_REGION" --log-group-name "$GROUP" \
--log-stream-name-prefix "$STREAM" \
--query "logStreams[?logStreamName=='$STREAM'].logStreamName | [0]" --output text)" = "$STREAM"
aws logs delete-log-group --region "$AWS_REGION" --log-group-name "$GROUP"
rm -f -- "$CLOUD_TMP/aws-log-events.json"
rmdir -- "$CLOUD_TMP"生产回滚不是删除 group。应先停止新订阅或恢复旧 Agent 配置,保留原 group 直到查询、归档和审计窗口结束。关键日志还应启用删除保护,并把解除保护和删除拆成可审计的高权限动作。这样 CloudWatch Logs 才是明确的云内日志产品,而不是一个谁都能写、没人敢删的无限收件箱。
