From a30a71905e85a5e90a9f5683c66c9485bfe601fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 16 Oct 2025 14:03:07 +0200 Subject: [PATCH] Chore: replace feature toggle filterOutBotsFromFrontendLogs with config (#112355) * Chore: replace feature toggle with config * chore: small fixes * chore: updates after pr feedback * chore: fixes writers toolkit error --- conf/defaults.ini | 2 + conf/sample.ini | 3 + .../setup-grafana/configure-grafana/_index.md | 4 + .../src/types/featureToggles.gen.ts | 5 -- packages/grafana-runtime/src/config.ts | 1 + pkg/services/featuremgmt/registry.go | 8 -- pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/featuremgmt/toggles_gen.json | 3 +- .../setting_grafana_javascript_agent.go | 2 + .../GrafanaJavascriptAgentBackend.test.ts | 11 +-- .../GrafanaJavascriptAgentBackend.ts | 24 +----- .../beforeSendHandler.test.ts | 75 +++++++++++-------- .../beforeSendHandler.ts | 53 ++++++++++--- .../grafana-javascript-agent/types.ts | 20 ++++- public/app/core/services/echo/init.ts | 1 + 16 files changed, 125 insertions(+), 92 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index b057f887fb4..977d0fa8783 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1234,6 +1234,8 @@ log_endpoint_requests_per_second_limit = 3 # Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log) log_endpoint_burst_limit = 15 +# Enables the bot filter for the Grafana Faro JavaScript agent integration. Default is `false`. When enabled, it will filter out requests from known bots and crawlers. +bot_filter_enabled = false #################################### Usage Quotas ######################## [quota] diff --git a/conf/sample.ini b/conf/sample.ini index eb59e6688f3..5518290525e 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1206,6 +1206,9 @@ # See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/web-vitals/#web-vitals-attribution-data ;web_vitals_attribution_enabled = true +# Enables the bot filter for the Grafana Faro JavaScript agent integration. Default is `false`. When enabled, it will filter out requests from known bots and crawlers. +;bot_filter_enabled = false + #################################### Usage Quotas ######################## [quota] ; enabled = false diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index ff77ac47fc8..b0b328866d6 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1679,6 +1679,10 @@ Requests per second limit enforced per an extended period, for Grafana backend l Maximum requests accepted per short interval of time for Grafana backend log ingestion endpoint, `/log-grafana-javascript-agent`. Default is `15`. +#### `bot_filter_enabled` + +Enables the bot filter for the Grafana Faro JavaScript agent integration. Default is `false`. When enabled, it will filter out requests from known bots and crawlers. +
### `[quota]` diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 476c60e68da..545d9dc7a3d 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -1200,11 +1200,6 @@ export interface FeatureToggles { */ tempoSearchBackendMigration?: boolean; /** - * Filter out bots from collecting data for Frontend Observability - * @default false - */ - filterOutBotsFromFrontendLogs?: boolean; - /** * Prioritize loading plugins from the CDN before other sources * @default false */ diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 8fc1fb68218..bf36333e107 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -161,6 +161,7 @@ export class GrafanaBootConfig { tracingInstrumentalizationEnabled: false, webVitalsAttribution: false, internalLoggerLevel: 0, + botFilterEnabled: false, }; pluginCatalogURL = 'https://grafana.com/grafana/plugins/'; pluginAdminEnabled = true; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 1ed039f3af0..f4145b84d5a 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -2080,14 +2080,6 @@ var ( Expression: "false", RequiresRestart: true, }, - { - Name: "filterOutBotsFromFrontendLogs", - Description: "Filter out bots from collecting data for Frontend Observability", - Stage: FeatureStageExperimental, - FrontendOnly: true, - Owner: grafanaPluginsPlatformSquad, - Expression: "false", - }, { Name: "cdnPluginsLoadFirst", Description: "Prioritize loading plugins from the CDN before other sources", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index e3dd5c8263a..3dbd97baa41 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -267,6 +267,5 @@ azureResourcePickerUpdates,preview,@grafana/partner-datasources,false,false,true prometheusTypeMigration,experimental,@grafana/partner-datasources,false,true,false pluginContainers,privatePreview,@grafana/plugins-platform-backend,false,true,false tempoSearchBackendMigration,GA,@grafana/oss-big-tent,false,true,false -filterOutBotsFromFrontendLogs,experimental,@grafana/plugins-platform-backend,false,false,true cdnPluginsLoadFirst,experimental,@grafana/plugins-platform-backend,false,false,false cdnPluginsUrls,experimental,@grafana/plugins-platform-backend,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 036298d25e2..9e4c919469d 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -1078,10 +1078,6 @@ const ( // Run search queries through the tempo backend FlagTempoSearchBackendMigration = "tempoSearchBackendMigration" - // FlagFilterOutBotsFromFrontendLogs - // Filter out bots from collecting data for Frontend Observability - FlagFilterOutBotsFromFrontendLogs = "filterOutBotsFromFrontendLogs" - // FlagCdnPluginsLoadFirst // Prioritize loading plugins from the CDN before other sources FlagCdnPluginsLoadFirst = "cdnPluginsLoadFirst" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index ef4b6aac2f8..cad2744aa58 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -1692,7 +1692,8 @@ "metadata": { "name": "filterOutBotsFromFrontendLogs", "resourceVersion": "1758000919535", - "creationTimestamp": "2025-09-16T05:35:19Z" + "creationTimestamp": "2025-09-16T05:35:19Z", + "deletionTimestamp": "2025-10-13T11:09:22Z" }, "spec": { "description": "Filter out bots from collecting data for Frontend Observability", diff --git a/pkg/setting/setting_grafana_javascript_agent.go b/pkg/setting/setting_grafana_javascript_agent.go index 9ce5633f492..cb07b818597 100644 --- a/pkg/setting/setting_grafana_javascript_agent.go +++ b/pkg/setting/setting_grafana_javascript_agent.go @@ -14,6 +14,7 @@ type GrafanaJavascriptAgent struct { CSPInstrumentalizationEnabled bool `json:"cspInstrumentalizationEnabled"` TracingInstrumentalizationEnabled bool `json:"tracingInstrumentalizationEnabled"` WebVitalsAttributionEnabled bool `json:"webVitalsAttributionEnabled"` + BotFilterEnabled bool `json:"botFilterEnabled"` } func (cfg *Cfg) readGrafanaJavascriptAgentConfig() { @@ -32,5 +33,6 @@ func (cfg *Cfg) readGrafanaJavascriptAgentConfig() { CSPInstrumentalizationEnabled: raw.Key("instrumentations_csp_enabled").MustBool(true), TracingInstrumentalizationEnabled: raw.Key("instrumentations_tracing_enabled").MustBool(true), WebVitalsAttributionEnabled: raw.Key("web_vitals_attribution_enabled").MustBool(true), + BotFilterEnabled: raw.Key("bot_filter_enabled").MustBool(false), } } diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts index 04fb4e88d33..a2394ca414d 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts @@ -12,14 +12,10 @@ import { ViewInstrumentation, } from '@grafana/faro-web-sdk'; import { TracingInstrumentation } from '@grafana/faro-web-tracing'; -import { config } from '@grafana/runtime'; import { EchoSrvTransport } from './EchoSrvTransport'; -import { - GrafanaJavascriptAgentBackend, - GrafanaJavascriptAgentBackendOptions, - TRACKING_URLS, -} from './GrafanaJavascriptAgentBackend'; +import { GrafanaJavascriptAgentBackend, TRACKING_URLS } from './GrafanaJavascriptAgentBackend'; +import { GrafanaJavascriptAgentBackendOptions } from './types'; describe('GrafanaJavascriptAgentEchoBackend', () => { let mockedSetUser: jest.Mock; @@ -55,8 +51,6 @@ describe('GrafanaJavascriptAgentEchoBackend', () => { instrumentations: mockedInstrumentations, internalLogger: mockedInternalLogger, }); - - config.featureToggles.filterOutBotsFromFrontendLogs = false; }); afterEach(() => { @@ -90,6 +84,7 @@ describe('GrafanaJavascriptAgentEchoBackend', () => { buildInfo: buildInfo, userIdentifier: 'abc123', ignoreUrls: [], + botFilterEnabled: false, }; it('will set up FetchTransport if customEndpoint is provided', () => { diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts index 386caff9f3b..872aedc4956 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts @@ -1,5 +1,5 @@ -import { BuildInfo, escapeRegex } from '@grafana/data'; -import { BaseTransport, defaultInternalLoggerLevel, InternalLoggerLevel } from '@grafana/faro-core'; +import { escapeRegex } from '@grafana/data'; +import { BaseTransport, defaultInternalLoggerLevel } from '@grafana/faro-core'; import { initializeFaro, BrowserConfig, @@ -12,7 +12,7 @@ import { EchoBackend, EchoEvent, EchoEventType } from '@grafana/runtime'; import { EchoSrvTransport } from './EchoSrvTransport'; import { beforeSendHandler } from './beforeSendHandler'; -import { GrafanaJavascriptAgentEchoEvent } from './types'; +import { GrafanaJavascriptAgentBackendOptions, GrafanaJavascriptAgentEchoEvent } from './types'; function isCrossOriginIframe() { try { @@ -22,22 +22,6 @@ function isCrossOriginIframe() { } } -export interface GrafanaJavascriptAgentBackendOptions { - apiKey?: string; - customEndpoint?: string; - internalLoggerLevel?: InternalLoggerLevel; - - webVitalsAttribution: boolean; - consoleInstrumentalizationEnabled: boolean; - performanceInstrumentalizationEnabled: boolean; - cspInstrumentalizationEnabled: boolean; - tracingInstrumentalizationEnabled: boolean; - - buildInfo: BuildInfo; - userIdentifier: string; - ignoreUrls: RegExp[]; -} - export const TRACKING_URLS = [ /\.(google-analytics|googletagmanager)\.com/, /frontend-metrics/, @@ -107,7 +91,7 @@ export class GrafanaJavascriptAgentBackend batching: { sendTimeout: 1000, }, - beforeSend: beforeSendHandler, + beforeSend: (item) => beforeSendHandler(options.botFilterEnabled, item), internalLoggerLevel: options.internalLoggerLevel ?? defaultInternalLoggerLevel, }; diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/beforeSendHandler.test.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/beforeSendHandler.test.ts index e78e3a50fc5..27a5d3abf92 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/beforeSendHandler.test.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/beforeSendHandler.test.ts @@ -1,5 +1,4 @@ import { TransportItem, TransportItemType } from '@grafana/faro-core'; -import { config } from '@grafana/runtime'; import { beforeSendHandler } from './beforeSendHandler'; @@ -11,43 +10,53 @@ const getTransportationItem = (userAgent: string | undefined): TransportItem => describe('beforeSendHandler', () => { beforeEach(() => { - config.featureToggles.filterOutBotsFromFrontendLogs = false; + jest.spyOn(console, 'warn').mockImplementation(() => {}); }); - it('should return item when feature toggle is disabled', () => { - const botUserAgent = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; - const item = getTransportationItem(botUserAgent); - expect(beforeSendHandler(item)).toBe(item); + describe('when botFilterEnabled is false', () => { + it('should return item', () => { + const botUserAgent = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; + const item = getTransportationItem(botUserAgent); + expect(beforeSendHandler(false, item)).toBe(item); + }); }); - it('should return item for regular user agents', () => { - config.featureToggles.filterOutBotsFromFrontendLogs = true; - const regularUserAgent = - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'; - const item = getTransportationItem(regularUserAgent); - expect(beforeSendHandler(item)).toBe(item); - }); + describe('when botFilterEnabled is true', () => { + const userUserAgents = [ + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0', + ]; + const invalidUserAgents = ['', ' ', undefined, null, 0, 1, {}, [], () => {}]; + const maliciousUserAgents = userUserAgents.map((ua) => ua + 'a'.repeat(600)); - it.each(['', undefined])('should return item when user agent is %s', (userAgent) => { - config.featureToggles.filterOutBotsFromFrontendLogs = true; - const item = getTransportationItem(userAgent); - expect(beforeSendHandler(item)).toBe(item); - }); + it.each(userUserAgents)('should return item for bot user agent: %s', (userAgent) => { + const item = getTransportationItem(userAgent); + expect(beforeSendHandler(true, item)).toBe(item); + }); - it.each([ - 'Googlebot/2.1 (+http://www.google.com/bot.html)', - 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)', - 'Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)', - 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)', - 'Twitterbot/1.0', - 'Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)', - 'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)', - 'Mozilla/5.0 (compatible; DuckDuckBot-Https/1.1; https://duckduckgo.com/duckduckbot)', - 'Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)', - 'Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)', - ])('should return 0 for bot user agent: %s', (userAgent) => { - config.featureToggles.filterOutBotsFromFrontendLogs = true; - const item = getTransportationItem(userAgent); - expect(beforeSendHandler(item)).toBe(null); + it.each([ + ...invalidUserAgents, + ...maliciousUserAgents, + 'Googlebot/2.1 (+http://www.google.com/bot.html)', + 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)', + 'Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)', + 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)', + 'Twitterbot/1.0', + 'Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)', + 'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)', + 'Mozilla/5.0 (compatible; DuckDuckBot-Https/1.1; https://duckduckgo.com/duckduckbot)', + 'Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)', + 'Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)', + 'Mozilla/5.0 (compatible; NOTgooglebot/2.1)', + 'Mozilla/5.0 (compatible; googlebotbypass/2.1)', + 'Mozilla/5.0 (compatible; notbingbot/2.0; +http://www.bing.com/notbingbot.htm)', + ])('should return null for bot user agent: %s', (userAgent) => { + const item = getTransportationItem(userAgent as string); + expect(beforeSendHandler(true, item)).toBe(null); + }); }); }); diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/beforeSendHandler.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/beforeSendHandler.ts index 95e65acc826..ee1a6ac8bfc 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/beforeSendHandler.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/beforeSendHandler.ts @@ -1,24 +1,55 @@ import { TransportItem } from '@grafana/faro-core'; -import { config } from '@grafana/runtime'; // as listed in https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/filter-bots/#filter-out-bots-from-collecting-data-for-frontend-observability const bots = - '(googlebot|Googlebot-Mobile|Googlebot-Image|Google favicon|Mediapartners-Google|bingbot|slurp|Commons-HttpClient|Python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|jyxobot|FAST-WebCrawler|FAST Enterprise Crawler|biglotron|teoma|convera|seekbot|gigablast|exabot|ngbot|ia_archiver|GingerCrawler|webmon |httrack|webcrawler|grub.org|UsineNouvelleCrawler|antibot|netresearchserver|speedy|fluffy|bibnum.bnf|findlink|msrbot|panscient|yacybot|AISearchBot|IOI|ips-agent|tagoobot|MJ12bot|dotbot|woriobot|yanga|buzzbot|mlbot|yandexbot|purebot|Linguee Bot|Voyager|CyberPatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|scribdbot|page2rss|sitebot|linkdex|Adidxbot|blekkobot|ezooms|Mail.RU_Bot|discobot|heritrix|findthatfile|europarchive.org|NerdByNature.Bot|sistrix crawler|ahrefsbot|Aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|gslfbot|aihitbot|intelium_bot|facebookexternalhit|yeti|RetrevoPageAnalyzer|lb-spider|sogou|lssbot|careerbot|wotbox|wocbot|ichiro|DuckDuckBot|lssrocketcrawler|drupact|webcompanycrawler|acoonbot|openindexspider|gnam gnam spider|web-archive-net.com.bot|backlinkcrawler|coccoc|integromedb|content crawler spider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler.com|siteexplorer.info|elisabot|proximic|changedetection|blexbot|arabot|WeSEE:Search|niki-bot|CrystalSemanticsBot|rogerbot|360Spider|psbot|InterfaxScanBot|Lipperhey SEO Service|CC Metadata Scaper|g00g1e.net|GrapeshotCrawler|urlappendbot|brainobot|fr-crawler|binlar|SimpleCrawler|Livelapbot|Twitterbot|cXensebot|smtbot|bnf.fr_bot|A6-Indexer|ADmantX|Facebot|OrangeBot|memorybot|AdvBot|MegaIndex|SemanticScholarBot|ltx71|nerdybot|xovibot|BUbiNG|Qwantify|archive.org_bot|Applebot|TweetmemeBot|crawler4j|findxbot|SemrushBot|yoozBot|lipperhey|y!j-asr|Domain Re-Animator Bot|AddThis)'; + '(googlebot|googlebot-mobile|googlebot-image|google favicon|mediapartners-google|' + + 'bingbot|slurp|commons-httpclient|python-urllib|libwww|httpunit|nutch|phpcrawl|' + + 'msnbot|jyxobot|fast-webcrawler|fast enterprise crawler|biglotron|teoma|convera|' + + 'seekbot|gigablast|exabot|ngbot|ia_archiver|gingercrawler|webmon |httrack|' + + 'webcrawler|grub.org|usinenouvellecrawler|antibot|netresearchserver|speedy|fluffy|' + + 'bibnum.bnf|findlink|msrbot|panscient|yacybot|aisearchbot|ioi|ips-agent|tagoobot|' + + 'mj12bot|dotbot|woriobot|yanga|buzzbot|mlbot|yandexbot|purebot|linguee bot|voyager|' + + 'cyberpatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|' + + 'scribdbot|page2rss|sitebot|linkdex|adidxbot|blekkobot|ezooms|mail.ru_bot|discobot|' + + 'heritrix|findthatfile|europarchive.org|nerdbynature.bot|sistrix crawler|ahrefsbot|' + + 'aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|' + + 'gslfbot|aihitbot|intelium_bot|facebookexternalhit|yeti|retrevopageanalyzer|lb-spider|' + + 'sogou|lssbot|careerbot|wotbox|wocbot|ichiro|duckduckbot|lssrocketcrawler|drupact|' + + 'webcompanycrawler|acoonbot|openindexspider|gnam gnam spider|web-archive-net.com.bot|' + + 'backlinkcrawler|coccoc|integromedb|content crawler spider|toplistbot|seokicks-robot|' + + 'it2media-domain-crawler|ip-web-crawler.com|siteexplorer.info|elisabot|proximic|' + + 'changedetection|blexbot|arabot|wesee:search|niki-bot|crystalsemanticsbot|rogerbot|' + + '360spider|psbot|interfaxscanbot|lipperhey seo service|cc metadata scraper|g00g1e.net|' + + 'grapeshotcrawler|urlappendbot|brainobot|fr-crawler|binlar|simplecrawler|livelapbot|' + + 'twitterbot|cxensebot|smtbot|bnf.fr_bot|a6-indexer|admantx|facebot|orangebot|' + + 'memorybot|advbot|megaindex|semanticscholarbot|ltx71|nerdybot|xovibot|bubing|' + + 'qwantify|archive.org_bot|applebot|tweetmemebot|crawler4j|findxbot|semrushbot|' + + 'yoozbot|lipperhey|y!j-asr|domain re-animator bot|addthis)'; -const botsRegex = new RegExp(bots, 'i'); +const botsRegex = new RegExp(bots); -export function beforeSendHandler(item: TransportItem): TransportItem | null { - if (!config.featureToggles.filterOutBotsFromFrontendLogs) { +export function beforeSendHandler(botFilterEnabled: boolean, item: TransportItem): TransportItem | null { + if (!botFilterEnabled) { return item; } - if (!item.meta.browser?.userAgent) { - return item; - } - - if (botsRegex.test(item.meta.browser.userAgent)) { + if (typeof item.meta.browser?.userAgent !== 'string') { return null; } - return item; + const userAgent = item.meta.browser?.userAgent?.trim().toLowerCase(); + if (!userAgent) { + return null; + } + + if (userAgent.length > 512) { + return null; + } + + try { + const isBot = botsRegex.test(userAgent); + return isBot ? null : item; + } catch (error) { + return null; + } } diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/types.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/types.ts index 35ffe99d8e2..049fbaf147e 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/types.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/types.ts @@ -1,4 +1,5 @@ -import { CurrentUserDTO } from '@grafana/data'; +import { BuildInfo, CurrentUserDTO } from '@grafana/data'; +import { InternalLoggerLevel } from '@grafana/faro-web-sdk'; import { EchoEvent, EchoEventType } from '@grafana/runtime'; export interface BaseTransport { @@ -11,3 +12,20 @@ export interface User extends Pick { id: string; orgId?: number; } + +export interface GrafanaJavascriptAgentBackendOptions { + apiKey?: string; + customEndpoint?: string; + internalLoggerLevel?: InternalLoggerLevel; + + webVitalsAttribution: boolean; + consoleInstrumentalizationEnabled: boolean; + performanceInstrumentalizationEnabled: boolean; + cspInstrumentalizationEnabled: boolean; + tracingInstrumentalizationEnabled: boolean; + + buildInfo: BuildInfo; + userIdentifier: string; + ignoreUrls: RegExp[]; + botFilterEnabled: boolean; +} diff --git a/public/app/core/services/echo/init.ts b/public/app/core/services/echo/init.ts index 6b13b4c4200..d82c58b69c1 100644 --- a/public/app/core/services/echo/init.ts +++ b/public/app/core/services/echo/init.ts @@ -109,6 +109,7 @@ async function initFaroBackend() { tracingInstrumentalizationEnabled: config.grafanaJavascriptAgent.tracingInstrumentalizationEnabled, webVitalsAttribution: config.grafanaJavascriptAgent.webVitalsAttribution, internalLoggerLevel: config.grafanaJavascriptAgent.internalLoggerLevel, + botFilterEnabled: config.grafanaJavascriptAgent.botFilterEnabled, }) ); }