Просмотр исходного кода

feat(metrics): Cardinality limit setting (#70214)

Add project setting for cardinality limit.

- Closes https://github.com/getsentry/sentry/issues/69299
ArthurKnaus 9 месяцев назад
Родитель
Сommit
d7b6714646

+ 1 - 0
fixtures/js-stubs/project.tsx

@@ -42,6 +42,7 @@ export function ProjectFixture(params: Partial<Project> = {}): Project {
     plugins: [],
     processingIssues: 0,
     relayPiiConfig: '',
+    relayCustomMetricCardinalityLimit: null,
     resolveAge: 0,
     safeFields: [],
     scrapeJavaScript: true,

+ 1 - 0
static/app/types/project.tsx

@@ -46,6 +46,7 @@ export type Project = {
   plugins: Plugin[];
 
   processingIssues: number;
+  relayCustomMetricCardinalityLimit: number | null;
   relayPiiConfig: string;
   resolveAge: number;
   safeFields: string[];

+ 74 - 0
static/app/views/settings/projectMetrics/cardinalityLimit.tsx

@@ -0,0 +1,74 @@
+import styled from '@emotion/styled';
+
+import Access from 'sentry/components/acl/access';
+import NumberField from 'sentry/components/forms/fields/numberField';
+import Form from 'sentry/components/forms/form';
+import LoadingIndicator from 'sentry/components/loadingIndicator';
+import Panel from 'sentry/components/panels/panel';
+import PanelBody from 'sentry/components/panels/panelBody';
+import PanelHeader from 'sentry/components/panels/panelHeader';
+import {t} from 'sentry/locale';
+import type {Project} from 'sentry/types/project';
+
+type Props = {
+  isLoading: boolean;
+  project: Project;
+};
+
+function transformData(data) {
+  const limit = data.relayCustomMetricCardinalityLimit;
+  return {
+    relayCustomMetricCardinalityLimit: limit === '' ? null : limit,
+  };
+}
+
+export function CardinalityLimit({project, isLoading}: Props) {
+  const endpoint = `/projects/${project.organization.slug}/${project.slug}/`;
+
+  return (
+    <Form
+      apiEndpoint={endpoint}
+      apiMethod="PUT"
+      saveOnBlur
+      initialData={{
+        relayCustomMetricCardinalityLimit: project.relayCustomMetricCardinalityLimit,
+      }}
+    >
+      <Panel>
+        <PanelHeader>{t('Limits')}</PanelHeader>
+        <PanelBody>
+          {isLoading ? (
+            <LoadingIndicator />
+          ) : (
+            <Access access={['project:write']} project={project}>
+              {({hasAccess}) => (
+                <StyledNumberField
+                  disabledReason={
+                    !hasAccess
+                      ? t('You do not have permission to edit the cardinality limit')
+                      : undefined
+                  }
+                  disabled={!hasAccess}
+                  name="relayCustomMetricCardinalityLimit"
+                  label={t('Cardinality Limit')}
+                  help={t(
+                    'The cardinality limit defines the maximum number of unique tag combinations allowed for a metric (measured per hour). If the cardinality limit is exceeded, the metric will be blocked.'
+                  )}
+                  saveOnBlur
+                  placeholder={t('Enter a value')}
+                  flexibleControlStateSize
+                  multiple
+                  getData={transformData}
+                />
+              )}
+            </Access>
+          )}
+        </PanelBody>
+      </Panel>
+    </Form>
+  );
+}
+
+const StyledNumberField = styled(NumberField)`
+  ${p => p.disabled && `cursor: not-allowed`}
+`;

+ 14 - 1
static/app/views/settings/projectMetrics/projectMetrics.tsx

@@ -31,6 +31,7 @@ import TextBlock from 'sentry/views/settings/components/text/textBlock';
 import PermissionAlert from 'sentry/views/settings/project/permissionAlert';
 import {useAccess} from 'sentry/views/settings/projectMetrics/access';
 import {BlockButton} from 'sentry/views/settings/projectMetrics/blockButton';
+import {CardinalityLimit} from 'sentry/views/settings/projectMetrics/cardinalityLimit';
 
 type Props = {
   organization: Organization;
@@ -106,11 +107,15 @@ function ProjectMetrics({project, location}: Props) {
 
       <PermissionAlert project={project} />
 
+      <CardinalityLimit project={project} isLoading={isLoading} />
+
       <SearchWrapper>
+        <h6>{t('Emitted Metrics')}</h6>
         <SearchBar
           placeholder={t('Search Metrics')}
           onChange={debouncedSearch}
           query={query}
+          size="sm"
         />
       </SearchWrapper>
 
@@ -224,7 +229,15 @@ const TabPanelsWrapper = styled(TabPanels)`
 `;
 
 const SearchWrapper = styled('div')`
-  margin-bottom: ${space(2)};
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  margin-top: ${space(4)};
+  margin-bottom: ${space(0)};
+
+  & > h6 {
+    margin: 0;
+  }
 `;
 
 const StyledPanelTable = styled(PanelTable)`