没有不值得去解决的问题,也没有不值得去学习的技术!

A Tour of Go 多语言翻译项目:接入 Google Analytics 与百度统计,从 systemd 注入到 EdgeOne 与真实上报验证

图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报

作者:

系列帖子

图 1:新生产域名 go-dev.shuijingwanwq.com 已正常访问,Go 示例远程运行成功

(1) A Tour of Go 多语言翻译项目:从 go-tour 到 go-dev,完成 EdgeOne、Nginx 与 HTTPS 生产域名迁移

图3:访问过去的 Go Tour 简体中文站 tour.go-zh.org 时,当前已经无法正常建立连接。

(2) 从「A Tour of Go 中文版」这个搜索词开始:我决定做一个持续维护的 Go Tour 中文版

本地运行的 A Tour of Go「Methods continued」课程页面,左侧为课程说明,右侧为 Go 代码编辑器及运行结果。

(3) A Tour of Go 中文版项目设计冻结:从 101 页到 103 页,从 Gin 转向 Cobra CLI

A Tour of Go 中文版开发实战:英文基线、在线运行与 101 页上游同步体系

(4) A Tour of Go 中文版开发实战:英文基线、在线运行与 101 页上游同步体系

go-tour-i18n 项目完成首个简体中文课程页面 welcome/1 的整页翻译、结构校验和本地预览。

(5) A Tour of Go 多语言翻译项目实录:完成首个 zh-CN 页面翻译闭环

图 1:DeepSeek 官方更新日志显示,本次只更新 DeepSeek-V4-Flash,DeepSeek-V4-Pro API 与 APP、Web 模型均未更新

(6) A Tour of Go 中文翻译项目进展:完成前 8 页、修复 present 语法,并暂缓 DeepSeek 对比

图 1:generics/1 简体中文页面本地预览

(7) 一个页面重试五次:使用 GLM-5.2 翻译 A Tour of Go 时遇到的 Token 与 Present 结构问题

图 1:methods/16 简体中文页面浏览器预览

(8) A Tour of Go 中文翻译实录:如何只翻译教学代码注释,而不破坏 Go 代码

图 2:A Tour of Go methods/20「练习:错误」中文候选页面最终渲染效果

(9) A Tour of Go 多语言翻译:当正确的中文语序被受保护标记顺序校验误判

图 3:methods/24 中文页面,静态代码、Bounds 行内代码和链接内的 image.Rectangle 均正确渲染

(10) A Tour of Go 中文翻译完成 7 个代表页校准:最后 3 页又发现了哪些真实问题

图 2:首批 10 个普通页面经过试跑与流程校准后全部进入 ready

(11) A Tour of Go 多语言翻译项目:首批 10 个普通页面试跑,从 2 个 blocked 到全部 ready

图 2:flowcontrol/6 最终中文页面,return 与 v 为适应自然中文语序发生整体换位

(12) A Tour of Go 中文翻译第二批实跑:从 Inline Code 顺序误判到历史响应重新验证

图 1:flowcontrol/6 首次原始输入实验直接通过统一自动校验

(13) A Tour of Go 多语言翻译项目:从 swap 问题重新审视 Token 保护,原始输入与最小保护的一次真实实验

图 7:moretypes/1 最终中文页面预览。左侧课程正文、静态代码块、行内代码和教学注释均正常渲染,右侧官方 Go 示例继续保持原样。

(14) A Tour of Go 多语言翻译实战:第三批 10 页全部 Ready,继续校准 Protected Token 的结构角色

图 4:上游源码确认共有 103 个课程页面,zh-CN 最终达到 ready=103、pending=0、blocked=0

(15) A Tour of Go 多语言翻译项目:zh-CN 103 个课程页面全部 ready,课程正文翻译阶段完成

图 4:GitHub 与 ChatGPT 已成功连接

(16) ChatGPT 连接 GitHub 实测:让 AI 直接读取 go-tour-i18n 仓库参与译文审核

图 3:中文 Go 术语校准完成后,将确定的译法写入 zh-CN glossary

(17) 为什么 channel 最终选择“通道”:A Tour of Go 中文术语统一的一次实践

图 1:技术含义基本正确,但“返回一个返回……”已经明显影响阅读,因此仍被列入 C 类修订

(18) 103 页逐页审核后,只返修 16 页:A Tour of Go 中文正文发布前质量审计

图 2:中文课程页面以及已经完成本地化的编辑器控制区

(19) A Tour of Go 多语言翻译项目:公共 UI 本地化完成,从课程译文走向完整中文界面

图 2:完整 zh-CN 正式投影成功生成,103 个课程页面被重新组装为 7 个 .article

(20) 从 103/103 ready 到完整站点验收:A Tour of Go 中文版补齐正式投影与课程元数据本地化

图 3:从 production bundle 独立启动的 A Tour of Go 简体中文页面,课程正文、公共 UI 和官方 Go 示例均已进入最终发布形态。

(21) A Tour of Go 多语言翻译项目:103 页全部完成后,我终于生成了可独立部署的 zh-CN Production Bundle

图 1:A Tour of Go 简体中文版正式生产页面,远程运行已经成功输出“Hello, 世界”。

(22) A Tour of Go 多语言翻译项目:zh-CN 正式上线,从生产发布到 go.dev Playground 的完整部署记录

图 1:A Tour of Go 多语言翻译项目正式生产首页

(23) A Tour of Go 多语言翻译项目正式上线:从项目首页、公共 Footer 到生产发布元数据的完整收尾

图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报

(24) A Tour of Go 多语言翻译项目:接入 Google Analytics 与百度统计,从 systemd 注入到 EdgeOne 与真实上报验证

A Tour of Go 简体中文站完成正式部署以后,我开始补齐生产环境的访问统计。

这次准备接入两套已经在博客中长期使用的统计服务:Google Analytics 4 和百度统计。

一开始看,这件事似乎很简单:把两段 JavaScript 放进页面 <head> 就可以了。

但真正开始处理以后,发现还涉及不少生产环境问题:

  • Go Tour 现有代码是否已经预留统计代码入口;
  • 本地预览与生产环境是否走相同的初始化逻辑;
  • 首页 //tour/... 是否共用同一个模板;
  • 统计代码应该直接写进模板,还是通过生产环境配置注入;
  • systemd 应该怎样持久加载包含 HTML 和 JavaScript 的环境变量;
  • 新版程序上线后,EdgeOne 会不会继续返回旧 HTML;
  • 最后怎样证明统计代码不仅“出现在网页源码里”,而且真的向 Google 和百度完成了数据上报。

最终我没有为统计功能另外设计一套配置系统,而是继续利用项目原来就存在的 TOUR_ANALYTICS,并把验收分成三个层次:

Plaintext
源站
→ EdgeOne 公网
→ 浏览器真实上报
图 1:已经正式运行的 A Tour of Go 简体中文站点
图 1:已经正式运行的 A Tour of Go 简体中文站点

一、项目原来已经存在 AnalyticsHTML

首先需要确认的是:现有 Go Tour 到底有没有统计代码注入机制。

搜索代码以后,可以看到共享 Tour 模板 _content/tour/template/index.tmpl 中本来就存在:

Plaintext
{{.AnalyticsHTML}}

继续沿着 AnalyticsHTML 往上追踪,最终得到生产环境的大致调用链:

Plaintext
cmd/tour-i18n publish
→ 构建 cmd/tour-production
→ tour.NewProductionHandler(...)
→ RegisterHandlersLocale(...)
→ analyticsHTML = template.HTML(os.Getenv("TOUR_ANALYTICS"))
→ initTour(...)
→ renderIndex(...)
→ {{.AnalyticsHTML}}

也就是说,项目原本已经设计了一套生产统计代码入口:

TOUR_ANALYTICS

生产进程启动时读取这个环境变量,再把里面的内容作为 HTML 注入页面 <head>

这里还有一个容易造成误解的地方:读取 TOUR_ANALYTICS 的逻辑位于 internal/tour/appengine.go

但现在的 A Tour of Go 中文站并没有运行在 Google App Engine 上。

当前生产链路仍然是:

Plaintext
EdgeOne
→ Nginx
→ 127.0.0.1:3999
→ Go production binary

appengine.go 只是上游项目历史演进留下来的文件名。

这也再次说明,在维护一个基于上游项目改造而来的仓库时,不能仅凭文件名判断实际生产行为,还是应该沿着真正的调用关系继续追踪。


二、本地开发默认不加载生产统计代码

项目中的普通本地运行使用 local.go

这里定义的是一个默认空值:

Go
var analyticsHTML template.HTML

本地预览不会主动读取 TOUR_ANALYTICS

而 production 初始化时才会执行:

Go
analyticsHTML = template.HTML(os.Getenv("TOUR_ANALYTICS"))

这正好符合目前项目的需求:

Plaintext
本地开发
→ 默认没有生产统计

正式生产
→ 通过 TOUR_ANALYTICS 开启统计

因此没有必要再引入 .env 解析库,也没有必要增加新的命令行参数或者配置系统。

统计仍然只是一个生产部署配置。


三、真正缺失的是首页 /

继续审计以后发现,现有统计入口其实还不完整。

Tour 课程页面使用 _content/tour/template/index.tmpl,其中已经包含:

Plaintext
{{.AnalyticsHTML}}

但独立首页 / 使用的是另外一个模板:

_content/tour/template/home.tmpl

这个模板原来没有 AnalyticsHTML

也就是说,如果此时直接给 production 设置 TOUR_ANALYTICS,结果会是:

Plaintext
/tour/...
→ 有统计代码

/
→ 没有统计代码

对于一个完整的独立站点来说,这显然不理想。

因此这次真正需要修改的 Go 项目代码其实非常少:让首页模板也复用已经存在的 AnalyticsHTML

对应提交为:

cac4d6e feat: 为首页接入统一统计代码

修改后,首页 / 与课程页面 /tour/... 使用同一个 TOUR_ANALYTICS

同时增加自动测试,分别覆盖两种状态:

Plaintext
analyticsHTML 为空
→ 首页和 Tour shell 都没有统计代码

analyticsHTML 已配置
→ 首页和 Tour shell 都恰好注入一次

这里没有增加第二套环境变量,也没有把 Google Analytics 或百度统计代码硬编码进模板。


四、百度统计继续与博客共用

我的 WordPress 中文站和英文站原来已经共用同一套百度统计代码。

在百度统计后台的“受访域名”报告中,可以分别看到:

  • www.shuijingwanwq.com
  • en.shuijingwanwq.com

也就是说,即使多个子域共用一套百度统计代码,仍然可以按照受访域名拆分查看数据。

因此对于新的:

go-dev.shuijingwanwq.com

我最终没有新建一个独立的百度统计站点,而是继续复用博客现有的百度统计代码。

这样以后既可以查看所有站点的总体数据,也可以通过受访域名分别分析中文博客、英文博客和 A Tour of Go 中文站。

Google Analytics 也采用了相同思路,继续使用博客现有的 GA4 配置。

对我当前的使用场景来说,这比维护多套统计账号和多个独立配置更简单。


五、没有采用普通 .env,而是使用 systemd EnvironmentFile

接下来需要解决的是生产服务器上的配置存放问题。

Go 程序读取的是标准环境变量:

Go
os.Getenv("TOUR_ANALYTICS")

它本身并不会自动读取项目目录中的 .env 文件。

而生产服务本来就由 systemd 管理,因此没有必要为了一个统计变量,再给 Go 项目增加 .env 加载代码。

服务器当前使用的 systemd 版本为 239,已经支持 EnvironmentFile=

所以我最终建立了专用环境变量文件:

/etc/go-tour/go-tour.env

权限设置为:

Plaintext
root:root
600

然后通过 systemd drop-in:

/etc/systemd/system/go-tour.service.d/analytics.conf

加载这个文件:

INI
[Service]
EnvironmentFile=/etc/go-tour/go-tour.env

最终形成的生产配置关系很清楚:

Plaintext
/etc/go-tour/go-tour.env

systemd EnvironmentFile

TOUR_ANALYTICS

Go production binary

{{.AnalyticsHTML}}

首页 / + /tour/... 页面

Google Analytics 和百度统计两套代码都放在同一个 TOUR_ANALYTICS 中。

实际统计代码和具体统计 ID 不进入 Git 仓库。


六、为什么这个环境变量仍然需要严格控制权限

统计 ID 本身最终会出现在网页源码中,并不是严格意义上的服务器秘密。

TOUR_ANALYTICS 本身却值得更谨慎地管理。

原因在于程序执行的是:

Go
template.HTML(...)

也就是说,Go 模板明确把这个环境变量里的内容当作可信 HTML,不会按照普通字符串再次转义。

这是统计代码能够正常注入 <script> 的基础,但同时也意味着:

谁能够修改 TOUR_ANALYTICS,谁实际上就能够向生产页面 <head> 注入任意 HTML 或 JavaScript。

因此,把环境变量文件设置为:

Plaintext
root:root
600

是合理的。

这也是为什么我没有把完整统计代码直接放进 Git,也没有把它作为普通启动参数写在命令行里。


七、先验证 systemd → Go → Tour 页面

systemd drop-in 配置完成以后,先执行:

Bash
systemctl daemon-reload
systemctl restart go-tour.service

然后从源站本机直接请求课程页面。

结果确认:

Plaintext
Google ID count: 2
Baidu ID count: 1

Google ID 出现两次是正常的,因为标准 GA4 代码中通常会分别出现在标签加载地址和 gtag("config", ...) 中。

百度统计 ID 出现一次。

这一步证明:

Plaintext
EnvironmentFile
→ TOUR_ANALYTICS
→ production binary
→ Tour shell

整条链路已经正常。


八、重新生成 production release,让首页也获得统计代码

不过此时生产服务器运行的还是旧 binary。

旧版已经支持 /tour/...TOUR_ANALYTICS,但还没有包含刚刚为首页 / 增加的统计入口。

因此接下来重新生成了一份 production bundle:

Plaintext
/tmp/go-tour-release-20260812-zh-CN-cac4d6e

构建结果仍然保持:

Plaintext
locale=zh-CN
ready=103
pending=0
blocked=0
pages=103
articles=7

同时检查完整的 SHA256SUMS,所有文件均通过。

发布包里的两个模板也分别确认包含:

Plaintext
{{.AnalyticsHTML}}

之后把新 bundle 上传至:

/data/go-tour/releases/20260812-zh-CN-cac4d6e

先在独立 release 目录中完成服务器端 SHA-256 校验和 go-tour 用户访问权限检查,没有马上切换 production。

确认全部通过以后,才把:

/data/go-tour/current

切换至新 release,并重启 go-tour.service

服务状态正常:

active

随后重新检查源站首页 //tour/welcome/1

Plaintext
/                       Google=2 Baidu=1
/tour/welcome/1         Google=2 Baidu=1

至此,Go 服务本身已经完成统计接入。

图 2:生产服务、systemd 环境变量以及源站与 EdgeOne 公网页面的统计代码最终验收
图 2:生产服务、systemd 环境变量以及源站与 EdgeOne 公网页面的统计代码最终验收

九、源站已经更新,EdgeOne 却仍然返回旧 HTML

这次部署中最值得记录的问题,反而出现在 CDN 层。

源站已经明确包含 Google Analytics 和百度统计代码,但通过公网域名访问时却得到:

Plaintext
Google ID count: 0
Baidu ID count: 0

继续查看响应头以后,发现:

Plaintext
EO-Cache-Status: HIT
Age: 4727

这意味着问题并不在 Go 服务,也不在 TOUR_ANALYTICS

而是 EdgeOne 仍然在返回大约一个多小时前缓存的旧 HTML。

这也是一个很典型的生产环境问题:

源站验证成功,并不代表公网用户已经看到新版本。

尤其这次修改的是 HTML shell,而不是带版本号的静态 JS 或 CSS。

如果 CDN 缓存仍然命中,统计代码即使已经在源站上线,真实访客仍然不会获得它。

因此我在 EdgeOne 中执行了 go-dev.shuijingwanwq.com 的 Hostname 级缓存清除。

没有只刷新 / 或某一个课程页面,因为整个 /tour/... 下已经存在大量可能被缓存的 URL。

清除以后再次测试,响应变成:

Plaintext
EO-Cache-Status: MISS

并且公网 HTML 已经变为:

Plaintext
Google=2
Baidu=1

随后部分请求重新出现 HIT 也完全正常,因为此时缓存的已经是包含统计代码的新版页面。

这次最终整理出的生产验收摘要为:

Plaintext
Release: 20260812-zh-CN-cac4d6e
Service: active
EnvironmentFile: /etc/go-tour/go-tour.env

Source /                  Google=2 Baidu=1
Source /tour/welcome/1    Google=2 Baidu=1
Public /                  Google=2 Baidu=1
Public /tour/welcome/1    Google=2 Baidu=1

这一步让我把统计部署验收明确拆成了两层:

Plaintext
源站正确

公网一定正确

CDN 必须单独验证。


十、代码出现在 HTML 中,还不代表统计真的工作了

到这里,其实仍然只能证明:

Google Analytics 和百度统计代码已经进入访客拿到的 HTML。

但统计服务是否真的工作,还需要浏览器运行 JavaScript 后才能确认。

因此最后一层验收放在 Firefox 开发者工具的 Network 面板中完成。


十一、Google Analytics:真实 g/collect 返回 204

首先过滤:

collect

可以看到请求发往:

analytics.google.com/g/collect

请求中包含当前 GA4 的 tid,同时 dl 参数已经正确记录:

https://go-dev.shuijingwanwq.com/tour/welcome/1

其中一次请求是 90% 页面滚动事件:

Plaintext
en=scroll
epn.percent_scrolled=90

响应状态:

204

这就不再只是“Google 标签加载成功”。

它证明浏览器已经真正执行 GA4 代码,并把 A Tour of Go 中文站上的实际用户事件发送给 Google Analytics。

图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报
图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报

十二、百度统计:hm.gif 返回 200

随后把 Network 过滤条件改成:

hm.baidu

可以看到两类主要请求:

  • hm.js
  • hm.gif

其中 hm.js 是百度统计脚本,而 hm.gif 则包含实际统计上报。

请求参数中同样可以看到当前页面:

https://go-dev.shuijingwanwq.com/tour/welcome/1

响应状态为:

200 OK

因此百度统计也完成了真正的数据上报。

图 4:百度统计 hm.gif 请求返回 HTTP 200,并携带当前课程页面地址,确认百度统计已经完成真实数据上报
图 4:百度统计 hm.gif 请求返回 HTTP 200,并携带当前课程页面地址,确认百度统计已经完成真实数据上报

十三、最终形成三层统计验收

经过这次部署以后,我把生产统计功能的验收方式固定成了三个层次。

第一层是源站。

直接检查:

Plaintext
http://127.0.0.1:3999/
http://127.0.0.1:3999/tour/welcome/1

这里验证的是:

Plaintext
systemd
→ EnvironmentFile
→ TOUR_ANALYTICS
→ Go 模板

第二层是公网。

检查:

Plaintext
https://go-dev.shuijingwanwq.com/
https://go-dev.shuijingwanwq.com/tour/welcome/1

这里除了统计代码本身,还要注意:

Plaintext
EO-Cache-Status
Age

避免 CDN 仍然返回旧 HTML。

第三层才是浏览器真实请求。

Google Analytics 看:

analytics.google.com/g/collect

百度统计看:

hm.baidu.com

最终确认响应状态分别正常。

这样得到的结论比单纯“网页源码里有 <script>”可靠得多。


十四、把生产配置写进运维文档

全部验证完成以后,我又把这套配置补进了:

docs/PRODUCTION_RUNBOOK.md

对应提交:

0759457 docs: 补充生产统计配置与验收流程

文档现在明确记录了:

  • TOUR_ANALYTICS 的统一统计入口;
  • /etc/go-tour/go-tour.env
  • systemd EnvironmentFile drop-in;
  • 首页 //tour/... 共用统计机制;
  • 实际统计 ID 与完整代码不进入 Git;
  • 修改环境变量后的服务重启要求;
  • EdgeOne HTML 缓存可能继续返回旧页面;
  • “源站 → 公网 → 浏览器”的三层验收方式;
  • Google g/collect 和百度 hm.gif 的真实上报验证。

这样即使以后迁移服务器、重建 systemd 服务或者重新部署 A Tour of Go,也不会因为配置只存在于服务器上而忘记统计系统的接入方式。


十五、这次真正解决的不是“加两段统计代码”

回头看,这次工作真正有价值的部分,并不是把 Google Analytics 和百度统计的 JavaScript 复制到网页里。

更重要的是把统计能力纳入了现有生产架构:

Plaintext
生产配置
→ systemd
→ Go 服务
→ 模板
→ EdgeOne
→ 浏览器
→ 第三方统计服务

而且每一层都有可以单独验证的结果。

最终形成的是:

Plaintext
本地开发
→ 默认没有生产统计

生产环境
→ EnvironmentFile 注入 TOUR_ANALYTICS

首页 /
→ 有统计

/tour/...
→ 有统计

EdgeOne
→ 已确认新版 HTML

Google Analytics
→ g/collect HTTP 204

百度统计
→ hm.gif HTTP 200

这比直接把统计脚本硬编码进模板多走了一些步骤,但长期维护会简单得多。

下一次需要替换统计代码时,不需要重新修改 Go 源码;迁移生产服务器时,也有明确的环境变量文件和 systemd 配置可以恢复。

而 CDN 是否已经拿到新版 HTML、浏览器是否完成真实上报,也都有固定的验收方法。

对于一个刚刚完成正式发布的 A Tour of Go 多语言项目来说,这算是又补齐了一块比较重要的生产基础设施。

A Tour of Go 中文翻译完成 7 个代表页校准:最后 3 页又发现了哪些真实问题

A Tour of Go 多语言翻译项目

本系列完整记录 A Tour of Go 多语言翻译项目从架构设计、整页翻译、结构保护、自动校验,到生产发布与后续维护的实际开发过程。

项目入口:
✅ 在线学习:A Tour of Go 简体中文版
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n

当前第一阶段已完成简体中文版本。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理