Skip to content

Commit c35b4bd

Browse files
committed
feat(tracemetrics): Add PII scrubbing UI
Adds another dataset to the advanced pii scrubbing modal, and intreprets $metric rules as relating to the metric dataset. Similar restrictions as with logs apply. This is behind the
1 parent 04eb17a commit c35b4bd

File tree

6 files changed

+72
-16
lines changed

6 files changed

+72
-16
lines changed

static/app/views/explore/metrics/metricsFlags.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,10 @@ export const canUseMetricsEquations = (organization: Organization) => {
4141
organization.features.includes('tracemetrics-equations-in-explore')
4242
);
4343
};
44+
45+
export const canUseMetricsPiiScrubbingUI = (organization: Organization) => {
46+
return (
47+
canUseMetricsUI(organization) &&
48+
organization.features.includes('tracemetrics-pii-scrubbing-ui')
49+
);
50+
};

static/app/views/settings/components/dataScrubbing/modals/dataScrubFormModal.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {IconChevron} from 'sentry/icons';
2323
import {t} from 'sentry/locale';
2424
import type {Project} from 'sentry/types/project';
2525
import {useOrganization} from 'sentry/utils/useOrganization';
26+
import {canUseMetricsPiiScrubbingUI} from 'sentry/views/explore/metrics/metricsFlags';
2627
import {submitRules} from 'sentry/views/settings/components/dataScrubbing/submitRules';
2728
import type {
2829
EditableRule,
@@ -97,6 +98,13 @@ export function DataScrubFormModal({
9798
}: DataScrubFormModalProps) {
9899
const organization = useOrganization();
99100
const traceItemDatasetsEnabled = areScrubbingDatasetsEnabled(organization);
101+
const enabledDatasets = [AllowedDataScrubbingDatasets.DEFAULT];
102+
if (organization.features.includes('ourlogs-enabled')) {
103+
enabledDatasets.push(AllowedDataScrubbingDatasets.LOGS);
104+
}
105+
if (canUseMetricsPiiScrubbingUI(organization)) {
106+
enabledDatasets.push(AllowedDataScrubbingDatasets.METRICS);
107+
}
100108
const {sourceGroupData} = useSourceGroupData();
101109

102110
// Compute initial dataset from initialState
@@ -250,7 +258,7 @@ export function DataScrubFormModal({
250258
variant="compact"
251259
>
252260
<Flex gap="lg">
253-
{sortBy(Object.values(AllowedDataScrubbingDatasets)).map(value => (
261+
{sortBy(enabledDatasets).map(value => (
254262
<field.Radio.Item key={value} value={value}>
255263
{getDatasetLabelLong(value)}
256264
</field.Radio.Item>

static/app/views/settings/components/dataScrubbing/modals/form/attributeField.tsx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ import {
2121
} from 'sentry/views/settings/components/dataScrubbing/types';
2222
import {TraceItemFieldSelector} from 'sentry/views/settings/components/dataScrubbing/utils';
2323

24+
const datasetToTraceItemType: Record<
25+
Exclude<AllowedDataScrubbingDatasets, AllowedDataScrubbingDatasets.DEFAULT>,
26+
TraceItemDataset
27+
> = {
28+
[AllowedDataScrubbingDatasets.LOGS]: TraceItemDataset.LOGS,
29+
[AllowedDataScrubbingDatasets.METRICS]: TraceItemDataset.TRACEMETRICS,
30+
};
31+
32+
const HIDDEN_SCRUBBING_ATTRIBUTES: Partial<
33+
Record<AllowedDataScrubbingDatasets, Set<string>>
34+
> = {
35+
[AllowedDataScrubbingDatasets.METRICS]: new Set(['metric.name']),
36+
};
37+
2438
type FieldProps = {
2539
'aria-describedby': string;
2640
'aria-invalid': boolean;
@@ -52,10 +66,14 @@ export function AttributeField({
5266
const [showSuggestions, setShowSuggestions] = useState(false);
5367
const [activeSuggestion, setActiveSuggestion] = useState(0);
5468

69+
const traceItemType =
70+
dataset === AllowedDataScrubbingDatasets.DEFAULT
71+
? TraceItemDataset.LOGS
72+
: datasetToTraceItemType[dataset];
5573
const traceItemAttributeStringsResult = useTraceItemAttributeKeys({
5674
enabled: true,
5775
type: 'string',
58-
traceItemType: TraceItemDataset.LOGS,
76+
traceItemType,
5977
projects: project ? [project] : undefined,
6078
});
6179
const [suggestedAttributeValues, setSuggestedAttributeValues] = useLocalStorageState(
@@ -100,16 +118,21 @@ export function AttributeField({
100118
return [];
101119
}
102120

121+
const hidden = HIDDEN_SCRUBBING_ATTRIBUTES[dataset];
122+
103123
return [
104124
...TraceItemFieldSelector.getAllStaticFields(dataset).map(staticField => ({
105125
value: staticField.fieldName,
106126
label: staticField.fieldName,
107127
})),
108-
...traceItemFields.map(field => ({
109-
value: field.label,
110-
label:
111-
TraceItemFieldSelector.fromField(dataset, field.key)?.toLabel() ?? field.label,
112-
})),
128+
...traceItemFields
129+
.filter(field => !hidden?.has(field.key))
130+
.map(field => ({
131+
value: field.label,
132+
label:
133+
TraceItemFieldSelector.fromField(dataset, field.key)?.toLabel() ??
134+
field.label,
135+
})),
113136
];
114137
}, [dataset, suggestedAttributeValues]);
115138

static/app/views/settings/components/dataScrubbing/types.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ export enum AllowedDataScrubbingDatasets {
132132
DEFAULT = 'default',
133133
// This is the dataset that is used for data scrubbing. When this is selected, the user will be shown a trace item attribute picker.
134134
LOGS = 'logs',
135+
// Trace metrics dataset. When selected, the user will be shown a trace item attribute picker for metrics.
136+
METRICS = 'metrics',
135137
}
136138

137139
export type PiiConfig =

static/app/views/settings/components/dataScrubbing/utils.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as Sentry from '@sentry/react';
33
import {t} from 'sentry/locale';
44
import type {Organization} from 'sentry/types/organization';
55
import type {useTraceItemAttributeKeys} from 'sentry/views/explore/hooks/useTraceItemAttributeKeys';
6+
import {canUseMetricsPiiScrubbingUI} from 'sentry/views/explore/metrics/metricsFlags';
67

78
import {
89
AllowedDataScrubbingDatasets,
@@ -82,6 +83,7 @@ function getDatasetLabel(dataset: AllowedDataScrubbingDatasets) {
8283
const labelMap: Record<AllowedDataScrubbingDatasets, string> = {
8384
[AllowedDataScrubbingDatasets.DEFAULT]: t('Events'),
8485
[AllowedDataScrubbingDatasets.LOGS]: t('Logs'),
86+
[AllowedDataScrubbingDatasets.METRICS]: t('Metrics'),
8587
};
8688
return labelMap[dataset];
8789
}
@@ -93,6 +95,7 @@ export function getDatasetLabelLong(dataset: AllowedDataScrubbingDatasets) {
9395
const labelMap: Record<AllowedDataScrubbingDatasets, string> = {
9496
[AllowedDataScrubbingDatasets.DEFAULT]: t('Errors, Transactions, Attachments'),
9597
[AllowedDataScrubbingDatasets.LOGS]: t('Logs'),
98+
[AllowedDataScrubbingDatasets.METRICS]: t('Metrics'),
9699
};
97100
return labelMap[dataset];
98101
}
@@ -244,8 +247,10 @@ export function getRuleDescription(rule: Rule) {
244247
* This indicates whether the data scrubbing modal shows "dataset" selector, which can be used to select new datasets that only allow for TraceItem attribute scrubbing.
245248
*/
246249
export function areScrubbingDatasetsEnabled(organization: Organization) {
247-
// Currently only logs supports scrubbing datasets.
248-
return organization.features.includes('ourlogs-enabled');
250+
return (
251+
organization.features.includes('ourlogs-enabled') ||
252+
canUseMetricsPiiScrubbingUI(organization)
253+
);
249254
}
250255

251256
function getSourceLabel(rule: Rule) {
@@ -283,6 +288,11 @@ export class TraceItemFieldSelector {
283288
fieldName: 'message',
284289
},
285290
],
291+
[AllowedDataScrubbingDatasets.METRICS]: [
292+
{
293+
regex: /^\$metric\.attributes\.'([^']+)'\.value$/,
294+
},
295+
],
286296
[AllowedDataScrubbingDatasets.DEFAULT]: [],
287297
};
288298

@@ -291,6 +301,7 @@ export class TraceItemFieldSelector {
291301
string | null
292302
> = {
293303
[AllowedDataScrubbingDatasets.LOGS]: '$log',
304+
[AllowedDataScrubbingDatasets.METRICS]: '$metric',
294305
[AllowedDataScrubbingDatasets.DEFAULT]: null,
295306
};
296307

@@ -304,6 +315,9 @@ export class TraceItemFieldSelector {
304315
}
305316
return `$log.attributes.'${alias}'.value`;
306317
},
318+
[AllowedDataScrubbingDatasets.METRICS]: (alias: string) => {
319+
return `$metric.attributes.'${alias}'.value`;
320+
},
307321
[AllowedDataScrubbingDatasets.DEFAULT]: () => null,
308322
};
309323

@@ -498,15 +512,15 @@ export class TraceItemFieldSelector {
498512
label: t('body'),
499513
},
500514
],
515+
[AllowedDataScrubbingDatasets.METRICS]: null,
501516
[AllowedDataScrubbingDatasets.DEFAULT]: null,
502517
};
503-
if (
504-
dataset === AllowedDataScrubbingDatasets.DEFAULT ||
505-
!nonAttributeFields[dataset]
506-
) {
507-
Sentry.captureException(
508-
new Error('Non-attribute fields should not be used for event selectors')
509-
);
518+
if (!nonAttributeFields[dataset]) {
519+
if (dataset === AllowedDataScrubbingDatasets.DEFAULT) {
520+
Sentry.captureException(
521+
new Error('Non-attribute fields should not be used for event selectors')
522+
);
523+
}
510524
return null;
511525
}
512526
return nonAttributeFields[dataset];

tests/js/fixtures/log.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,13 @@ export function createMockAttributeResults(empty = false): AttributeResults {
424424
return {
425425
[AllowedDataScrubbingDatasets.DEFAULT]: null,
426426
[AllowedDataScrubbingDatasets.LOGS]: mockTraceItemAttributeKeysEmptyResult,
427+
[AllowedDataScrubbingDatasets.METRICS]: mockTraceItemAttributeKeysEmptyResult,
427428
};
428429
}
429430

430431
return {
431432
[AllowedDataScrubbingDatasets.DEFAULT]: null,
432433
[AllowedDataScrubbingDatasets.LOGS]: mockTraceItemAttributeKeysResult,
434+
[AllowedDataScrubbingDatasets.METRICS]: mockTraceItemAttributeKeysResult,
433435
};
434436
}

0 commit comments

Comments
 (0)