Skip to content

Commit 0fa9a4b

Browse files
feat(pr, header-capture): trackResourceHeaders parameter
1 parent 0a1350e commit 0fa9a4b

9 files changed

Lines changed: 193 additions & 24 deletions

File tree

packages/core/src/DdSdkReactNative.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,12 @@ export class DdSdkReactNative {
515515
const resourceTraceSampleRate =
516516
configuration.rumConfiguration?.resourceTraceSampleRate ||
517517
RUM_DEFAULTS.resourceTraceSampleRate;
518+
const trackResourceHeaders =
519+
configuration.rumConfiguration?.trackResourceHeaders ||
520+
RUM_DEFAULTS.trackResourceHeaders;
518521
const headerCaptureRules =
519-
configuration.rumConfiguration?.headerCaptureRules;
522+
configuration.rumConfiguration?.headerCaptureRules ||
523+
RUM_DEFAULTS.headerCaptureRules;
520524
const logEventMapper = configuration.logsConfiguration?.logEventMapper;
521525
const errorEventMapper =
522526
configuration.rumConfiguration?.errorEventMapper;
@@ -552,9 +556,9 @@ export class DdSdkReactNative {
552556
});
553557
}
554558

555-
if (!trackResources && headerCaptureRules != null) {
559+
if (trackResourceHeaders && !trackResources) {
556560
InternalLog.log(
557-
'headerCaptureRules is set but trackResources is false. Header capture will be disabled.',
561+
'trackResourceHeaders is set but trackResources is false. Header capture will be disabled.',
558562
SdkVerbosity.WARN
559563
);
560564
}
@@ -563,7 +567,9 @@ export class DdSdkReactNative {
563567
DdRumResourceTracking.startTracking({
564568
resourceTraceSampleRate,
565569
firstPartyHosts,
566-
headerCaptureRules
570+
headerCaptureRules: trackResourceHeaders
571+
? headerCaptureRules
572+
: undefined
567573
});
568574
}
569575

packages/core/src/__tests__/DdSdkReactNative.test.tsx

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { version as reactNativeVersion } from 'react-native/package.json';
88
import { NativeModules } from 'react-native';
99

1010
import { DdSdkReactNative } from '../DdSdkReactNative';
11+
import { InternalLog } from '../InternalLog';
1112
import type { DdSdkNativeConfiguration } from '../config/features/CoreConfigurationNative';
1213
import { CoreConfiguration } from '../config/features/CoreConfiguration';
1314
import { LogsConfiguration } from '../config/features/LogsConfiguration';
@@ -681,6 +682,130 @@ describe('DdSdkReactNative', () => {
681682
});
682683
});
683684

685+
it('enables header capture with the default headerCaptureRules when trackResourceHeaders is true', async () => {
686+
// GIVEN
687+
const fakeAppId = '1';
688+
const fakeClientToken = '2';
689+
const fakeEnvName = 'env';
690+
const configuration = new CoreConfiguration(
691+
fakeClientToken,
692+
fakeEnvName
693+
);
694+
configuration.rumConfiguration = new RumConfiguration(
695+
fakeAppId,
696+
false,
697+
true
698+
);
699+
configuration.rumConfiguration.trackResourceHeaders = true;
700+
701+
NativeModules.DdSdk.initialize.mockResolvedValue(null);
702+
703+
// WHEN
704+
await DdSdkReactNative.initialize(configuration);
705+
706+
// THEN
707+
expect(DdRumResourceTracking.startTracking).toHaveBeenCalledWith(
708+
expect.objectContaining({
709+
headerCaptureRules: 'defaults'
710+
})
711+
);
712+
});
713+
714+
it('uses the custom headerCaptureRules when trackResourceHeaders is true', async () => {
715+
// GIVEN
716+
const fakeAppId = '1';
717+
const fakeClientToken = '2';
718+
const fakeEnvName = 'env';
719+
const configuration = new CoreConfiguration(
720+
fakeClientToken,
721+
fakeEnvName
722+
);
723+
configuration.rumConfiguration = new RumConfiguration(
724+
fakeAppId,
725+
false,
726+
true
727+
);
728+
configuration.rumConfiguration.trackResourceHeaders = true;
729+
configuration.rumConfiguration.headerCaptureRules = [
730+
{ type: 'matchHeaders', headers: ['x-request-id'] }
731+
];
732+
733+
NativeModules.DdSdk.initialize.mockResolvedValue(null);
734+
735+
// WHEN
736+
await DdSdkReactNative.initialize(configuration);
737+
738+
// THEN
739+
expect(DdRumResourceTracking.startTracking).toHaveBeenCalledWith(
740+
expect.objectContaining({
741+
headerCaptureRules: [
742+
{ type: 'matchHeaders', headers: ['x-request-id'] }
743+
]
744+
})
745+
);
746+
});
747+
748+
it('ignores headerCaptureRules when trackResourceHeaders is false', async () => {
749+
// GIVEN
750+
const fakeAppId = '1';
751+
const fakeClientToken = '2';
752+
const fakeEnvName = 'env';
753+
const configuration = new CoreConfiguration(
754+
fakeClientToken,
755+
fakeEnvName
756+
);
757+
configuration.rumConfiguration = new RumConfiguration(
758+
fakeAppId,
759+
false,
760+
true
761+
);
762+
configuration.rumConfiguration.trackResourceHeaders = false;
763+
configuration.rumConfiguration.headerCaptureRules = [
764+
{ type: 'matchHeaders', headers: ['x-request-id'] }
765+
];
766+
767+
NativeModules.DdSdk.initialize.mockResolvedValue(null);
768+
769+
// WHEN
770+
await DdSdkReactNative.initialize(configuration);
771+
772+
// THEN
773+
expect(DdRumResourceTracking.startTracking).toHaveBeenCalledWith(
774+
expect.objectContaining({
775+
headerCaptureRules: undefined
776+
})
777+
);
778+
});
779+
780+
it('logs a warning and disables header capture when trackResourceHeaders is true but trackResources is false', async () => {
781+
// GIVEN
782+
const fakeAppId = '1';
783+
const fakeClientToken = '2';
784+
const fakeEnvName = 'env';
785+
const configuration = new CoreConfiguration(
786+
fakeClientToken,
787+
fakeEnvName
788+
);
789+
configuration.rumConfiguration = new RumConfiguration(
790+
fakeAppId,
791+
false,
792+
false
793+
);
794+
configuration.rumConfiguration.trackResourceHeaders = true;
795+
796+
NativeModules.DdSdk.initialize.mockResolvedValue(null);
797+
798+
// WHEN
799+
await DdSdkReactNative.initialize(configuration);
800+
801+
// THEN
802+
expect(DdRumResourceTracking.startTracking).not.toHaveBeenCalled();
803+
expect(InternalLog.log).toHaveBeenCalledWith(
804+
'trackResourceHeaders is set but trackResources is false. Header capture will be disabled.',
805+
SdkVerbosity.WARN
806+
);
807+
});
808+
684809
it('enables error tracking feature when initialize { error tracking config enabled }', async () => {
685810
// GIVEN
686811
const fakeAppId = '1';

packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ describe('DdSdkReactNativeConfiguration', () => {
6161
"customEndpoint": undefined,
6262
"errorEventMapper": null,
6363
"firstPartyHosts": [],
64-
"headerCaptureRules": undefined,
64+
"headerCaptureRules": "defaults",
6565
"initialResourceThreshold": undefined,
6666
"longTaskThresholdMs": 0,
6767
"nativeCrashReportEnabled": false,
@@ -78,6 +78,7 @@ describe('DdSdkReactNativeConfiguration', () => {
7878
"trackInteractions": false,
7979
"trackMemoryWarnings": true,
8080
"trackNonFatalAnrs": undefined,
81+
"trackResourceHeaders": false,
8182
"trackResources": false,
8283
"trackWatchdogTerminations": false,
8384
"useAccessibilityLabel": true,
@@ -206,7 +207,7 @@ describe('DdSdkReactNativeConfiguration', () => {
206207
],
207208
},
208209
],
209-
"headerCaptureRules": undefined,
210+
"headerCaptureRules": "defaults",
210211
"initialResourceThreshold": 0.123,
211212
"longTaskThresholdMs": 567,
212213
"nativeCrashReportEnabled": true,
@@ -223,6 +224,7 @@ describe('DdSdkReactNativeConfiguration', () => {
223224
"trackInteractions": true,
224225
"trackMemoryWarnings": true,
225226
"trackNonFatalAnrs": true,
227+
"trackResourceHeaders": false,
226228
"trackResources": true,
227229
"trackWatchdogTerminations": false,
228230
"useAccessibilityLabel": true,
@@ -310,7 +312,7 @@ describe('DdSdkReactNativeConfiguration', () => {
310312
"customEndpoint": undefined,
311313
"errorEventMapper": null,
312314
"firstPartyHosts": [],
313-
"headerCaptureRules": undefined,
315+
"headerCaptureRules": "defaults",
314316
"initialResourceThreshold": 0,
315317
"longTaskThresholdMs": false,
316318
"nativeCrashReportEnabled": false,
@@ -327,6 +329,7 @@ describe('DdSdkReactNativeConfiguration', () => {
327329
"trackInteractions": false,
328330
"trackMemoryWarnings": false,
329331
"trackNonFatalAnrs": false,
332+
"trackResourceHeaders": false,
330333
"trackResources": false,
331334
"trackWatchdogTerminations": false,
332335
"useAccessibilityLabel": false,

packages/core/src/config/__tests__/FileBasedConfiguration.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('FileBasedConfiguration', () => {
5555
],
5656
},
5757
],
58-
"headerCaptureRules": undefined,
58+
"headerCaptureRules": "defaults",
5959
"initialResourceThreshold": 456,
6060
"longTaskThresholdMs": 44,
6161
"nativeCrashReportEnabled": true,
@@ -72,6 +72,7 @@ describe('FileBasedConfiguration', () => {
7272
"trackInteractions": true,
7373
"trackMemoryWarnings": false,
7474
"trackNonFatalAnrs": true,
75+
"trackResourceHeaders": false,
7576
"trackResources": true,
7677
"trackWatchdogTerminations": true,
7778
"useAccessibilityLabel": false,
@@ -168,7 +169,7 @@ describe('FileBasedConfiguration', () => {
168169
],
169170
},
170171
],
171-
"headerCaptureRules": undefined,
172+
"headerCaptureRules": "defaults",
172173
"initialResourceThreshold": undefined,
173174
"longTaskThresholdMs": 44,
174175
"nativeCrashReportEnabled": false,
@@ -185,6 +186,7 @@ describe('FileBasedConfiguration', () => {
185186
"trackInteractions": true,
186187
"trackMemoryWarnings": true,
187188
"trackNonFatalAnrs": undefined,
189+
"trackResourceHeaders": false,
188190
"trackResources": true,
189191
"trackWatchdogTerminations": false,
190192
"useAccessibilityLabel": false,
@@ -233,7 +235,7 @@ describe('FileBasedConfiguration', () => {
233235
"customEndpoint": undefined,
234236
"errorEventMapper": null,
235237
"firstPartyHosts": [],
236-
"headerCaptureRules": undefined,
238+
"headerCaptureRules": "defaults",
237239
"initialResourceThreshold": undefined,
238240
"longTaskThresholdMs": 0,
239241
"nativeCrashReportEnabled": false,
@@ -250,6 +252,7 @@ describe('FileBasedConfiguration', () => {
250252
"trackInteractions": false,
251253
"trackMemoryWarnings": true,
252254
"trackNonFatalAnrs": undefined,
255+
"trackResourceHeaders": false,
253256
"trackResources": false,
254257
"trackWatchdogTerminations": false,
255258
"useAccessibilityLabel": true,

packages/core/src/config/async/AutoInstrumentationConfiguration.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type AutoInstrumentationConfiguration = {
2626
readonly actionNameAttribute?: string;
2727
readonly resourceTraceSampleRate?: number;
2828
readonly headerCaptureRules?: 'defaults' | HeaderCaptureRule[];
29+
readonly trackResourceHeaders?: boolean;
2930
readonly nativeCrashReportEnabled?: boolean;
3031
readonly nativeLongTaskThresholdMs?: number;
3132
readonly nativeViewTracking?: boolean;
@@ -58,6 +59,7 @@ export type AutoInstrumentationParameters = {
5859
readonly resourceEventMapper: ResourceEventMapper | null;
5960
readonly firstPartyHosts: FirstPartyHost[];
6061
readonly headerCaptureRules?: 'defaults' | HeaderCaptureRule[];
62+
readonly trackResourceHeaders: boolean;
6163
};
6264
readonly logsConfiguration?: {
6365
readonly logEventMapper: LogEventMapper | null;
@@ -118,7 +120,10 @@ export const addDefaultValuesToAutoInstrumentationConfiguration = (
118120
firstPartyHosts:
119121
features.rumConfiguration.firstPartyHosts ||
120122
RUM_DEFAULTS.getFirstPartyHosts(),
121-
headerCaptureRules: features.rumConfiguration.headerCaptureRules
123+
headerCaptureRules: features.rumConfiguration.headerCaptureRules,
124+
trackResourceHeaders:
125+
features.rumConfiguration.trackResourceHeaders ??
126+
RUM_DEFAULTS.trackResourceHeaders
122127
},
123128
logsConfiguration: {
124129
logEventMapper:

packages/core/src/config/features/RumConfiguration.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ const DEFAULTS = {
3838
trackInteractions: false,
3939
trackMemoryWarnings: true,
4040
trackNonFatalAnrs: undefined,
41-
headerCaptureRules: undefined as
41+
headerCaptureRules: 'defaults' as
4242
| 'defaults'
4343
| HeaderCaptureRule[]
4444
| undefined,
45+
trackResourceHeaders: false,
4546
trackResources: false,
4647
trackWatchdogTerminations: false,
4748
useAccessibilityLabel: true,
@@ -121,6 +122,9 @@ export class RumConfiguration implements RumConfigurationType {
121122
public headerCaptureRules: 'defaults' | HeaderCaptureRule[] | undefined =
122123
DEFAULTS.headerCaptureRules;
123124

125+
// Track Resource Headers enabled
126+
public trackResourceHeaders: boolean = DEFAULTS.trackResourceHeaders;
127+
124128
// Track Watchdog Terminations enabled
125129
public trackWatchdogTerminations: boolean =
126130
DEFAULTS.trackWatchdogTerminations;

packages/core/src/config/features/RumConfiguration.type.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,15 @@ export interface RumConfigurationOptions {
175175

176176
/**
177177
* Controls which resource headers the SDK captures on network requests.
178+
* Only takes effect when {@link RumConfigurationOptions.trackResourceHeaders}
179+
* is `true`.
178180
*
179-
* - **Omitted** (default): No headers are captured.
180-
* - `'defaults'`: Captures a predefined set of caching and content headers
181-
* across all URLs.
181+
* - `'defaults'` (default): Captures a predefined set of caching and content
182+
* headers across all URLs.
182183
* - `HeaderCaptureRule[]`: An array of composable rules. When multiple rules
183184
* match a URL their header sets are merged. If at least one scoped rule
184185
* (with explicit `forURLs` patterns) matches, catch-all rules (no `forURLs`
185186
* or `['*']`) are ignored for that URL.
186-
*
187-
* Requires `trackResources: true` to take effect.
188187
*/
189188
headerCaptureRules?: 'defaults' | HeaderCaptureRule[];
190189

@@ -248,6 +247,16 @@ export interface RumConfigurationOptions {
248247
*/
249248
trackNonFatalAnrs?: boolean;
250249

250+
/**
251+
* Enables capturing of HTTP request and response headers on RUM resource
252+
* events. Defaults to `false`.
253+
*
254+
* When enabled, the headers that get captured are controlled by
255+
* {@link RumConfigurationOptions.headerCaptureRules} (defaults to
256+
* `'defaults'`). Requires `trackResources: true` to take effect.
257+
*/
258+
trackResourceHeaders?: boolean;
259+
251260
/**
252261
* Enables tracking of app termination by the iOS watchdog.
253262
*/

0 commit comments

Comments
 (0)