From 94957abfaf8f0da6c7a9e07341ec5e39985d84f7 Mon Sep 17 00:00:00 2001 From: paulj Date: Mon, 2 Feb 2026 12:39:02 -0500 Subject: [PATCH 1/2] feat(metrics): Add trace sampling metrics for traffic visibility Track sampling decisions with Sentry.metrics.count to provide visibility into traffic patterns hitting the docs site. Each sampling decision now emits a docs.trace.sampled metric with attributes for traffic_type (ai_agent, bot, user, unknown), the specific bot/agent matched, and the sample rate applied. This enables querying in Sentry to understand: - Volume breakdown by traffic type - Which bots are hitting the site most frequently - Which AI agents are consuming docs content - How much traffic is being filtered vs sampled Co-Authored-By: Claude --- src/tracesSampler.ts | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/tracesSampler.ts b/src/tracesSampler.ts index 4305e850732862..f6b1b232f31ad8 100644 --- a/src/tracesSampler.ts +++ b/src/tracesSampler.ts @@ -1,3 +1,5 @@ +import * as Sentry from '@sentry/nextjs'; + // Sampling context passed to tracesSampler // Using inline type to avoid dependency on internal Sentry types interface SamplingContext { @@ -93,17 +95,45 @@ export function tracesSampler(samplingContext: SamplingContext): number { (samplingContext.attributes?.['user_agent.original'] as string | undefined); if (!userAgent) { + Sentry.metrics.count('docs.trace.sampled', 1, { + attributes: { + traffic_type: 'unknown', + sample_rate: DEFAULT_SAMPLE_RATE, + }, + }); return DEFAULT_SAMPLE_RATE; } - if (AI_AGENT_PATTERN.test(userAgent)) { + const aiMatch = userAgent.match(AI_AGENT_PATTERN); + if (aiMatch) { + Sentry.metrics.count('docs.trace.sampled', 1, { + attributes: { + traffic_type: 'ai_agent', + agent_match: aiMatch[0].toLowerCase(), + sample_rate: 1, + }, + }); return 1; } - if (BOT_PATTERN.test(userAgent)) { + const botMatch = userAgent.match(BOT_PATTERN); + if (botMatch) { + Sentry.metrics.count('docs.trace.sampled', 1, { + attributes: { + traffic_type: 'bot', + bot_match: botMatch[0].toLowerCase(), + sample_rate: 0, + }, + }); return 0; } // Sample real users at default rate + Sentry.metrics.count('docs.trace.sampled', 1, { + attributes: { + traffic_type: 'user', + sample_rate: DEFAULT_SAMPLE_RATE, + }, + }); return DEFAULT_SAMPLE_RATE; } From dbec1a87f64e0a81d14e2dea06e96fb9699e8e3e Mon Sep 17 00:00:00 2001 From: paulj Date: Mon, 2 Feb 2026 12:56:08 -0500 Subject: [PATCH 2/2] refactor regex match for readability --- src/tracesSampler.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/tracesSampler.ts b/src/tracesSampler.ts index f6b1b232f31ad8..98521249664ed3 100644 --- a/src/tracesSampler.ts +++ b/src/tracesSampler.ts @@ -78,6 +78,15 @@ const BOT_PATTERN = new RegExp( // Default sample rate for real users const DEFAULT_SAMPLE_RATE = 0.3; +/** + * Checks if the input matches the pattern. + * Returns the matched substring (lowercase), or undefined if no match. + */ +function matchPattern(input: string, pattern: RegExp): string | undefined { + const match = input.match(pattern); + return match ? match[0].toLowerCase() : undefined; +} + /** * Determines trace sample rate based on user agent. * - AI agents: 100% (we want full visibility into agentic docs consumption) @@ -104,24 +113,24 @@ export function tracesSampler(samplingContext: SamplingContext): number { return DEFAULT_SAMPLE_RATE; } - const aiMatch = userAgent.match(AI_AGENT_PATTERN); - if (aiMatch) { + const aiAgent = matchPattern(userAgent, AI_AGENT_PATTERN); + if (aiAgent) { Sentry.metrics.count('docs.trace.sampled', 1, { attributes: { traffic_type: 'ai_agent', - agent_match: aiMatch[0].toLowerCase(), + agent_match: aiAgent, sample_rate: 1, }, }); return 1; } - const botMatch = userAgent.match(BOT_PATTERN); - if (botMatch) { + const bot = matchPattern(userAgent, BOT_PATTERN); + if (bot) { Sentry.metrics.count('docs.trace.sampled', 1, { attributes: { traffic_type: 'bot', - bot_match: botMatch[0].toLowerCase(), + bot_match: bot, sample_rate: 0, }, });