diff --git a/developer-extension/src/common/extension.types.ts b/developer-extension/src/common/extension.types.ts index 6f99b3c5ca..e4ab3315f0 100644 --- a/developer-extension/src/common/extension.types.ts +++ b/developer-extension/src/common/extension.types.ts @@ -45,6 +45,9 @@ export type SdkMessage = export type EventCollectionStrategy = 'sdk' | 'requests' +export type InjectionVariant = 'local-dev' | 'cdn' + +export type SdkInjectionType = 'RUM' | 'LOGS' | 'BOTH' export interface Settings { useDevBundles: DevBundlesOverride useDevReplaySandbox: boolean @@ -57,4 +60,6 @@ export interface Settings { logsConfigurationOverride: object | null debugMode: boolean datadogMode: boolean + injectionVariant: InjectionVariant + sdkInjectionType: SdkInjectionType } diff --git a/developer-extension/src/common/packagesUrlConstants.ts b/developer-extension/src/common/packagesUrlConstants.ts index 9ec3f66a3f..f5efa3591b 100644 --- a/developer-extension/src/common/packagesUrlConstants.ts +++ b/developer-extension/src/common/packagesUrlConstants.ts @@ -3,6 +3,13 @@ export const DEV_LOGS_URL = `${DEV_SERVER_ORIGIN}/datadog-logs.js` export const DEV_RUM_SLIM_URL = `${DEV_SERVER_ORIGIN}/datadog-rum-slim.js` export const DEV_RUM_URL = `${DEV_SERVER_ORIGIN}/datadog-rum.js` +export const CDN_BASE_URL = 'https://www.datadoghq-browser-agent.com' +// This version corresponds to the major version of the Browser SDK and needs to be manually updated when bumping major versions +export const CDN_VERSION = 'v6' +export const CDN_RUM_URL = `${CDN_BASE_URL}/${CDN_VERSION}/datadog-rum.js` +export const CDN_RUM_SLIM_URL = `${CDN_BASE_URL}/${CDN_VERSION}/datadog-rum-slim.js` +export const CDN_LOGS_URL = `${CDN_BASE_URL}/${CDN_VERSION}/datadog-logs.js` + // To follow web-ui development, this version will need to be manually updated from time to time. // When doing that, be sure to update types and implement any protocol changes. export const PROD_REPLAY_SANDBOX_VERSION = '0.119.0' diff --git a/developer-extension/src/content-scripts/main.ts b/developer-extension/src/content-scripts/main.ts index 4e1155b1e2..45d01a0b8f 100644 --- a/developer-extension/src/content-scripts/main.ts +++ b/developer-extension/src/content-scripts/main.ts @@ -1,7 +1,8 @@ import type { Settings } from '../common/extension.types' import { EventListeners } from '../common/eventListeners' -import { DEV_LOGS_URL, DEV_RUM_SLIM_URL, DEV_RUM_URL } from '../common/packagesUrlConstants' +import { CDN_BASE_URL, CDN_VERSION, DEV_LOGS_URL, DEV_RUM_SLIM_URL, DEV_RUM_URL } from '../common/packagesUrlConstants' import { SESSION_STORAGE_SETTINGS_KEY } from '../common/sessionKeyConstant' +import { createLogger } from '../common/logger' declare global { interface Window extends EventTarget { @@ -11,6 +12,8 @@ declare global { } } +const logger = createLogger('content-script-main') + interface SdkPublicApi { [key: string]: (...args: any[]) => unknown } @@ -22,7 +25,6 @@ function main() { } sendEventsToExtension() - const settings = getSettings() if ( @@ -47,9 +49,11 @@ function main() { overrideInitConfiguration(ddLogsGlobal, settings.logsConfigurationOverride) } - if (settings.useDevBundles === 'npm') { + if (settings.injectionVariant === 'local-dev' && settings.useDevBundles === 'npm') { injectDevBundle(settings.useRumSlim ? DEV_RUM_SLIM_URL : DEV_RUM_URL, ddRumGlobal) injectDevBundle(DEV_LOGS_URL, ddLogsGlobal) + } else if (settings.injectionVariant === 'cdn' && settings.datadogMode) { + injectCdnBundle(settings) } } } @@ -126,8 +130,7 @@ function loadSdkScriptFromURL(url: string) { xhr.open('GET', url, false) // `false` makes the request synchronous xhr.send() } catch (error) { - // eslint-disable-next-line no-console - console.error(`[DD Browser SDK extension] Error while loading ${url}:`, error) + logger.error(`Error while loading ${url}:`, error) return } if (xhr.status === 200) { @@ -184,3 +187,109 @@ function instrumentGlobal(global: 'DD_RUM' | 'DD_LOGS') { function proxySdk(target: SdkPublicApi, root: SdkPublicApi) { Object.assign(target, root) } + +function injectCdnBundle(settings: Settings) { + const injectWhenReady = () => { + if (settings.sdkInjectionType === 'RUM' || settings.sdkInjectionType === 'BOTH') { + const rumSite = (settings.rumConfigurationOverride as any)?.site as string | undefined + const rumUrl = getRumBundleUrl(settings.useRumSlim ? 'rum-slim' : 'rum', rumSite) + const rumConfig = + settings.rumConfigurationOverride || + (settings.datadogMode + ? { + applicationId: 'xxx', + clientToken: 'xxx', + site: 'datad0g.com', + allowedTrackingOrigins: [location.origin], + sessionReplaySampleRate: 100, + } + : null) + injectAndInitializeSDK(rumUrl, 'DD_RUM', rumConfig as any) + } + + if (settings.sdkInjectionType === 'LOGS' || settings.sdkInjectionType === 'BOTH') { + const logsSite = (settings.logsConfigurationOverride as any)?.site as string | undefined + const logsUrl = getLogsBundleUrl(logsSite) + const logsConfig = + settings.logsConfigurationOverride || + (settings.datadogMode + ? { + clientToken: 'xxx', + site: 'datad0g.com', + allowedTrackingOrigins: [location.origin], + } + : null) + injectAndInitializeSDK(logsUrl, 'DD_LOGS', logsConfig as any) + } + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', injectWhenReady, { once: true }) + } else { + injectWhenReady() + } +} + +function getRumBundleUrl(bundle: 'rum' | 'rum-slim', site?: string): string { + const region = getCdnRegion(site) + return `${CDN_BASE_URL}/${region}/${CDN_VERSION}/datadog-${bundle}.js` +} + +function getLogsBundleUrl(site?: string) { + const region = getCdnRegion(site) + return `${CDN_BASE_URL}/${region}/${CDN_VERSION}/datadog-logs.js` +} + +function getCdnRegion(site?: string) { + if (!site || site === 'datadoghq.com') { + return 'us1' + } + if (site === 'datadoghq.eu') { + return 'eu1' + } + if (site?.startsWith('us3.')) { + return 'us3' + } + if (site?.startsWith('us5.')) { + return 'us5' + } + if (site?.endsWith('datad0g.com')) { + return 'us3' + } + + return 'us1' +} + +function injectAndInitializeSDK(url: string, globalName: 'DD_RUM' | 'DD_LOGS', config: object | null) { + // If the SDK is already loaded, don't try to load it again + if (window[globalName]) { + logger.log(`${globalName} already exists, skipping injection`) + return + } + + if (url.includes('datadoghq-browser-agent.com')) { + const script = document.createElement('script') + script.src = url + script.async = true + script.onload = () => { + if (config && window[globalName] && 'init' in window[globalName]) { + try { + window[globalName].init(config) + } catch (e) { + logger.error(`Error initializing ${globalName}:`, e) + } + } else { + logger.log(`${globalName} loaded. No init called (no config provided).`) + } + } + script.onerror = (e) => { + logger.error(`Error loading ${globalName} script:`, e) + } + try { + document.head.appendChild(script) + } catch (appendErr) { + logger.error('failed to append script to head, retrying on documentElement', appendErr) + document.documentElement.appendChild(script) + } + } +} diff --git a/developer-extension/src/panel/components/tabs/settingsTab.tsx b/developer-extension/src/panel/components/tabs/settingsTab.tsx index 77ab8260cb..d1b076deb2 100644 --- a/developer-extension/src/panel/components/tabs/settingsTab.tsx +++ b/developer-extension/src/panel/components/tabs/settingsTab.tsx @@ -5,7 +5,12 @@ import { DevServerStatus, useDevServerStatus } from '../../hooks/useDevServerSta import { useSettings } from '../../hooks/useSettings' import { Columns } from '../columns' import { TabBase } from '../tabBase' -import type { DevBundlesOverride, EventCollectionStrategy } from '../../../common/extension.types' +import type { + DevBundlesOverride, + EventCollectionStrategy, + InjectionVariant, + SdkInjectionType, +} from '../../../common/extension.types' export function SettingsTab() { const sdkDevServerStatus = useDevServerStatus(DEV_LOGS_URL) @@ -21,10 +26,34 @@ export function SettingsTab() { autoFlush, debugMode: debug, datadogMode, + injectionVariant, + sdkInjectionType, }, setSetting, ] = useSettings() + const badgeStatus = () => { + const toBadge = (color: 'blue' | 'green' | 'yellow' | 'red', text: string) => {text} + + const overridden = useDevBundles && (injectionVariant === 'cdn' || sdkDevServerStatus === DevServerStatus.AVAILABLE) + if (overridden) { + return toBadge('blue', 'Overridden') + } + + if (injectionVariant === 'cdn') { + return toBadge('green', 'Available') + } + + switch (sdkDevServerStatus) { + case DevServerStatus.AVAILABLE: + return toBadge('green', 'Available') + case DevServerStatus.CHECKING: + return toBadge('yellow', 'Checking...') + default: + return toBadge('red', 'Unavailable') + } + } + return (
@@ -35,17 +64,7 @@ export function SettingsTab() { Browser SDK - - {sdkDevServerStatus === DevServerStatus.AVAILABLE && useDevBundles ? ( - Overridden - ) : sdkDevServerStatus === DevServerStatus.AVAILABLE ? ( - Available - ) : sdkDevServerStatus === DevServerStatus.CHECKING ? ( - Checking... - ) : ( - Unavailable - )} - + {badgeStatus()} @@ -56,6 +75,55 @@ export function SettingsTab() { + {datadogMode && ( + <> + + Injection variant: + { + setSetting('injectionVariant', value as InjectionVariant) + }} + /> + + } + description={<>} + /> + + {injectionVariant === 'cdn' && ( + + SDK injection type: + { + setSetting('sdkInjectionType', value as SdkInjectionType) + }} + /> + + } + description={<>} + /> + )} + + )} + diff --git a/developer-extension/src/panel/hooks/useSettings.ts b/developer-extension/src/panel/hooks/useSettings.ts index 732bb84279..437bcf2574 100644 --- a/developer-extension/src/panel/hooks/useSettings.ts +++ b/developer-extension/src/panel/hooks/useSettings.ts @@ -19,6 +19,8 @@ const DEFAULT_SETTINGS: Readonly = { logsConfigurationOverride: null, debugMode: false, datadogMode: false, + injectionVariant: 'local-dev', + sdkInjectionType: 'BOTH', } let settings: Settings | undefined @@ -46,9 +48,19 @@ async function loadSettingsFromStorage() { function setSetting(name: Name, value: Settings[Name]) { settings![name] = value + + const settingsToStore: Partial = { [name]: value } + + // Reset injectionVariant to default when Datadog mode is disabled + if (name === 'datadogMode' && value === false) { + settings!.injectionVariant = DEFAULT_SETTINGS.injectionVariant + settingsToStore.injectionVariant = DEFAULT_SETTINGS.injectionVariant + } + onSettingsChange.notify() + chrome.storage.local - .set({ [name]: value }) + .set(settingsToStore) .catch((error) => logger.error('Error while storing setting to the storage', error)) if (settings) { syncSettingsWithSessionStorage(settings) diff --git a/rum-events-format b/rum-events-format index fe242fe9a0..1e34da223d 160000 --- a/rum-events-format +++ b/rum-events-format @@ -1 +1 @@ -Subproject commit fe242fe9a02cc373e61127d7a2ef629991a5c28f +Subproject commit 1e34da223dbe809e9b8c90ad2109860ec149ce88