Skip to content
5 changes: 5 additions & 0 deletions developer-extension/src/common/extension.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -57,4 +60,6 @@ export interface Settings {
logsConfigurationOverride: object | null
debugMode: boolean
datadogMode: boolean
injectionVariant: InjectionVariant
sdkInjectionType: SdkInjectionType
Comment on lines +63 to +64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔨 warning: ‏The settings should be redesigned to make it more explicit and ideally avoid forbidden states. Things that should be clarified at the settings level:

  • useDevBundles: npm won't work if injectionVariant is not cdn.
  • How can I disable injection?
  • Why injection variant CDN only available in datadogMode?
  • What's the difference between injectionVariant and sdkInjectionType just by looking at property names?

My suggestion: keep things simple. Always inject both RUM and Logs CDN bundles with a default config. If dev bundle override is enabled, those bundles will be overridden (including rum-slim override). If config override is enabled, the config will be overridden. No need to do anything at the injection level.

So in the end you have a single option, inject: boolean. No need to enforce datadogMode usage, people can use it it's fine.

}
7 changes: 7 additions & 0 deletions developer-extension/src/common/packagesUrlConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
119 changes: 114 additions & 5 deletions developer-extension/src/content-scripts/main.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -11,6 +12,8 @@ declare global {
}
}

const logger = createLogger('content-script-main')

interface SdkPublicApi {
[key: string]: (...args: any[]) => unknown
}
Expand All @@ -22,7 +25,6 @@ function main() {
}

sendEventsToExtension()

const settings = getSettings()

if (
Expand All @@ -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)
}
}
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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`
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per Benoit's good suggestion. This will be removed and use US1 prod.


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)
}
}
}
92 changes: 80 additions & 12 deletions developer-extension/src/panel/components/tabs/settingsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) => <Badge color={color}>{text}</Badge>

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 (
<TabBase>
<div className="dd-privacy-allow">
Expand All @@ -35,17 +64,7 @@ export function SettingsTab() {
<Accordion.Control>
<Group>
<Text>Browser SDK</Text>
<Box style={{ marginLeft: 'auto' }}>
{sdkDevServerStatus === DevServerStatus.AVAILABLE && useDevBundles ? (
<Badge color="blue">Overridden</Badge>
) : sdkDevServerStatus === DevServerStatus.AVAILABLE ? (
<Badge color="green">Available</Badge>
) : sdkDevServerStatus === DevServerStatus.CHECKING ? (
<Badge color="yellow">Checking...</Badge>
) : (
<Badge color="red">Unavailable</Badge>
)}
</Box>
<Box style={{ marginLeft: 'auto' }}>{badgeStatus()}</Box>
</Group>
</Accordion.Control>
<Accordion.Panel>
Expand All @@ -56,6 +75,55 @@ export function SettingsTab() {

<Space h="md" />

{datadogMode && (
<>
<SettingItem
input={
<Group>
<Text>Injection variant:</Text>
<SegmentedControl
color="violet"
value={injectionVariant}
size="xs"
data={[
{ value: 'local-dev', label: 'Local Dev' },
{ value: 'cdn', label: 'CDN' },
]}
onChange={(value) => {
setSetting('injectionVariant', value as InjectionVariant)
}}
/>
</Group>
}
description={<></>}
/>

{injectionVariant === 'cdn' && (
<SettingItem
input={
<Group>
<Text>SDK injection type:</Text>
<SegmentedControl
color="violet"
value={sdkInjectionType}
size="xs"
data={[
{ value: 'RUM', label: 'RUM' },
{ value: 'LOGS', label: 'LOGS' },
{ value: 'BOTH', label: 'BOTH' },
]}
onChange={(value) => {
setSetting('sdkInjectionType', value as SdkInjectionType)
}}
/>
</Group>
}
description={<></>}
/>
)}
</>
)}

<SettingItem
input={
<Group>
Expand Down
14 changes: 13 additions & 1 deletion developer-extension/src/panel/hooks/useSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const DEFAULT_SETTINGS: Readonly<Settings> = {
logsConfigurationOverride: null,
debugMode: false,
datadogMode: false,
injectionVariant: 'local-dev',
sdkInjectionType: 'BOTH',
}

let settings: Settings | undefined
Expand Down Expand Up @@ -46,9 +48,19 @@ async function loadSettingsFromStorage() {

function setSetting<Name extends keyof Settings>(name: Name, value: Settings[Name]) {
settings![name] = value

const settingsToStore: Partial<Settings> = { [name]: value }

// Reset injectionVariant to default when Datadog mode is disabled
if (name === 'datadogMode' && value === false) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable anymore.

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)
Expand Down