Google Cloud Logging 工具手册
bucket、view 和 sink 不是三个存储层
Google Cloud Logging 接收的是写到项目、文件夹或组织范围的 LogEntry。资源上的 sinks 根据过滤器把新日志路由到 log bucket 或外部目标;log view 再限制调用者能读取 bucket 中的哪部分内容。应用并不是直接把事件写进某个 bucket。
这一区分会直接影响权限。roles/logging.logWriter 允许创建并路由日志条目,view accessor 或 viewer 负责读取,sink 的 writer identity 则要在目标资源上获得写权限。给应用项目级 Editor 既扩大攻击面,也让排障时无法判断真正缺失的是写、读、路由还是目标权限。可从官方 日志 bucket 说明、log view 与 IAM 角色 校准这些边界。
写入一条带所有权的合成日志
先选择获准使用且启用结算的测试项目,在 Linux Bash 中按官方安装说明准备 Google Cloud CLI。人机交互可以 gcloud auth login,工作负载应使用附加服务账号或 Workload Identity Federation;不要为了方便下载长期 JSON key。以下各段在同一专用终端执行,失败后先处理错误。
set -euo pipefail
gcloud auth login
gcloud config set project PROJECT_ID
gcloud auth list
PROJECT_ID=$(gcloud config get-value project)
test -n "$PROJECT_ID" && test "$PROJECT_ID" != "(unset)" \
|| { echo 'select an authorized test project first' >&2; exit 1; }
LAB_ID="$(date -u +%Y%m%d%H%M%S)-$RANDOM"
LOG_ID="checkout-dev-$LAB_ID"
TRACE_ID="TE-GCP-$LAB_ID"
gcloud logging write "$LOG_ID" --project "$PROJECT_ID" \
"{\"service\":\"checkout\",\"trace_id\":\"$TRACE_ID\",\"message\":\"payment timeout\"}" \
--payload-type=json --severity=ERROR --labels=log_lab_owner="$LAB_ID"
gcloud logging read --project "$PROJECT_ID" \
"logName=\"projects/$PROJECT_ID/logs/$LOG_ID\" AND jsonPayload.trace_id=\"$TRACE_ID\" AND labels.log_lab_owner=\"$LAB_ID\"" \
--freshness=1h --limit=20 --format=jsonPROJECT_ID 要替换为真实测试项目。写入成功通常没有业务输出,读取结果应同时包含 jsonPayload、logName、owner label、resource 与时间戳。空结果先检查活动账号、项目、logName URL 编码、freshness、resource scope 和权限。PERMISSION_DENIED 也要区分缺少 logging.logEntries.create 与缺少私有日志读取权限。
固定 TRACE_ID 证明这条事件可查,owner label 则为清理提供额外保护。它们不能替代平台侧的接收量、错误率和路由指标;一次成功写入也无法证明持续采集链可靠。
Ops Agent 要暴露自己的失败
Compute Engine 主机可以使用 Google Cloud Ops Agent,GKE 和托管服务则有各自接入路径。Agent 配置必须明确 receiver、processor 与 pipeline;结构化解析、排除和 multiline 都在发送前改变摄取字节与事件边界。
先按 Ops Agent 官方安装步骤在已有且获准使用的 Compute Engine 测试主机安装,核对操作系统支持、API 启用和实例访问范围。默认 Agent 同时采集日志与指标,服务账号通常需要 roles/logging.logWriter 与 roles/monitoring.metricWriter;若只授权日志,必须同时关闭默认指标 pipeline,不能只删掉 IAM 角色。
保留现有配置备份后,把下面日志 pipeline 合并进 /etc/google-cloud-ops-agent/config.yaml,不要覆盖正在使用的其他采集规则。该片段不关闭默认指标采集:
logging:
receivers:
checkout_json:
type: files
include_paths:
- /var/log/checkout/app.jsonl
record_log_file_path: true
processors:
parse_checkout_json:
type: parse_json
time_key: "@timestamp"
time_format: "%Y-%m-%dT%H:%M:%SZ"
service:
pipelines:
checkout_pipeline:
receivers: [checkout_json]
processors: [parse_checkout_json]# 合成文件只能创建在未被现有应用使用的路径。
sudo test ! -e /var/log/checkout || { echo 'refuse existing log directory' >&2; exit 1; }
sudo install -d -m 0750 /var/log/checkout
printf '{"@timestamp":"%s","message":"ops-agent-check","trace_id":"%s"}\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$TRACE_ID" \
| sudo tee /var/log/checkout/app.jsonl >/dev/null
sudo chmod 0640 /var/log/checkout/app.jsonl
sudo systemctl restart google-cloud-ops-agent
sudo systemctl status google-cloud-ops-agent --no-pager
sudo journalctl -u google-cloud-ops-agent --since "10 minutes ago" --no-pager服务状态应为 active (running)。从 CLI 查询 resource.type="gce_instance" AND jsonPayload.trace_id="<本次 TRACE_ID>",确认消息是 ops-agent-check、实例 ID 正确;这条事件的日志名与前面手工写入的 $LOG_ID 不同,不要沿用原来的 logName 条件。Agent 用户能否读取文件、时间字段能否解析、multiline 是否吞行、API 是否限流,都要从 Agent 自身日志和平台指标判断。开发者查询通常使用 roles/logging.viewer;读取 Data Access 等私有日志需要额外权限。bucket、view 和 sink 管理角色不应授予业务服务账号。实验结束恢复备份配置并重启 Agent,确认测试 pipeline 已退出;下面的 $LOG_ID 清理只删除手工日志,不涵盖 Agent 日志或下游副本。
sink 的 writer identity 是独立主体
Cloud Logging 的 sink 可以把新日志路由到另一个 log bucket、Cloud Storage、BigQuery 或 Pub/Sub。创建 sink 后会得到 writer identity,它必须在目标资源上获得最小写权限。来源项目中的应用身份即使能写日志,也不会自动获得目标 bucket 或 topic 权限。
这条路由是面向生效后的新事件,不会自动搬迁历史数据。迁移时应让新旧目标短时间双路接收固定样本,核对 writer identity、过滤器、数量、延迟和目标读取权限,再切消费者。旧 bucket 按原保留策略留存,不能因为新 sink 已创建就提前删除。
目标不可写、Pub/Sub 配额不足或过滤器错误时,路由失败会以平台日志和指标呈现。平台 owner 需要为 sink 错误与落后建立告警。只在目标系统里查“为什么没数据”,通常看不到来源路由的直接证据。
view 承担的是读取隔离
log view 让团队只访问 bucket 中的一部分日志。集中项目接收多个项目日志时,可以按来源、服务或数据级别创建 view,再把 roles/logging.viewAccessor 授给读者。直接在项目层给宽泛 Viewer,可能绕过原本想用 view 表达的最小访问边界。
读取权限与存储位置也不能混为一谈。日志已进入 bucket,不代表当前 principal 能通过指定 view 看见;view 过滤器错误也会产生“平台没有日志”的假象。排障记录应包含 resource scope、bucket location、bucket ID、view ID、查询过滤器与实际 principal。
日志中的 Authorization、Cookie、连接串、完整请求体和个人信息应该在应用或 Agent 侧删除。IAM 只能限制谁看,不能让已经摄取的敏感字段消失;后续删除还可能受保留、复制和下游 sink 影响。
配额与费用要从目标项目读取
Cloud Logging 对 LogEntry 大小、请求大小、区域写入速率、读取调用、bucket、sink 和过滤器都有配额或固定限制。超出区域摄取配额可能返回 RESOURCE_EXHAUSTED。排除过滤发生在写 API 接收之后时,不一定降低写入配额占用,因此“采集后再排除”不能替代源头降噪。
容量评审应打开目标项目的 Quotas 页面,读取真实额度,为突发流量设置采样、限流、有限缓冲和降级。SDK 无限重试会把云端节流转成节点内存或磁盘故障。
费用也不只来自摄取。超过默认保留的存储、Log Analytics、BigQuery/Storage/Pub/Sub 目标、跨区域网络和 sink 下游都会增加成本。大量离线分析不应反复耗尽在线读取 API;如果数据天然需要批量扫描,就提前路由到合适的分析目标,并让这个出口拥有明确预算与 owner。
删除实验日志前重新确认项目
删除一个日志名是破坏性操作。执行前核对当前项目、唯一前缀和本次 owner label;固定业务日志名不能进入这段命令:
(
set -euo pipefail
: "${PROJECT_ID:?}" "${LAB_ID:?}" "${LOG_ID:?}"
test "$LOG_ID" = "checkout-dev-$LAB_ID" || { echo 'refuse unexpected log' >&2; exit 1; }
CURRENT_PROJECT=$(gcloud config get-value project) || exit 1
test "$CURRENT_PROJECT" = "$PROJECT_ID" || { echo 'refuse changed project' >&2; exit 1; }
OWNER=$(gcloud logging read --project "$PROJECT_ID" \
"logName=\"projects/$PROJECT_ID/logs/$LOG_ID\" AND labels.log_lab_owner=\"$LAB_ID\"" \
--freshness=1h --limit=1 --format='value(labels.log_lab_owner)') || exit 1
test "$OWNER" = "$LAB_ID" || { echo 'refuse missing owner evidence' >&2; exit 1; }
gcloud logging logs delete "$LOG_ID" --project "$PROJECT_ID"
)变量缺失、身份或所有权不匹配、查询失败都会终止子 Shell。若实验超过一小时,先在明确的历史时间窗口查到本次合成事件,再调整查询窗口;不要删除 owner 检查来绕过空结果。日志名的删除语义可查 gcloud logging logs delete;sink 已导出的外部副本仍需按目标端策略管理。
生产退出应先停止或切换写入与 sink,验证新目标数量和读取权限,再让旧 bucket 按保留期自然退出。删除项目、bucket 或固定日志名都不是普通回滚动作。
权威资料与规范地址
- 日志 bucket 说明:https://cloud.google.com/logging/docs/buckets
- log view:https://cloud.google.com/logging/docs/logs-views
- IAM 角色:https://cloud.google.com/logging/docs/access-control
- 官方安装说明:https://cloud.google.com/sdk/docs/install
- Ops Agent 官方安装步骤:https://cloud.google.com/stackdriver/docs/solutions/agents/ops-agent/installation
- gcloud logging logs delete:https://cloud.google.com/sdk/gcloud/reference/logging/logs/delete
