Skip to content

docs: add Google Analytics - #502

Merged
shenxianpeng merged 2 commits into
mainfrom
chore/google-analytics
Oct 18, 2026
Merged

shenxianpeng merged 2 commits into
mainfrom
chore/google-analytics

Conversation

@shenxianpeng

@shenxianpeng shenxianpeng commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

These docs are served from the cpp-linter.github.io origin, but unlike the organization site they have no Google Analytics. So there is no record of what readers look at once they leave the organization's home page. This PR adds the same property the organization site uses (G-JKH4SXC7HC). It also adds the maintainer flag from cpp-linter/cpp-linter.github.io#83, so the maintainers' own reading can be filtered out.

This PR only changes the site. The Google Analytics side (the Internal Traffic filter, key events) is handled separately.

Before / After (改动前 / 改动后)

Before (live site, origin/main) After (this branch)
Google Analytics No gtag on any page G-JKH4SXC7HC on every page
Maintainer visits — traffic_type=internal while the cpp-linter-internal flag is set (?internal=1 / ?internal=0 on any page of the origin)
404 page <title> Cpp-Linter (same as the home page) Page not found - Cpp-Linter

The overrides live in docs/overrides/, next to docs/docs/, so they are not published as pages.

Evidence: built <head> of index.html

<script>
  try {
    var param = new URLSearchParams(location.search).get("internal");
    if (param === "1") localStorage.setItem("cpp-linter-internal", "1");
    if (param === "0") localStorage.removeItem("cpp-linter-internal");
    if (localStorage.getItem("cpp-linter-internal") === "1") {
      window.dataLayer = window.dataLayer || [];
      (function () { dataLayer.push(arguments); })("set", { traffic_type: "internal" });
    }
  } catch (e) {
    // localStorage is unavailable when storage is blocked; the visit is then not tagged.
  }
</script>
<script id="__analytics">function __md_analytics(){ /* … the theme's snippet, unchanged … */ var t=document.createElement("script");t.async=!0,t.src="https://www.googletagmanager.com/gtag/js?id=G-JKH4SXC7HC",document.getElementById("__analytics").insertAdjacentElement("afterEnd",t)}</script>

I did not run the full docs build locally, because it compiles the Rust CLI generator. The theme changes were built with the same mkdocs 1.6.1 / mkdocs-material 9.7.7 pins from docs/pyproject.toml, with a temporary config that inherits docs/mkdocs.yml and leaves out the gen-files plugins and the other nav pages. The Build docs job in this PR runs the full build.

Notes

  • The flag snippet runs in the theme's analytics block, before the theme's own snippet. That way gtag('set', {traffic_type: 'internal'}) is queued ahead of the first page_view. The same snippet was tested end to end with the real gtag.js and a fake measurement ID in feat: tag maintainer visits, track code copies and add social cards cpp-linter.github.io#83: tt=internal is sent after ?internal=1 and stops after ?internal=0.
  • None of these changes alters a page's layout, so there are no screenshots.
  • cspell.config.yml gains htmltitle (a Jinja block name in docs/overrides/404.html).

Summary by CodeRabbit

  • Documentation
    • Added a custom “404 - Not found” page to the documentation site.
    • Configured Google Analytics for the documentation site, including support for distinguishing internally tagged visits.
    • Updated the documentation theme configuration to load custom overrides.

- Send page views to the Google Analytics property that
  cpp-linter.github.io already uses. These docs share its origin, so
  one property covers the whole site.
- Tag maintainer visits with traffic_type=internal when the
  cpp-linter-internal flag is set (?internal=1 on any page of the
  site), so the Internal Traffic data filter can drop them.
- Title the 404 page "Page not found" so missing pages do not show up
  under the home page's title.
@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c87be8f2-3d98-451e-b074-0b04a489d255
📥 Commits

Reviewing files that changed from the base of the PR and between d8c28eb and 973201d.

📒 Files selected for processing: 4
  • cspell.config.yml
  • docs/mkdocs.yml
  • docs/overrides/404.html
  • docs/overrides/main.html

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

The documentation configuration now loads theme overrides and Google Analytics. The main override handles internal-traffic classification. A custom 404 template renders a page-specific title and heading, and the spell-checker word list includes htmltitle.

Changes: Documentation analytics and templates

Layer / File(s) Summary
Configure analytics and internal-traffic handling
docs/mkdocs.yml, docs/overrides/main.html
MkDocs configures the override directory and Google Analytics property. The main template updates the internal-traffic flag from the internal query parameter and queues traffic_type=internal when the flag is set.
Render the 404 page
docs/overrides/404.html, cspell.config.yml
The 404 template extends main.html, sets a title using the site name, and renders a “404 - Not found” heading. The spell-checker word list adds htmltitle.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 97320

This change adds Google Analytics internal-traffic tagging and a custom 404 page. The inspected templates match the pinned theme behavior, and no actionable merge-blocking risk remains.

Pre-merge checks: Passed 5
Check name Status Explanation
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely identifies the primary change: adding Google Analytics to the documentation site.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (4 skipped: 4 unsupported.)

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@2bndy5 2bndy5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, I have an DNS sink hole filtering out most HTTP requests for ads and analytics across my entire network. It works great for blocking full-page ads in mobile apps 😉. So, this analytics stuff is all you; I can't even log in to Google Analytics site without explicitly whitelisting the domain...

@shenxianpeng

Copy link
Copy Markdown
Member Author

Haha, makes sense 😄 No worries, I’ll take care of the analytics side. I have added you to the GA property just in case. if you can not visit it pls let me know.

@shenxianpeng
shenxianpeng merged commit 1bce8f1 into main Oct 18, 2026
24 checks passed
@shenxianpeng
shenxianpeng deleted the chore/google-analytics branch October 18, 2026 21:00
shenxianpeng added a commit to cpp-linter/cpp-linter.github.io that referenced this pull request Oct 18, 2026
)

Follow-up to a review of this site's Google Analytics and Search Console
data. Over the last 90 days roughly 55% of users came from data-center
cities with 0 s engagement. Maintainer visits were about 21% of engaged
sessions and half of the `click_github` key events. Nothing recorded
that a reader copied a workflow or hook. Links shared in chats had no
preview card, and crawlers asking for `/robots.txt` landed on the 404
page.

This PR only changes the site. The Google Analytics side (turning the
Internal Traffic filter on, key events, custom dimensions) and Search
Console are handled separately.

## Before / After (改动前 / 改动后)

None of these changes alters a page's layout. The only new visual is the
social card, shown below.

### Behaviour

| | Before (live site, `origin/main`) | After (this branch) |
|---|---|---|
| Maintainer visits | Counted like any other visit | `?internal=1`
stores a flag in `localStorage`; from then on every event carries
`traffic_type=internal` (sent as `tt=internal`). `?internal=0` removes
it. The project docs under `/cpp-linter-action/` etc. read the same
flag. |
| Copying a code block | No event | `copy_code` event with `snippet`
(section id, or `cl-workflow` for the home page block) and `code_title`
(the file name above the block) |
| Open Graph / Twitter tags | 0 on every page | 13 on every page, with a
1200×630 card |
| 404 page `<title>` | `cpp-linter` (same as a real page) | `Page not
found - cpp-linter` |
| `/robots.txt` | 404 (served the 404 page, which was then counted as a
page view) | 200, `Allow: /` plus the sitemaps of this site and the four
project docs |
| Home page `<title>` | `C/C++ Linting - cpp-linter` | `C/C++ Linter
with clang-format and clang-tidy - cpp-linter` |

### Old URLs that Google Analytics recorded as 404s

`hooks/redirects.py` writes a small redirect page for each one after the
build. Each page has a meta refresh, `location.replace`, a canonical
link and `noindex`.

| Path | Before | After |
|---|---|---|
| `/docs/` | 404 | → `/getting-started/` |
| `/merch/` | 404 | → `/sponsor/#t-shirt-perk` |
| `/inputs-outputs/`, `/examples/`, `/permissions/`,
`/pr-review-caveats/` | 404 | → the same page under
`/cpp-linter-action/` |
| `/cli_args` | 404 | → `/cpp-linter/cli_args.html` |
| `/cpp_linter_rs/`, `/cpp_linter_rs/cli.html` | 404 | →
`/cpp-linter-rs/`, `/cpp-linter-rs/cli/` |

### Evidence: events sent by the real gtag.js

Built this branch with the measurement ID swapped for a fake one
(`G-TESTTEST00`), so nothing reached the real property. I served it
locally and recorded every `g/collect` request in a browser while going
through the pages in this order:

| # | Page opened (in order) | Event | `tt` (traffic_type) |
`ep.snippet` | `ep.code_title` |
|---|---|---|---|---|---|
| 1 | `/getting-started/` (no flag) | page_view | — | | |
| 2 | same page, copy button in "On every pull request" | copy_code | —
| `on-every-pull-request` | `.github/workflows/cpp-linter.yml` |
| 3 | `/getting-started/?internal=1` | page_view | `internal` | | |
| 4 | same page, copy button in "Before every commit" | copy_code |
`internal` | `before-every-commit` | `.pre-commit-config.yaml` |
| 5 | `/` (no parameter, flag still stored) | page_view | `internal` | |
|
| 6 | home page "Copy" button | copy_code | `internal` | `cl-workflow` |
`.github/workflows/cpp-linter.yml` |
| 7 | `/blog/?internal=0` | page_view | — | | |
| 8 | `/blog/` | page_view | — | | |

### Evidence: built `<head>` of the home page

Before (live):

```html
<title>C/C++ Linting - cpp-linter</title>
<!-- no og:* or twitter:* tags -->
```

After (`mkdocs build --strict`):

```html
<title>C/C++ Linter with clang-format and clang-tidy - cpp-linter</title>
<meta property="og:type" content="website">
<meta property="og:site_name" content="cpp-linter">
<meta property="og:title" content="C/C++ Linter with clang-format and clang-tidy">
<meta property="og:description" content="clang-format and clang-tidy for C and C++ pull requests, pre-commit hooks and CI, with the LLVM version you pin.">
<meta property="og:url" content="https://cpp-linter.github.io/">
<meta property="og:image" content="https://cpp-linter.github.io/static/og-image.png">
<meta name="twitter:card" content="summary_large_image">
```

### Social card

Before: no card. After:

![Social
card](https://raw.githubusercontent.com/cpp-linter/cpp-linter.github.io/pr-assets/feature-analytics-and-seo/og-card-after.png)

## Notes

- The flag snippet runs in the `analytics` block, before Material's own
snippet. That way `gtag('set', {traffic_type: 'internal'})` is queued
ahead of the first `page_view`, which rows 3 and 5 above confirm.
- To report on `snippet` and `code_title`, they need to be registered as
event-scoped custom dimensions in Google Analytics. That happens outside
this PR.
- `robots.txt` lists `/cpp-linter/sitemap.xml`, which is added by
cpp-linter/cpp-linter#237. Until that one is deployed, that URL returns
404 to crawlers, which is harmless.
- Companion PRs add the same Google Analytics property and the same flag
to the project docs: cpp-linter/cpp-linter-action#504,
cpp-linter/clang-tools-pip#229, cpp-linter/cpp-linter-rs#502 and
cpp-linter/cpp-linter#237.

## After merging

Open `https://cpp-linter.github.io/?internal=1` once on each device and
browser you use. To check it is working, wait about a day, open any GA4
report, add the comparison "Test data filter name = Internal Traffic"
(it exists while the filter is in Testing), and confirm your own visits
show up there. Only then should the filter be switched to Active.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants