diff --git a/static/app/utils/fields/getAttributeSearchMetadataKey.spec.ts b/static/app/utils/fields/getAttributeSearchMetadataKey.spec.ts new file mode 100644 index 000000000000..28523b50dd4b --- /dev/null +++ b/static/app/utils/fields/getAttributeSearchMetadataKey.spec.ts @@ -0,0 +1,26 @@ +import {getAttributeSearchMetadataKey} from 'sentry/utils/fields/getAttributeSearchMetadataKey'; + +describe('getAttributeSearchMetadataKey', () => { + it('returns the canonical metadata key', () => { + expect(getAttributeSearchMetadataKey('http.request.method')).toBe( + 'http.request.method' + ); + }); + + it('returns the canonical metadata key for a deprecated key', () => { + expect(getAttributeSearchMetadataKey('http.method')).toBe('http.request.method'); + }); + + it('unwraps typed tag keys', () => { + expect(getAttributeSearchMetadataKey('tags[http.method,string]')).toBe( + 'http.request.method' + ); + }); + + it('returns the requested key when it has no search metadata', () => { + expect(getAttributeSearchMetadataKey('tags[unknown.attribute,string]')).toBe( + 'tags[unknown.attribute,string]' + ); + expect(getAttributeSearchMetadataKey('unknown.attribute')).toBe('unknown.attribute'); + }); +}); diff --git a/static/app/utils/fields/getAttributeSearchMetadataKey.ts b/static/app/utils/fields/getAttributeSearchMetadataKey.ts new file mode 100644 index 000000000000..b5ffa3e71c15 --- /dev/null +++ b/static/app/utils/fields/getAttributeSearchMetadataKey.ts @@ -0,0 +1,27 @@ +import {ATTRIBUTE_SEARCH_METADATA} from '@sentry/conventions'; + +const TYPED_TAG_KEY_RE = /tags\[(\S*),(\S*)\]/; +const ATTRIBUTE_SEARCH_METADATA_KEY_BY_KEY = new Map(); + +/** + * Returns the canonical attribute key for an attribute key or one of its + * deprecated aliases, or the requested key when no metadata exists. Metadata + * lookups, including misses, are cached. + */ +export function getAttributeSearchMetadataKey(key: string): string { + const unwrappedKey = key.match(TYPED_TAG_KEY_RE)?.[1] ?? key; + const cachedMetadataKey = ATTRIBUTE_SEARCH_METADATA_KEY_BY_KEY.get(unwrappedKey); + if (ATTRIBUTE_SEARCH_METADATA_KEY_BY_KEY.has(unwrappedKey)) { + return cachedMetadataKey ?? key; + } + + const metadata = + ATTRIBUTE_SEARCH_METADATA[unwrappedKey] ?? + Object.values(ATTRIBUTE_SEARCH_METADATA).find(({deprecationChain}) => + deprecationChain.includes(unwrappedKey) + ); + const metadataKey = metadata?.canonicalName; + + ATTRIBUTE_SEARCH_METADATA_KEY_BY_KEY.set(unwrappedKey, metadataKey); + return metadataKey ?? key; +} diff --git a/static/app/views/performance/newTraceDetails/index.tsx b/static/app/views/performance/newTraceDetails/index.tsx index 754ee6559f18..6dbf1becf806 100644 --- a/static/app/views/performance/newTraceDetails/index.tsx +++ b/static/app/views/performance/newTraceDetails/index.tsx @@ -10,6 +10,7 @@ import {SentryDocumentTitle} from 'sentry/components/sentryDocumentTitle'; import {IconClose} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import type {Organization} from 'sentry/types/organization'; +import {getAttributeSearchMetadataKey} from 'sentry/utils/fields/getAttributeSearchMetadataKey'; import {useDismissAlert} from 'sentry/utils/useDismissAlert'; import {useFeedbackForm} from 'sentry/utils/useFeedbackForm'; import {useHasProjectAccess} from 'sentry/utils/useHasProjectAccess'; @@ -120,11 +121,11 @@ function TraceViewImplInner({traceSlug}: {traceSlug: string}) { traceSlug, timestamp: queryParams.timestamp, additionalAttributes: [ - 'thread.id', - 'tags[performance.timeOrigin,number]', - 'gen_ai.operation.type', - 'http.response.status_code', - 'span.status', + getAttributeSearchMetadataKey('thread.id'), + getAttributeSearchMetadataKey('tags[performance.timeOrigin,number]'), + getAttributeSearchMetadataKey('gen_ai.operation.type'), + getAttributeSearchMetadataKey('http.response.status_code'), + getAttributeSearchMetadataKey('span.status'), ], }); const tree = useTraceTree({trace, replay: null});