diff --git a/static/app/components/performance/spanSearchQueryBuilder.tsx b/static/app/components/performance/spanSearchQueryBuilder.tsx
index 887c64a9580817..ffe19b36214e72 100644
--- a/static/app/components/performance/spanSearchQueryBuilder.tsx
+++ b/static/app/components/performance/spanSearchQueryBuilder.tsx
@@ -179,7 +179,12 @@ export function EAPSpanSearchQueryBuilder({
SPANS_FILTER_KEY_SECTIONS.flatMap(section => section.children)
);
return [
- ...SPANS_FILTER_KEY_SECTIONS,
+ ...SPANS_FILTER_KEY_SECTIONS.map(section => {
+ return {
+ ...section,
+ children: section.children.filter(key => stringTags.hasOwnProperty(key)),
+ };
+ }),
{
value: 'custom_fields',
label: 'Custom Tags',
diff --git a/static/app/utils/discover/fieldRenderers.tsx b/static/app/utils/discover/fieldRenderers.tsx
index 4577ab1d61fe54..876a62dc7bc210 100644
--- a/static/app/utils/discover/fieldRenderers.tsx
+++ b/static/app/utils/discover/fieldRenderers.tsx
@@ -366,7 +366,6 @@ type SpecialFields = {
replayId: SpecialField;
'span.description': SpecialField;
'span.status_code': SpecialField;
- span_id: SpecialField;
team_key_transaction: SpecialField;
'timestamp.to_day': SpecialField;
'timestamp.to_hour': SpecialField;
@@ -482,17 +481,6 @@ const SPECIAL_FIELDS: SpecialFields = {
return {getShortEventId(id)};
},
},
- span_id: {
- sortField: 'span_id',
- renderFunc: data => {
- const spanId: string | unknown = data?.span_id;
- if (typeof spanId !== 'string') {
- return null;
- }
-
- return {getShortEventId(spanId)};
- },
- },
'span.description': {
sortField: 'span.description',
renderFunc: data => {
diff --git a/static/app/utils/discover/fields.tsx b/static/app/utils/discover/fields.tsx
index 7d7968074329b2..72d1d69661bfa4 100644
--- a/static/app/utils/discover/fields.tsx
+++ b/static/app/utils/discover/fields.tsx
@@ -1639,12 +1639,14 @@ export const COMBINED_DATASET_FILTER_KEY_SECTIONS: FilterKeySection[] = [
// will take in a project platform key, and output only the relevant filter key sections.
// This way, users will not be suggested mobile fields for a backend transaction, for example.
-export const TYPED_TAG_KEY_RE = /tags\[(.*),(.*)\]/;
+export const TYPED_TAG_KEY_RE = /tags\[([^\s]*),([^\s]*)\]/;
-export function formatParsedFunction(func: ParsedFunction) {
- const args = func.arguments.map(arg => {
- const result = arg.match(TYPED_TAG_KEY_RE);
- return result?.[1] ?? arg;
- });
+export function prettifyTagKey(key: string): string {
+ const result = key.match(TYPED_TAG_KEY_RE);
+ return result?.[1] ?? key;
+}
+
+export function prettifyParsedFunction(func: ParsedFunction) {
+ const args = func.arguments.map(prettifyTagKey);
return `${func.name}(${args.join(',')})`;
}
diff --git a/static/app/views/dashboards/widgetBuilder/widgetBuilder.spec.tsx b/static/app/views/dashboards/widgetBuilder/widgetBuilder.spec.tsx
index 717653def0ee5f..d953f67320ad63 100644
--- a/static/app/views/dashboards/widgetBuilder/widgetBuilder.spec.tsx
+++ b/static/app/views/dashboards/widgetBuilder/widgetBuilder.spec.tsx
@@ -2608,7 +2608,7 @@ describe('WidgetBuilder', function () {
body: [
{
key: 'plan',
- name: 'Plan',
+ name: 'plan',
},
],
match: [
@@ -2621,12 +2621,12 @@ describe('WidgetBuilder', function () {
url: `/organizations/org-slug/spans/fields/`,
body: [
{
- key: 'lcp.size',
- name: 'Lcp.Size',
+ key: 'tags[lcp.size,number]',
+ name: 'lcp.size',
},
{
- key: 'something.else',
- name: 'Something.Else',
+ key: 'tags[something.else,number]',
+ name: 'something.else',
},
],
match: [
diff --git a/static/app/views/explore/charts/index.tsx b/static/app/views/explore/charts/index.tsx
index 6030c6a9cf92dc..2cfcd3a90a71a0 100644
--- a/static/app/views/explore/charts/index.tsx
+++ b/static/app/views/explore/charts/index.tsx
@@ -14,8 +14,8 @@ import {space} from 'sentry/styles/space';
import {dedupeArray} from 'sentry/utils/dedupeArray';
import {
aggregateOutputType,
- formatParsedFunction,
parseFunction,
+ prettifyParsedFunction,
} from 'sentry/utils/discover/fields';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import useOrganization from 'sentry/utils/useOrganization';
@@ -172,7 +172,7 @@ export function ExploreCharts({query, setError}: ExploreChartsProps) {
const formattedYAxes = dedupedYAxes.map(yaxis => {
const func = parseFunction(yaxis);
- return func ? formatParsedFunction(func) : undefined;
+ return func ? prettifyParsedFunction(func) : undefined;
});
const {chartType, label, yAxes: visualizeYAxes} = visualize;
diff --git a/static/app/views/explore/contexts/spanTagsContext.tsx b/static/app/views/explore/contexts/spanTagsContext.tsx
index 29ae6185475cf1..85459a48055983 100644
--- a/static/app/views/explore/contexts/spanTagsContext.tsx
+++ b/static/app/views/explore/contexts/spanTagsContext.tsx
@@ -9,10 +9,7 @@ import {useApiQuery} from 'sentry/utils/queryClient';
import useOrganization from 'sentry/utils/useOrganization';
import usePageFilters from 'sentry/utils/usePageFilters';
import {SpanIndexedField} from 'sentry/views/insights/types';
-import {
- useSpanFieldStaticTags,
- useSpanFieldSupportedTags,
-} from 'sentry/views/performance/utils/useSpanFieldSupportedTags';
+import {useSpanFieldCustomTags} from 'sentry/views/performance/utils/useSpanFieldSupportedTags';
type TypedSpanTags = {
number: TagCollection;
@@ -27,22 +24,9 @@ interface SpanTagsProviderProps {
}
export function SpanTagsProvider({children, dataset}: SpanTagsProviderProps) {
- const numericSpanFields: Set = useMemo(() => {
- return new Set([
- SpanIndexedField.SPAN_DURATION,
- SpanIndexedField.SPAN_SELF_TIME,
- SpanIndexedField.INP,
- SpanIndexedField.INP_SCORE,
- SpanIndexedField.INP_SCORE_WEIGHT,
- SpanIndexedField.TOTAL_SCORE,
- SpanIndexedField.CACHE_ITEM_SIZE,
- SpanIndexedField.MESSAGING_MESSAGE_BODY_SIZE,
- SpanIndexedField.MESSAGING_MESSAGE_RECEIVE_LATENCY,
- SpanIndexedField.MESSAGING_MESSAGE_RETRY_COUNT,
- ]);
- }, []);
-
- const supportedTags = useSpanFieldSupportedTags();
+ const {data: indexedTags} = useSpanFieldCustomTags({
+ enabled: dataset === DiscoverDatasets.SPANS_INDEXED,
+ });
const numberTags: TagCollection = useTypedSpanTags({
enabled: dataset === DiscoverDatasets.SPANS_EAP,
@@ -54,42 +38,92 @@ export function SpanTagsProvider({children, dataset}: SpanTagsProviderProps) {
type: 'string',
});
- const staticTags = useSpanFieldStaticTags();
-
const allNumberTags = useMemo(() => {
+ const measurements = [
+ SpanIndexedField.SPAN_DURATION,
+ SpanIndexedField.SPAN_SELF_TIME,
+ ].map(measurement => [
+ measurement,
+ {
+ key: measurement,
+ name: measurement,
+ kind: FieldKind.MEASUREMENT,
+ },
+ ]);
+
if (dataset === DiscoverDatasets.SPANS_INDEXED) {
- return {};
+ return {
+ ...Object.fromEntries(measurements),
+ };
}
return {
...numberTags,
- ...Object.fromEntries(
- Object.entries(staticTags)
- .filter(([key, _]) => numericSpanFields.has(key))
- .map(([key, tag]) => [key, {...tag, kind: FieldKind.MEASUREMENT}])
- ),
+ ...Object.fromEntries(measurements),
};
- }, [dataset, numberTags, numericSpanFields, staticTags]);
+ }, [dataset, numberTags]);
const allStringTags = useMemo(() => {
+ const tags = [
+ // NOTE: intentionally choose to not expose transaction id
+ // as we're moving toward span ids
+
+ 'id', // SpanIndexedField.SPAN_OP is actually `span_id`
+ SpanIndexedField.BROWSER_NAME,
+ SpanIndexedField.ENVIRONMENT,
+ SpanIndexedField.ORIGIN_TRANSACTION,
+ SpanIndexedField.PROJECT,
+ SpanIndexedField.RAW_DOMAIN,
+ SpanIndexedField.RELEASE,
+ SpanIndexedField.SDK_NAME,
+ SpanIndexedField.SDK_VERSION,
+ SpanIndexedField.SPAN_ACTION,
+ SpanIndexedField.SPAN_CATEGORY,
+ SpanIndexedField.SPAN_DESCRIPTION,
+ SpanIndexedField.SPAN_DOMAIN,
+ SpanIndexedField.SPAN_GROUP,
+ SpanIndexedField.SPAN_MODULE,
+ SpanIndexedField.SPAN_OP,
+ SpanIndexedField.SPAN_STATUS,
+ SpanIndexedField.TIMESTAMP,
+ SpanIndexedField.TRACE,
+ SpanIndexedField.TRANSACTION,
+ SpanIndexedField.TRANSACTION_METHOD,
+ SpanIndexedField.TRANSACTION_OP,
+ SpanIndexedField.USER,
+ SpanIndexedField.USER_EMAIL,
+ SpanIndexedField.USER_GEO_SUBREGION,
+ SpanIndexedField.USER_ID,
+ SpanIndexedField.USER_IP,
+ SpanIndexedField.USER_USERNAME,
+ ].map(tag => [
+ tag,
+ {
+ key: tag,
+ name: tag,
+ kind: FieldKind.TAG,
+ },
+ ]);
+
if (dataset === DiscoverDatasets.SPANS_INDEXED) {
- return supportedTags.data;
+ return {
+ ...indexedTags,
+ ...Object.fromEntries(tags),
+ };
}
return {
...stringTags,
- ...Object.fromEntries(
- Object.entries(staticTags)
- .filter(([key, _]) => !numericSpanFields.has(key))
- .map(([key, tag]) => [key, {...tag, kind: FieldKind.TAG}])
- ),
+ ...Object.fromEntries(tags),
};
- }, [dataset, supportedTags, stringTags, staticTags, numericSpanFields]);
+ }, [dataset, indexedTags, stringTags]);
- const tags = {
- number: allNumberTags,
- string: allStringTags,
- };
+ const tags = useMemo(() => {
+ return {
+ number: allNumberTags,
+ string: allStringTags,
+ };
+ }, [allNumberTags, allStringTags]);
return {children};
}
@@ -131,6 +165,7 @@ function useTypedSpanTags({
environment: selection.environments,
...normalizeDateTimeParams(selection.datetime),
dataset: 'spans',
+ process: 1,
type,
},
};
@@ -149,20 +184,22 @@ function useTypedSpanTags({
// For now, skip all the sentry. prefixed tags as they
// should be covered by the static tags that will be
// merged with these results.
- if (tag.key.startsWith('sentry.')) {
+ if (tag.key.startsWith('sentry.') || tag.key.startsWith('tags[sentry.')) {
continue;
}
// EAP spans contain tags with illegal characters
- if (!/^[a-zA-Z0-9_.:-]+$/.test(tag.key)) {
+ // SnQL forbids `-` but is allowed in RPC. So add it back later
+ if (
+ !/^[a-zA-Z0-9_.:]+$/.test(tag.key) &&
+ !/^tags\[[a-zA-Z0-9_.:]+,number\]$/.test(tag.key)
+ ) {
continue;
}
- const key = type === 'number' ? `tags[${tag.key},number]` : tag.key;
-
- allTags[key] = {
- key,
- name: tag.key,
+ allTags[tag.key] = {
+ key: tag.key,
+ name: tag.name,
kind: type === 'number' ? FieldKind.MEASUREMENT : FieldKind.TAG,
};
}
diff --git a/static/app/views/explore/hooks/useResultsMode.spec.tsx b/static/app/views/explore/hooks/useResultsMode.spec.tsx
index ca060618933591..92b8a7594c98ec 100644
--- a/static/app/views/explore/hooks/useResultsMode.spec.tsx
+++ b/static/app/views/explore/hooks/useResultsMode.spec.tsx
@@ -39,7 +39,7 @@ describe('useResultMode', function () {
expect(resultMode).toEqual('samples'); // default
expect(sampleFields).toEqual([
- 'span_id',
+ 'id',
'project',
'span.op',
'span.description',
@@ -56,7 +56,7 @@ describe('useResultMode', function () {
expect(resultMode).toEqual('samples');
expect(sampleFields).toEqual([
- 'span_id',
+ 'id',
'project',
'span.op',
'span.description',
diff --git a/static/app/views/explore/hooks/useSampleFields.spec.tsx b/static/app/views/explore/hooks/useSampleFields.spec.tsx
index 2e5f464458e05c..59ccc2aff095b4 100644
--- a/static/app/views/explore/hooks/useSampleFields.spec.tsx
+++ b/static/app/views/explore/hooks/useSampleFields.spec.tsx
@@ -14,7 +14,7 @@ describe('useSampleFields', function () {
render(, {disableRouterMocks: true});
expect(sampleFields).toEqual([
- 'span_id',
+ 'id',
'project',
'span.op',
'span.description',
@@ -27,7 +27,7 @@ describe('useSampleFields', function () {
act(() => setSampleFields([]));
expect(sampleFields).toEqual([
- 'span_id',
+ 'id',
'project',
'span.op',
'span.description',
diff --git a/static/app/views/explore/hooks/useSampleFields.tsx b/static/app/views/explore/hooks/useSampleFields.tsx
index 762425d8773c44..701e6b80db9458 100644
--- a/static/app/views/explore/hooks/useSampleFields.tsx
+++ b/static/app/views/explore/hooks/useSampleFields.tsx
@@ -31,14 +31,7 @@ function useSampleFieldsImpl({
return fields;
}
- return [
- 'span_id',
- 'project',
- 'span.op',
- 'span.description',
- 'span.duration',
- 'timestamp',
- ];
+ return ['id', 'project', 'span.op', 'span.description', 'span.duration', 'timestamp'];
}, [location.query.field]);
const setSampleFields = useCallback(
diff --git a/static/app/views/explore/tables/aggregatesTable.tsx b/static/app/views/explore/tables/aggregatesTable.tsx
index be597c601a42c0..cc1e9ffe411508 100644
--- a/static/app/views/explore/tables/aggregatesTable.tsx
+++ b/static/app/views/explore/tables/aggregatesTable.tsx
@@ -15,9 +15,9 @@ import EventView from 'sentry/utils/discover/eventView';
import type {Sort} from 'sentry/utils/discover/fields';
import {
fieldAlignment,
- formatParsedFunction,
getAggregateAlias,
parseFunction,
+ prettifyParsedFunction,
} from 'sentry/utils/discover/fields';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import useOrganization from 'sentry/utils/useOrganization';
@@ -147,7 +147,7 @@ export function AggregatesTable({setError}: AggregatesTableProps) {
const func = parseFunction(field);
if (func) {
- label = formatParsedFunction(func);
+ label = prettifyParsedFunction(func);
}
const direction = sorts.find(s => s.field === field)?.kind;
diff --git a/static/app/views/explore/tables/fieldRenderer.tsx b/static/app/views/explore/tables/fieldRenderer.tsx
index 49419bad455e3c..5ca833eb4a7520 100644
--- a/static/app/views/explore/tables/fieldRenderer.tsx
+++ b/static/app/views/explore/tables/fieldRenderer.tsx
@@ -6,7 +6,9 @@ import type {TableDataRow} from 'sentry/utils/discover/discoverQuery';
import type {EventData, MetaType} from 'sentry/utils/discover/eventView';
import EventView from 'sentry/utils/discover/eventView';
import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
+import {Container} from 'sentry/utils/discover/styles';
import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
+import {getShortEventId} from 'sentry/utils/events';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
@@ -33,7 +35,7 @@ export function FieldRenderer({data, meta, unit, column}: FieldProps) {
const query = new MutableSearch(userQuery);
const field = column.name;
- const renderer = getFieldRenderer(field, meta, false);
+ const renderer = getExploreFieldRenderer(field, meta);
let rendered = renderer(data, {
location,
@@ -91,6 +93,28 @@ export function FieldRenderer({data, meta, unit, column}: FieldProps) {
);
}
+function getExploreFieldRenderer(
+ field: string,
+ meta: MetaType
+): ReturnType {
+ if (field === 'id' || field === 'span_id') {
+ return eventIdRenderFunc(field);
+ }
+ return getFieldRenderer(field, meta, false);
+}
+
+function eventIdRenderFunc(field: string) {
+ function renderer(data: EventData) {
+ const spanId: string | unknown = data?.[field];
+ if (typeof spanId !== 'string') {
+ return null;
+ }
+
+ return {getShortEventId(spanId)};
+ }
+ return renderer;
+}
+
const StyledTimeSince = styled(TimeSince)`
width: fit-content;
`;
diff --git a/static/app/views/explore/tables/spansTable.tsx b/static/app/views/explore/tables/spansTable.tsx
index a43b5247573942..cbf4274f3902e1 100644
--- a/static/app/views/explore/tables/spansTable.tsx
+++ b/static/app/views/explore/tables/spansTable.tsx
@@ -56,7 +56,7 @@ export function SpansTable({setError}: SpansTableProps) {
'project',
'trace',
'transaction.span_id',
- 'span_id',
+ 'id',
'timestamp',
];
diff --git a/static/app/views/explore/toolbar/index.spec.tsx b/static/app/views/explore/toolbar/index.spec.tsx
index cc06217d93f02f..2239519d172c7b 100644
--- a/static/app/views/explore/toolbar/index.spec.tsx
+++ b/static/app/views/explore/toolbar/index.spec.tsx
@@ -53,7 +53,7 @@ describe('ExploreToolbar', function () {
expect(resultMode).toEqual('samples');
expect(sampleFields).toEqual([
- 'span_id',
+ 'id',
'project',
'span.op',
'span.description',
@@ -80,7 +80,7 @@ describe('ExploreToolbar', function () {
expect(resultMode).toEqual('samples');
expect(sampleFields).toEqual([
- 'span_id',
+ 'id',
'project',
'span.op',
'span.description',
@@ -217,7 +217,7 @@ describe('ExploreToolbar', function () {
// check the default field options
const fields = [
- 'span_id',
+ 'id',
'project',
'span.op',
'span.description',
diff --git a/static/app/views/explore/toolbar/toolbarSortBy.tsx b/static/app/views/explore/toolbar/toolbarSortBy.tsx
index e1906a45507e9b..af7a32ce161ad0 100644
--- a/static/app/views/explore/toolbar/toolbarSortBy.tsx
+++ b/static/app/views/explore/toolbar/toolbarSortBy.tsx
@@ -6,7 +6,7 @@ import {CompactSelect} from 'sentry/components/compactSelect';
import {Tooltip} from 'sentry/components/tooltip';
import {t} from 'sentry/locale';
import type {Sort} from 'sentry/utils/discover/fields';
-import {formatParsedFunction, parseFunction} from 'sentry/utils/discover/fields';
+import {parseFunction, prettifyParsedFunction} from 'sentry/utils/discover/fields';
import {TypeBadge} from 'sentry/views/explore/components/typeBadge';
import {useSpanTags} from 'sentry/views/explore/contexts/spanTagsContext';
import {useResultMode} from 'sentry/views/explore/hooks/useResultsMode';
@@ -45,7 +45,7 @@ export function ToolbarSortBy({fields, setSorts, sorts}: ToolbarSortByProps) {
const func = parseFunction(field);
if (func) {
- const formatted = formatParsedFunction(func);
+ const formatted = prettifyParsedFunction(func);
return {
label: formatted,
value: field,
diff --git a/static/app/views/insights/constants.tsx b/static/app/views/insights/constants.tsx
index 3d4c5cf5b44f36..47c46bcf6cca9e 100644
--- a/static/app/views/insights/constants.tsx
+++ b/static/app/views/insights/constants.tsx
@@ -87,6 +87,7 @@ const USER_CONTEXT_FILTERS: FilterKeySection = {
children: [
SpanIndexedField.USER,
SpanIndexedField.USER_ID,
+ SpanIndexedField.USER_IP,
SpanIndexedField.USER_EMAIL,
SpanIndexedField.USER_USERNAME,
SpanIndexedField.USER_GEO_SUBREGION,
diff --git a/static/app/views/insights/types.tsx b/static/app/views/insights/types.tsx
index 81f36d2321e158..af4efcb98be3eb 100644
--- a/static/app/views/insights/types.tsx
+++ b/static/app/views/insights/types.tsx
@@ -229,6 +229,7 @@ export enum SpanIndexedField {
SPAN_AI_PIPELINE_GROUP = 'span.ai.pipeline.group',
SPAN_AI_PIPELINE_GROUP_TAG = 'ai_pipeline_group',
SDK_NAME = 'sdk.name',
+ SDK_VERSION = 'sdk.version',
TRACE = 'trace',
TRANSACTION_ID = 'transaction.id',
TRANSACTION_METHOD = 'transaction.method',
@@ -246,6 +247,7 @@ export enum SpanIndexedField {
BROWSER_NAME = 'browser.name',
USER = 'user',
USER_ID = 'user.id',
+ USER_IP = 'user.ip',
USER_EMAIL = 'user.email',
USER_USERNAME = 'user.username',
INP = 'measurements.inp',
@@ -268,6 +270,7 @@ export type SpanIndexedResponse = {
[SpanIndexedField.ENVIRONMENT]: string;
[SpanIndexedField.RELEASE]: string;
[SpanIndexedField.SDK_NAME]: string;
+ [SpanIndexedField.SDK_VERSION]: string;
[SpanIndexedField.SPAN_CATEGORY]: string;
[SpanIndexedField.SPAN_DURATION]: number;
[SpanIndexedField.SPAN_SELF_TIME]: number;
diff --git a/static/app/views/performance/utils/useSpanFieldSupportedTags.tsx b/static/app/views/performance/utils/useSpanFieldSupportedTags.tsx
index 7023051ad694b8..26ece7815db1e4 100644
--- a/static/app/views/performance/utils/useSpanFieldSupportedTags.tsx
+++ b/static/app/views/performance/utils/useSpanFieldSupportedTags.tsx
@@ -66,8 +66,11 @@ export function useSpanMetricsFieldSupportedTags(options?: {excludedTags?: strin
);
}
-export function useSpanFieldCustomTags(options?: {projects?: PageFilters['projects']}) {
- const {projects} = options || {};
+export function useSpanFieldCustomTags(options?: {
+ enabled?: boolean;
+ projects?: PageFilters['projects'];
+}) {
+ const {enabled, projects} = options || {};
const {selection} = usePageFilters();
const organization = useOrganization();
@@ -80,6 +83,7 @@ export function useSpanFieldCustomTags(options?: {projects?: PageFilters['projec
{
staleTime: 0,
retry: false,
+ enabled,
}
);