Skip to content

Instantly share code, notes, and snippets.

@wcastand
Created June 13, 2025 16:14
Show Gist options
  • Select an option

  • Save wcastand/8017632b103639f76e22797bbaaae2ea to your computer and use it in GitHub Desktop.

Select an option

Save wcastand/8017632b103639f76e22797bbaaae2ea to your computer and use it in GitHub Desktop.
import type { ExpoConfig } from "@expo/config"
const DATADOG_HOST_URLS = [
{ match: "google.com, propagatorTypes: ["TRACECONTEXT"] },
]
export default (): ExpoConfig => ({
// your config
plugins: [
// your plugins
[
"./plugins/build/init-datadog-native.js",
{
applicationId: process.env.EXPO_PUBLIC_DATADOG_RUM_APPLICATION_ID,
batchSize: "SMALL",
clientToken: process.env.EXPO_PUBLIC_DATADOG_CLIENT_TOKEN,
env: process.env.EXPO_PUBLIC_APP_VARIANT ?? "development",
firstPartyHosts: DATADOG_HOST_URLS,
longTaskThresholdMs: 1000,
nativeCrashReportEnabled: true,
site: "EU1",
trackBackgroundEvents: false,
trackErrors: true,
trackInteractions: false,
trackResources: false,
uploadFrequency: "FREQUENT",
},
],
["expo-datadog"],
],
})
import { DdRum, ErrorSource, FileBasedConfiguration, RumActionType } from "expo-datadog"
import configuration from "../../datadog-configuration.json"
// @ts-expect-error not sure why datadog is fucked
export const config = new FileBasedConfiguration(configuration.configuration)
import { mkdirSync, writeFileSync } from "node:fs"
import { join, relative } from "node:path"
import {
type ConfigPlugin,
createRunOncePlugin,
IOSConfig,
withAppDelegate,
withDangerousMod,
withMainApplication,
withXcodeProject,
type XcodeProject,
} from "@expo/config-plugins"
import { addImports, findNewInstanceCodeBlock } from "@expo/config-plugins/build/android/codeMod"
import { insertContentsInsideSwiftFunctionBlock } from "@expo/config-plugins/build/ios/codeMod"
import { insertContentsAtOffset } from "@expo/config-plugins/build/utils/commonCodeMod"
import { mergeContents } from "@expo/config-plugins/build/utils/generateCode"
type DatadogConfig = {
clientToken: string
env: string
applicationId: string
nativeCrashReportEnabled: string
nativeLongTaskThresholdMs: number
longTaskThresholdMs: number
sessionSamplingRate: number
site: "US1" | "US1_FED" | "US3" | "US5" | "EU1" | "AP1"
trackingConsent: "PENDING" | "GRANTED" | "NOT_GRANTED"
telemetrySampleRate: number
vitalsUpdateFrequency: "NEVER" | "RARE" | "AVERAGE" | "FREQUENT"
uploadFrequency: "RARE" | "AVERAGE" | "FREQUENT"
batchSize: "SMALL" | "MEDIUM" | "LARGE"
trackFrustrations: boolean
trackBackgroundEvents: boolean
customEndpoints?: {
rum?: string
logs?: string
trace?: string
}
nativeViewTracking: boolean
nativeInteractionTracking: boolean
verbosity: "DEBUG" | "INFO" | "WARN" | "ERROR"
proxy?: {
type: "HTTP" | "HTTPS" | "SOCKS"
address: string
port: number
username?: string
password?: string
}
serviceName?: string
version?: string
firstPartyHosts?: Array<{
match: string
propagatorTypes?: Array<"DATADOG" | "B3" | "B3MULTI" | "TRACECONTEXT">
}>
trackInteractions: boolean
trackResources: boolean
trackErrors: boolean
actionNameAttribute?: string
useAccessibilityLabel?: boolean
resourceTracingSamplingRate?: number
bundleLogsWithRum?: boolean
bundleLogsWithTraces?: boolean
appHangThreshold?: number
trackNonFatalAnrs?: boolean
initialResourceThreshold?: number
}
function addResourceFiles(project: XcodeProject, platformRoot: string, assets: string[]) {
for (const asset of assets) {
const assetPath = relative(platformRoot, asset)
IOSConfig.XcodeUtils.addResourceFileToGroup({
filepath: assetPath,
groupName: "Resources",
isBuildFile: true,
project,
verbose: true,
})
}
}
const withAndroid: ConfigPlugin = (config) => {
return withMainApplication(config, async (config) => {
config.modResults.contents = addImports(
config.modResults.contents,
["com.datadog.reactnative.DdSdkNativeInitialization"],
false,
)
const addon = "DdSdkNativeInitialization.initFromNative(this.applicationContext)\n"
const res = findNewInstanceCodeBlock(config.modResults.contents, "onCreate", "kt")
if (!config.modResults.contents.includes(addon) && res) {
const q = "super.onCreate()"
const index = res.code.indexOf(q)
config.modResults.contents = insertContentsAtOffset(
config.modResults.contents,
addon,
res.start + index + q.length + 1,
)
}
return config
})
}
const withIos: ConfigPlugin = (config) => {
return withAppDelegate(config, (config) => {
let stringContents = config.modResults.contents
stringContents = mergeContents({
anchor: /import Expo/,
comment: "//",
newSrc: "import DatadogSDKReactNative",
offset: 1,
src: stringContents,
tag: "ios-datadog-import",
}).contents
stringContents = insertContentsInsideSwiftFunctionBlock(
stringContents,
"application(_:didFinishLaunchingWithOptions:)",
"DdSdk.initFromNative()",
{ position: "head" },
)
config.modResults.contents = stringContents
return config
})
}
const withDatadogConfig: ConfigPlugin<DatadogConfig> = (c, datadogConfig) => {
let config = c
const localConfigPath = "datadog-configuration.json"
// create file in src/config/datadog-configuration.json
// this file will be used to initialize the Datadog SDK in the native code
config = withDangerousMod(config, [
"android",
(config) => {
const configFilePath = join(config.modRequest.projectRoot, localConfigPath)
writeFileSync(
configFilePath,
`{
"$schema": "./node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json",
"configuration": ${JSON.stringify(datadogConfig, null, 2)}
}
`,
)
return config
},
])
config = withDangerousMod(config, [
"android",
(config) => {
const fileName = "datadog-configuration.json"
const assetDir = join(config.modRequest.platformProjectRoot, "app/src/main/assets/")
const assetPath = join(assetDir, fileName)
mkdirSync(assetDir, { recursive: true })
writeFileSync(
assetPath,
`{
"$schema": "./node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json",
"configuration": ${JSON.stringify(datadogConfig, null, 2)}
}
`,
)
return config
},
])
config = withXcodeProject(config, (config) => {
const project = config.modResults
const configFilePath = join(config.modRequest.projectRoot, localConfigPath)
addResourceFiles(project, config.modRequest.platformProjectRoot, [configFilePath])
return config
})
return config
}
const withDatadogInit: ConfigPlugin<DatadogConfig> = (c, datadogConfig) => {
let config = c
config = withDatadogConfig(config, datadogConfig)
config = withAndroid(config)
config = withIos(config)
return config
}
export default createRunOncePlugin(withDatadogInit, "DatadogInitNative", "1.0.0")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment