Summary
BlogFlow's HTTP metrics use low-cardinality route patterns (GET /posts/{slug}) for system health, but cannot answer "which posts are popular?" or "what content gets the most traffic?".
Proposed Solution
Two complementary, cloud-agnostic signals:
A. Prometheus counter
blogflow_content_views_total{type, slug} incremented on every successful content serve. Enables PromQL queries like "top 10 posts this week".
Labels:
type: post, page, tag, list, home
slug: actual slug/tag for posts/pages/tags; empty for list/home
Cardinality: ~1 series per content item (500 posts → 500 series — well within Prometheus comfort zone).
C. OTel span attributes
Added to the existing otelhttp request span:
| Attribute |
Type |
Example |
content.type |
string |
post |
content.slug |
string |
hello-world |
content.title |
string |
Hello World |
content.tags |
string[] |
["go","blog"] |
Works with any OTel-compatible backend (Jaeger, Tempo, Zipkin, Grafana Cloud, etc.).
Implementation
- New
internal/server/handlers/content_metrics.go with counter + RecordContentView() helper
- Wire to all 6 content handlers (PostHandler, PageHandler, TagHandler, PostsListHandler, ListHandler, HomeHandler)
- Zero config — lights up automatically when content is served
- Unit tests for counter increments and span attribute population
Acceptance Criteria
Summary
BlogFlow's HTTP metrics use low-cardinality route patterns (
GET /posts/{slug}) for system health, but cannot answer "which posts are popular?" or "what content gets the most traffic?".Proposed Solution
Two complementary, cloud-agnostic signals:
A. Prometheus counter
blogflow_content_views_total{type, slug}incremented on every successful content serve. Enables PromQL queries like "top 10 posts this week".Labels:
type:post,page,tag,list,homeslug: actual slug/tag for posts/pages/tags; empty for list/homeCardinality: ~1 series per content item (500 posts → 500 series — well within Prometheus comfort zone).
C. OTel span attributes
Added to the existing
otelhttprequest span:content.typepostcontent.slughello-worldcontent.titleHello Worldcontent.tags["go","blog"]Works with any OTel-compatible backend (Jaeger, Tempo, Zipkin, Grafana Cloud, etc.).
Implementation
internal/server/handlers/content_metrics.gowith counter +RecordContentView()helperAcceptance Criteria
blogflow_content_views_totalcounter increments on each content servego test ./...passes