Skip to main content

AsciiDoc Analytics: Track Your Documentation Performance

Learn how to track page views, scroll depth, and feedback in AsciiDoc documentation. Includes ready-to-use code snippets for HTML export and PDF measurement.

You can precisely measure the success of your documentation. To do this, you need two things:

  1. Clearly defined metrics.

  2. A clean technical implementation in your AsciiDoc setup.

In this article, I'll proceed step by step: First, we'll clarify which metrics are truly meaningful for your documentation, such as page views, reading time, scroll depth, PDF download rates, or clicks on specific call-to-actions.

Then we'll look at what technical prerequisites must be in place, for example a web analytics tool, clean URLs, consistent UTM parameters, and a clear structure for your AsciiDoc sources.

Finally, I'll show you how to concretely implement these metrics in AsciiDoc, for instance by integrating tracking links and event IDs directly into the source text, so they are automatically included in HTML, PDF, and other web exports.

Prerequisites for Tracking to Work

Before discussing metrics, make sure you have:

Unique URLs and IDs

  • Each documentation page has a stable URL.

  • Each page has a unique identifier (doc-id).

  • This identifier appears in the HTML, in the PDF (indirectly), and in events.

Central Template or Include

  • No tracking code in each AsciiDoc document individually.

  • A shared include or layout template that reads the doc-id and embeds analytics or logging code.

Analytics or Logging System

Typical options:

  • Web analytics (e.g., Matomo On-Prem, Plausible, GA4).

  • Custom backend (events via REST API).

  • Combination with support system (Zendesk, Freshdesk, etc.) for deflection.

Consent / Data Privacy

  • Proper consent when collecting personal or pseudonymous user data.

  • Ideally: self-hosted, data-minimal solution.

Metric Categories and How to Implement Them

Metric Effort Business Impact Requirements
Page Views ⭐⭐ Analytics
Unique Users ⭐⭐ Analytics
Scroll Depth ⭐⭐ ⭐⭐⭐ JS + Backend
On-Page Feedback ⭐⭐ ⭐⭐⭐⭐ Widget + Backend
Search & Findability ⭐⭐ ⭐⭐⭐ Search Logging
Task Success ⭐⭐⭐ ⭐⭐⭐⭐ Funnel Tracking
Ticket Deflection ⭐⭐⭐ ⭐⭐⭐⭐⭐ Support Integration
Internal Quality Metrics ⭐⭐ ⭐⭐⭐ CI/Linter
PDF Tracking (Links) ⭐⭐ Redirect URLs
PDF Tracking (QR) ⭐⭐ ⭐⭐ QR Generator

1. Page Views

What: How often a page was viewed. Basic metric for usage.

Technical Prerequisites

  • Stable URL per page.

  • Tracking snippet in the HTML template.

  • doc-id as unique identifier.

AsciiDoc Example

In the header of each page:

= Getting Started with Product X
:doc-id: getting-started
:doc-area: onboarding
:doc-version: 1.4

In the HTML template or as an include:

ifdef::backend-html5[]

++++
<script>
  (function() {
    // Provide attributes via meta tags or data attributes
    const docMeta = document.querySelector('meta[name="doc-id"]');
    const areaMeta = document.querySelector('meta[name="doc-area"]');
    const versionMeta = document.querySelector('meta[name="doc-version"]');
    
    const docId = docMeta ? docMeta.content : 'unknown';
    const area = areaMeta ? areaMeta.content : 'unknown';
    const version = versionMeta ? versionMeta.content : 'unknown';

    // Send pageview to your system
    // pageview({ page: window.location.pathname, docId, area, version });
  })();
</script>
++++

endif::[]

Important Note: AsciiDoc does not substitute attributes inside passthrough blocks (++++...++++) by default. Instead, use meta tags in the document header or data attributes on a container element, which you then read via JavaScript. See the appendix for details.

Where to Measure?

In your analytics under "Pages" by page or docId. You can:

  • Group views by doc-area (e.g., Onboarding, API, Admin).

  • Compare releases (doc-version).

  • Identify top and low performers.

You don't just see "lots of traffic," but clearly: which page, which area, which version.

adoc Studio for Mac, iPad & iPhone

adoc Studio for Mac, iPad & iPhone

Create Technical Documentation
with AsciiDoc

Free 14-Day Trial

2. Unique Users & Returning Visitors

What: How many people/browsers use your documentation and whether they return.

Technical Prerequisites

  • Analytics tool with sessions/user logic.

  • Same tracking snippet as above.

No additional code needed in AsciiDoc. What matters is that all documentation pages use the snippet.

Where to Measure?
Standard reports in analytics:

  • "Users / Unique Visitors"

  • "New vs. Returning"

Filterable by paths or doc-id areas.

Example: Only doc-area=api → How many users regularly work with API documentation?

3. Time on Page & Scroll Depth

What: Do users read the content or do they leave early?

Technical Prerequisites

  • JavaScript enabled.

  • Events sent to analytics or custom backend.

AsciiDoc Integration: Global include – use sendBeacon() for reliable transmission:

ifdef::backend-html5[]

++++
<script>
  (function() {
    const docMeta = document.querySelector('meta[name="doc-id"]');
    const docId = docMeta ? docMeta.content : 'unknown';
    let maxScroll = 0;

    window.addEventListener('scroll', () => {
      const scrolled = (window.scrollY + window.innerHeight) 
        / document.body.scrollHeight * 100;
      if (scrolled > maxScroll) {
        maxScroll = scrolled;
      }
    });

    // sendBeacon is more reliable than fetch in beforeunload
    window.addEventListener('beforeunload', () => {
      const data = JSON.stringify({
        docId: docId,
        depth: Math.round(maxScroll)
      });
      navigator.sendBeacon('/api/scroll-depth', data);
    });
  })();
</script>

++++
endif::[]

Note: The beforeunload event often blocks asynchronous requests in modern browsers. navigator.sendBeacon() solves this problem by reliably sending data even when the tab is closed. Alternatively, you can send events periodically (e.g., every 30 seconds).

Where to Measure?

In your event dashboard: Average scroll depth per doc-id.

Interpretation:

  • <25%: Title/intro doesn't match or content is irrelevant.

  • 50–75%: normal.

  • ~100% on troubleshooting pages: good, users are actively looking for a solution.

4. On-Page Feedback ("Was This Helpful?")

What: Quick quality rating per page.

Technical Prerequisites

  • Small HTML widget.

  • Event endpoint in the backend or analytics as event target.

AsciiDoc Example

ifdef::backend-html5[]

++++
<div id="doc-feedback">
  Was this page helpful?
  <button data-feedback="yes">Yes</button>
  <button data-feedback="no">No</button>
</div>
<script>
  (function() {
    const docMeta = document.querySelector('meta[name="doc-id"]');
    const docId = docMeta ? docMeta.content : 'unknown';
    
    document.querySelectorAll('#doc-feedback button')
      .forEach(btn => btn.addEventListener('click', () => {
        const value = btn.dataset.feedback;
        // feedbackEvent({ docId, value });
      }));
  })();
</script>
++++

endif::[]

Where to Measure?

Aggregation per doc-id:

helpful_rate = yes / (yes + no)

Benefits:

  • Ranking: Which pages deliver the most value?

  • Prioritization: Improve pages with many views + poor ratings first.

5. Search & Findability

What: Do users find what they need?

Technical Prerequisites

  • Search function in the documentation portal.

  • Logging of search terms + results.

AsciiDoc Integration

If your search runs in the frontend:

ifdef::backend-html5[]

++++
<form id="doc-search">
  <input name="q" />
  <button type="submit">Search</button>
</form>
<script>
  document.getElementById('doc-search')
    .addEventListener('submit', function(e) {
      e.preventDefault();
      const query = this.q.value;
      // searchEvent({ query });
      // Then execute actual search
    });
</script>
++++

endif::[]

Where to Measure?

  • List of all search queries.

  • Search queries with no results.

  • Terms that lead to wrong pages (high bounce rate).

From this, you optimize: page titles, synonyms, and content structure.

6. Task Success & Ticket Deflection

This is where you connect documentation with support costs. Internally, this is often the strongest lever.

a) Task Success

What: Percentage of users who successfully complete a defined task with the help of documentation.

Technical Prerequisites

  • Defined task flows (e.g., "Set up API key").

  • Pages belonging to this flow carry an attribute.

AsciiDoc Example

= Create API Key
:doc-id: api-key-create
:task-flow: api-key-setup

In the tracking snippet (meta tag approach):

<script>
  const flowMeta = document.querySelector('meta[name="task-flow"]');
  const docMeta = document.querySelector('meta[name="doc-id"]');
  
  if (flowMeta && docMeta) {
    // taskStepEvent({
    //   flow: flowMeta.content,
    //   docId: docMeta.content
    // });
  }
</script>

Success Measurement: Success when users reach the target page (e.g., "Successfully connected" page) or perform an action (e.g., save API key; event from product).

Where to Measure? Funnel per task-flow: Entry → Intermediate steps → Success. You see where users drop off and which pages in the flow need optimization.

b) Ticket Deflection

What: How many support requests does your content prevent?

Technical Prerequisites

  • Support form or help widget with article reference.

  • Mapping of tickets to doc-id.

Concrete Approach: In the help widget, users see article suggestions. If they still create a ticket, send the last viewed doc-id along.

Analysis:

  • Topics with many tickets despite documentation → content unclear.

  • Drop in tickets after article update → measurable success.

7. Internal Quality Metrics

These metrics are directly tied to your AsciiDoc setup.

Examples:

  • Percentage of outdated pages (:doc-version: vs. product version).

  • Broken links (via link check in CI).

  • Update time after release (commit timestamp).

AsciiDoc Support: Attributes like :last-review: 2025-10-15. Tools/linters read these attributes and generate reports.

Making PDF Exports Measurable

PDFs cannot send JS events. You make them measurable through indirect signals.

Use a unique short URL per PDF:

More info:
link:https://docs.example.com/go/getting-started-pdf[Online Documentation]

This URL redirects internally to the actual page. You measure:

  • How often readers jump from the PDF back to online documentation.

  • Which PDFs are actually being used.

9. QR Codes

Use doc-id to generate QR codes.

image::qrcode-getting-started.png[Give Feedback]

QR target e.g.: https://docs.example.com/f/{doc-id}

There: Feedback form or link to the updated version.

10. Metadata

Set in the PDF generator: Title, Subject, Keywords with doc-id and product. This way, internal systems (DAM, intranet, DMS) can better track usage and versions.

Practical Minimal Implementation for Your Team

If you want to start with minimal effort, this setup is sufficient:

In each AsciiDoc page:

= Title
:doc-id: <unique>
:doc-area: <area>
:doc-version: <version>

A Global HTML Include

  • Pageview with doc-id (via meta tags or data attributes).

  • Scroll depth event (with sendBeacon for reliability).

  • Optional feedback buttons.

A Simple Backend or Analytics

Per doc-id:

  • Pageviews

  • Scroll depth

  • Feedback rate

Monthly Evaluation

  • Top 20 pages by views.

  • Pages with many views and poor feedback.

  • Search terms with no results.

  • Tickets per topic before/after documentation updates.

This creates measurable value: You see in black and white which content works and where you need to improve.

Appendix: Technical Implementation Notes

Attribute Substitution in AsciiDoc

AsciiDoc does not substitute attributes inside passthrough blocks (++++...++++) by default. This is a common pitfall.

Solution 1 – Meta Tags in Document Header:

Generate meta tags outside the passthrough block and read them via JavaScript.

:doc-id: getting-started

// In docinfo header or template:
<meta name="doc-id" content="{doc-id}">

The meta tag is correctly substituted because it's outside the passthrough block. In JavaScript, you then read:

const docId = document.querySelector('meta[name="doc-id"]').content;

Solution 2 – subs Attribute:

In some AsciiDoc versions, you can use [subs=attributes] before the passthrough block:

[subs=attributes]
++++
<script>
  const docId = '{doc-id}';
</script>
++++

Test this with your AsciiDoc version, as behavior may vary.

Solution 3 – Data Attributes on Container Elements:

[id="tracking-container",data-doc-id="{doc-id}"]
--
Content
--

beforeunload and sendBeacon

The beforeunload event is problematic for analytics:

  • Modern browsers often block asynchronous requests (fetch, XMLHttpRequest).

  • Mobile browsers frequently suppress the event completely.

  • Safari has particularly strict restrictions.

Recommendation: Use navigator.sendBeacon(). This method was specifically designed for this use case and reliably sends data even when the tab is closed.

window.addEventListener('beforeunload', () => {
  const data = JSON.stringify({ docId, depth: maxScroll });
  navigator.sendBeacon('/api/analytics', data);
});

Alternative: Send events periodically (e.g., every 30 seconds) instead of only when leaving. This way, you lose at most 30 seconds of data.

setInterval(() => {
  navigator.sendBeacon('/api/scroll-depth', JSON.stringify({
    docId,
    depth: Math.round(maxScroll)
  }));
}, 30000);

© adoc Studio