@@ -12,19 +12,27 @@ import {PanelTable} from 'sentry/components/panels/panelTable';
1212import SearchBar from 'sentry/components/searchBar' ;
1313import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle' ;
1414import { TabList , TabPanels , Tabs } from 'sentry/components/tabs' ;
15+ import { Tooltip } from 'sentry/components/tooltip' ;
1516import { DEFAULT_DEBOUNCE_DURATION } from 'sentry/constants' ;
17+ import { IconArrow , IconWarning } from 'sentry/icons' ;
1618import { t , tct } from 'sentry/locale' ;
1719import { space } from 'sentry/styles/space' ;
18- import type { MetricMeta , Organization , Project } from 'sentry/types' ;
19- import { browserHistory } from 'sentry/utils/browserHistory' ;
20- import { METRICS_DOCS_URL } from 'sentry/utils/metrics/constants' ;
20+ import type { MetricMeta } from 'sentry/types/metrics' ;
21+ import type { Organization } from 'sentry/types/organization' ;
22+ import type { Project } from 'sentry/types/project' ;
23+ import {
24+ DEFAULT_METRICS_CARDINALITY_LIMIT ,
25+ METRICS_DOCS_URL ,
26+ } from 'sentry/utils/metrics/constants' ;
2127import { getReadableMetricType } from 'sentry/utils/metrics/formatters' ;
2228import { formatMRI } from 'sentry/utils/metrics/mri' ;
2329import { useBlockMetric } from 'sentry/utils/metrics/useBlockMetric' ;
30+ import { useMetricsCardinality } from 'sentry/utils/metrics/useMetricsCardinality' ;
2431import { useMetricsMeta } from 'sentry/utils/metrics/useMetricsMeta' ;
2532import { decodeScalar } from 'sentry/utils/queryString' ;
2633import routeTitleGen from 'sentry/utils/routeTitle' ;
2734import { middleEllipsis } from 'sentry/utils/string/middleEllipsis' ;
35+ import { useNavigate } from 'sentry/utils/useNavigate' ;
2836import { useMetricsOnboardingSidebar } from 'sentry/views/metrics/ddmOnboarding/useMetricsOnboardingSidebar' ;
2937import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader' ;
3038import TextBlock from 'sentry/views/settings/components/text/textBlock' ;
@@ -43,35 +51,68 @@ enum BlockingStatusTab {
4351 DISABLED = 'disabled' ,
4452}
4553
54+ type MetricWithCardinality = MetricMeta & { cardinality : number } ;
55+
4656function ProjectMetrics ( { project, location} : Props ) {
47- const { data : meta , isLoading } = useMetricsMeta (
57+ const metricsMeta = useMetricsMeta (
4858 { projects : [ parseInt ( project . id , 10 ) ] } ,
4959 [ 'custom' ] ,
5060 false
5161 ) ;
62+
63+ const metricsCardinality = useMetricsCardinality ( {
64+ project,
65+ } ) ;
66+
67+ const sortedMeta = useMemo ( ( ) => {
68+ if ( ! metricsMeta . data ) {
69+ return [ ] ;
70+ }
71+
72+ if ( ! metricsCardinality . data ) {
73+ return metricsMeta . data . map ( meta => ( { ...meta , cardinality : 0 } ) ) ;
74+ }
75+
76+ return metricsMeta . data
77+ . map ( ( { mri, ...rest } ) => {
78+ return {
79+ mri,
80+ cardinality : metricsCardinality . data [ mri ] ?? 0 ,
81+ ...rest ,
82+ } ;
83+ } )
84+ . sort ( ( a , b ) => {
85+ return b . cardinality - a . cardinality ;
86+ } ) as MetricWithCardinality [ ] ;
87+ } , [ metricsCardinality . data , metricsMeta . data ] ) ;
88+
5289 const query = decodeScalar ( location . query . query , '' ) . trim ( ) ;
53- const { activateSidebar} = useMetricsOnboardingSidebar ( ) ;
54- const [ selectedTab , setSelectedTab ] = useState ( BlockingStatusTab . ACTIVE ) ;
5590
91+ const metrics = sortedMeta . filter (
92+ ( { mri, type, unit} ) =>
93+ mri . includes ( query ) ||
94+ getReadableMetricType ( type ) . includes ( query ) ||
95+ unit . includes ( query )
96+ ) ;
97+
98+ const isLoading = metricsMeta . isLoading || metricsCardinality . isLoading ;
99+
100+ const navigate = useNavigate ( ) ;
56101 const debouncedSearch = useMemo (
57102 ( ) =>
58103 debounce (
59104 ( searchQuery : string ) =>
60- browserHistory . replace ( {
105+ navigate ( {
61106 pathname : location . pathname ,
62107 query : { ...location . query , query : searchQuery } ,
63108 } ) ,
64109 DEFAULT_DEBOUNCE_DURATION
65110 ) ,
66- [ location . pathname , location . query ]
111+ [ location . pathname , location . query , navigate ]
67112 ) ;
68113
69- const metrics = meta . filter (
70- ( { mri, type, unit} ) =>
71- mri . includes ( query ) ||
72- getReadableMetricType ( type ) . includes ( query ) ||
73- unit . includes ( query )
74- ) ;
114+ const { activateSidebar} = useMetricsOnboardingSidebar ( ) ;
115+ const [ selectedTab , setSelectedTab ] = useState ( BlockingStatusTab . ACTIVE ) ;
75116
76117 return (
77118 < Fragment >
@@ -151,21 +192,27 @@ function ProjectMetrics({project, location}: Props) {
151192
152193interface MetricsTableProps {
153194 isLoading : boolean ;
154- metrics : MetricMeta [ ] ;
195+ metrics : MetricWithCardinality [ ] ;
155196 project : Project ;
156197 query : string ;
157198}
158199
159200function MetricsTable ( { metrics, isLoading, query, project} : MetricsTableProps ) {
160201 const blockMetricMutation = useBlockMetric ( project ) ;
161202 const { hasAccess} = useAccess ( { access : [ 'project:write' ] } ) ;
203+ const cardinalityLimit =
204+ project . relayCustomMetricCardinalityLimit ?? DEFAULT_METRICS_CARDINALITY_LIMIT ;
162205
163206 return (
164207 < StyledPanelTable
165208 headers = { [
166209 t ( 'Metric' ) ,
210+ < Cell right key = "cardinality" >
211+ < IconArrow size = "xs" direction = "down" />
212+
213+ { t ( 'Cardinality' ) }
214+ </ Cell > ,
167215 < Cell right key = "type" >
168- { ' ' }
169216 { t ( 'Type' ) }
170217 </ Cell > ,
171218 < Cell right key = "unit" >
@@ -183,8 +230,9 @@ function MetricsTable({metrics, isLoading, query, project}: MetricsTableProps) {
183230 isEmpty = { metrics . length === 0 }
184231 isLoading = { isLoading }
185232 >
186- { metrics . map ( ( { mri, type, unit, blockingStatus} ) => {
233+ { metrics . map ( ( { mri, type, unit, cardinality , blockingStatus} ) => {
187234 const isBlocked = blockingStatus [ 0 ] ?. isBlocked ;
235+ const isCardinalityLimited = cardinality >= cardinalityLimit ;
188236 return (
189237 < Fragment key = { mri } >
190238 < Cell >
@@ -196,6 +244,19 @@ function MetricsTable({metrics, isLoading, query, project}: MetricsTableProps) {
196244 { middleEllipsis ( formatMRI ( mri ) , 65 , / \. | - | _ / ) }
197245 </ Link >
198246 </ Cell >
247+ < Cell right >
248+ { isCardinalityLimited && (
249+ < Tooltip
250+ title = { tct (
251+ 'The tag cardinality of this metric exceeded our limit of [cardinalityLimit], which led to the data being dropped' ,
252+ { cardinalityLimit}
253+ ) }
254+ >
255+ < StyledIconWarning size = "sm" color = "red300" />
256+ </ Tooltip >
257+ ) }
258+ { cardinality }
259+ </ Cell >
199260 < Cell right >
200261 < Tag > { getReadableMetricType ( type ) } </ Tag >
201262 </ Cell >
@@ -241,14 +302,22 @@ const SearchWrapper = styled('div')`
241302` ;
242303
243304const StyledPanelTable = styled ( PanelTable ) `
244- grid-template-columns: 1fr repeat(3, minmax(115px, min-content) );
305+ grid-template-columns: 1fr repeat(4, min-content);
245306` ;
246307
247308const Cell = styled ( 'div' ) < { right ?: boolean } > `
248309 display: flex;
249310 align-items: center;
250311 align-self: stretch;
312+ gap: ${ space ( 0.5 ) } ;
251313 justify-content: ${ p => ( p . right ? 'flex-end' : 'flex-start' ) } ;
252314` ;
253315
316+ const StyledIconWarning = styled ( IconWarning ) `
317+ margin-top: ${ space ( 0.5 ) } ;
318+ &:hover {
319+ cursor: pointer;
320+ }
321+ ` ;
322+
254323export default ProjectMetrics ;
0 commit comments