@parca/profile 0.16.466 → 0.16.468
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/MatchersInput/SuggestionsList.d.ts +2 -1
- package/dist/MatchersInput/SuggestionsList.d.ts.map +1 -1
- package/dist/MatchersInput/SuggestionsList.js +6 -2
- package/dist/MatchersInput/index.d.ts +4 -2
- package/dist/MatchersInput/index.d.ts.map +1 -1
- package/dist/MatchersInput/index.js +51 -22
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.js +2 -2
- package/dist/ProfileSelector/index.d.ts +7 -1
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +3 -2
- package/dist/SimpleMatchers/index.d.ts +4 -2
- package/dist/SimpleMatchers/index.d.ts.map +1 -1
- package/dist/SimpleMatchers/index.js +46 -32
- package/dist/contexts/MatchersInputLabelsContext.d.ts +25 -0
- package/dist/contexts/MatchersInputLabelsContext.d.ts.map +1 -0
- package/dist/contexts/MatchersInputLabelsContext.js +75 -0
- package/dist/contexts/SimpleMatchersLabelContext.d.ts +17 -0
- package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +1 -0
- package/dist/contexts/SimpleMatchersLabelContext.js +45 -0
- package/dist/contexts/UtilizationLabelsContext.d.ts +14 -0
- package/dist/contexts/UtilizationLabelsContext.d.ts.map +1 -0
- package/dist/contexts/UtilizationLabelsContext.js +25 -0
- package/package.json +2 -2
- package/src/MatchersInput/SuggestionsList.tsx +9 -2
- package/src/MatchersInput/index.tsx +71 -27
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.tsx +2 -2
- package/src/ProfileSelector/index.tsx +65 -54
- package/src/SimpleMatchers/index.tsx +65 -33
- package/src/contexts/MatchersInputLabelsContext.tsx +130 -0
- package/src/contexts/SimpleMatchersLabelContext.tsx +78 -0
- package/src/contexts/UtilizationLabelsContext.tsx +44 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
15
|
+
import { useLabelNames } from '../MatchersInput';
|
|
16
|
+
import { transformLabelsForSelect } from '../SimpleMatchers';
|
|
17
|
+
import { useUtilizationLabels } from './UtilizationLabelsContext';
|
|
18
|
+
const LabelContext = createContext(null);
|
|
19
|
+
// With there being the possibility of having utilization labels, we need to be able to determine whether the labels to be used are utilization labels or profiling data labels.
|
|
20
|
+
// This context is used to determine this.
|
|
21
|
+
export function LabelProvider({ children, queryClient, profileType, labelNameFromMatchers, }) {
|
|
22
|
+
const utilizationLabels = useUtilizationLabels();
|
|
23
|
+
const { loading, result } = useLabelNames(queryClient, profileType);
|
|
24
|
+
const value = useMemo(() => {
|
|
25
|
+
const baseLabels = result.error != null ? [] : result.response?.labelNames.filter(e => e !== '__name__') ?? [];
|
|
26
|
+
const labelsToUse = utilizationLabels?.utilizationLabelNames?.length !== undefined
|
|
27
|
+
? utilizationLabels?.utilizationLabelNames ?? []
|
|
28
|
+
: baseLabels;
|
|
29
|
+
const uniqueLabels = Array.from(new Set([...labelsToUse, ...labelNameFromMatchers]));
|
|
30
|
+
const shouldTrimPrefix = Boolean(utilizationLabels?.utilizationLabelNames);
|
|
31
|
+
return {
|
|
32
|
+
labelNameOptions: transformLabelsForSelect(uniqueLabels, shouldTrimPrefix),
|
|
33
|
+
isLoading: loading,
|
|
34
|
+
error: result.error ?? null,
|
|
35
|
+
};
|
|
36
|
+
}, [result, loading, utilizationLabels, labelNameFromMatchers]);
|
|
37
|
+
return _jsx(LabelContext.Provider, { value: value, children: children });
|
|
38
|
+
}
|
|
39
|
+
export function useLabels() {
|
|
40
|
+
const context = useContext(LabelContext);
|
|
41
|
+
if (context === null) {
|
|
42
|
+
throw new Error('useLabels must be used within a LabelProvider');
|
|
43
|
+
}
|
|
44
|
+
return context;
|
|
45
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export interface UtilizationLabels {
|
|
3
|
+
utilizationLabelNames?: string[];
|
|
4
|
+
utilizationFetchLabelValues?: (key: string) => Promise<string[]>;
|
|
5
|
+
utilizationLabelValues?: string[];
|
|
6
|
+
}
|
|
7
|
+
interface UtilizationLabelsProviderProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
value: UtilizationLabels | undefined;
|
|
10
|
+
}
|
|
11
|
+
export declare function UtilizationLabelsProvider({ children, value, }: UtilizationLabelsProviderProps): JSX.Element;
|
|
12
|
+
export declare function useUtilizationLabels(): UtilizationLabels | undefined;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=UtilizationLabelsContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UtilizationLabelsContext.d.ts","sourceRoot":"","sources":["../../src/contexts/UtilizationLabelsContext.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,SAAS,EAA4B,MAAM,OAAO,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,2BAA2B,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjE,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;CACnC;AAED,UAAU,8BAA8B;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACtC;AAOD,wBAAgB,yBAAyB,CAAC,EACxC,QAAQ,EACR,KAAK,GACN,EAAE,8BAA8B,GAAG,GAAG,CAAC,OAAO,CAI9C;AAED,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,SAAS,CAGpE"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import { createContext, useContext } from 'react';
|
|
15
|
+
// The UtilizationLabelsContext is used to store the utilization label names and values. It also
|
|
16
|
+
// contains the function utilizationFetchLabelValues to fetch the utilization label values.
|
|
17
|
+
// This context was created so as to avoid props drilling.
|
|
18
|
+
const UtilizationLabelsContext = createContext(undefined);
|
|
19
|
+
export function UtilizationLabelsProvider({ children, value, }) {
|
|
20
|
+
return (_jsx(UtilizationLabelsContext.Provider, { value: value, children: children }));
|
|
21
|
+
}
|
|
22
|
+
export function useUtilizationLabels() {
|
|
23
|
+
const context = useContext(UtilizationLabelsContext);
|
|
24
|
+
return context;
|
|
25
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.468",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@headlessui/react": "^1.7.19",
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
"access": "public",
|
|
76
76
|
"registry": "https://registry.npmjs.org/"
|
|
77
77
|
},
|
|
78
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "1c8eeeff45c162e0c93a59b2cab77f21615e5397"
|
|
79
79
|
}
|
|
@@ -52,6 +52,7 @@ interface Props {
|
|
|
52
52
|
focusedInput: boolean;
|
|
53
53
|
isLabelNamesLoading: boolean;
|
|
54
54
|
isLabelValuesLoading: boolean;
|
|
55
|
+
shouldTrimPrefix: boolean;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
const LoadingSpinner = (): JSX.Element => {
|
|
@@ -60,6 +61,11 @@ const LoadingSpinner = (): JSX.Element => {
|
|
|
60
61
|
return <div className="pt-2 pb-4">{Spinner}</div>;
|
|
61
62
|
};
|
|
62
63
|
|
|
64
|
+
const transformLabelsForSuggestions = (labelNames: string, shouldTrimPrefix = false): string => {
|
|
65
|
+
const trimmedLabel = shouldTrimPrefix ? labelNames.split('.').pop() ?? labelNames : labelNames;
|
|
66
|
+
return trimmedLabel;
|
|
67
|
+
};
|
|
68
|
+
|
|
63
69
|
const SuggestionsList = ({
|
|
64
70
|
suggestions,
|
|
65
71
|
applySuggestion,
|
|
@@ -68,6 +74,7 @@ const SuggestionsList = ({
|
|
|
68
74
|
focusedInput,
|
|
69
75
|
isLabelNamesLoading,
|
|
70
76
|
isLabelValuesLoading,
|
|
77
|
+
shouldTrimPrefix = false,
|
|
71
78
|
}: Props): JSX.Element => {
|
|
72
79
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
73
80
|
const {styles, attributes} = usePopper(inputRef, popperElement, {
|
|
@@ -234,8 +241,8 @@ const SuggestionsList = ({
|
|
|
234
241
|
onHighlight={() => setHighlightedSuggestionIndex(i)}
|
|
235
242
|
onApplySuggestion={() => applySuggestionWithIndex(i)}
|
|
236
243
|
onResetHighlight={() => resetHighlight()}
|
|
237
|
-
value={l.value}
|
|
238
|
-
key={l.value}
|
|
244
|
+
value={transformLabelsForSuggestions(l.value, shouldTrimPrefix)}
|
|
245
|
+
key={transformLabelsForSuggestions(l.value, shouldTrimPrefix)}
|
|
239
246
|
/>
|
|
240
247
|
))}
|
|
241
248
|
</>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import React, {useMemo, useRef, useState} from 'react';
|
|
15
15
|
|
|
16
|
+
import {useQuery} from '@tanstack/react-query';
|
|
16
17
|
import cx from 'classnames';
|
|
17
18
|
import TextareaAutosize from 'react-textarea-autosize';
|
|
18
19
|
|
|
@@ -21,6 +22,8 @@ import {useGrpcMetadata} from '@parca/components';
|
|
|
21
22
|
import {Query} from '@parca/parser';
|
|
22
23
|
import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
|
|
23
24
|
|
|
25
|
+
import {UtilizationLabels} from '../ProfileSelector';
|
|
26
|
+
import {LabelsProvider, useLabels} from '../contexts/MatchersInputLabelsContext';
|
|
24
27
|
import useGrpcQuery from '../useGrpcQuery';
|
|
25
28
|
import SuggestionsList, {Suggestion, Suggestions} from './SuggestionsList';
|
|
26
29
|
|
|
@@ -111,33 +114,39 @@ export const useLabelValues = (
|
|
|
111
114
|
return {result: {response: data ?? [], error: error as Error}, loading: isLoading};
|
|
112
115
|
};
|
|
113
116
|
|
|
117
|
+
export const useFetchUtilizationLabelValues = (
|
|
118
|
+
labelName: string,
|
|
119
|
+
utilizationLabels?: UtilizationLabels
|
|
120
|
+
): string[] => {
|
|
121
|
+
const {data} = useQuery({
|
|
122
|
+
queryKey: ['utilizationLabelValues', labelName],
|
|
123
|
+
queryFn: async () => {
|
|
124
|
+
return await utilizationLabels?.utilizationFetchLabelValues?.(labelName);
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return data ?? [];
|
|
129
|
+
};
|
|
130
|
+
|
|
114
131
|
const MatchersInput = ({
|
|
115
|
-
queryClient,
|
|
116
132
|
setMatchersString,
|
|
117
133
|
runQuery,
|
|
118
134
|
currentQuery,
|
|
119
|
-
profileType,
|
|
120
135
|
}: MatchersInputProps): JSX.Element => {
|
|
121
136
|
const inputRef = useRef<HTMLTextAreaElement | null>(null);
|
|
122
137
|
const [focusedInput, setFocusedInput] = useState(false);
|
|
123
138
|
const [lastCompleted, setLastCompleted] = useState<Suggestion>(new Suggestion('', '', ''));
|
|
124
|
-
const [currentLabelName, setCurrentLabelName] = useState<string | null>(null);
|
|
125
|
-
|
|
126
|
-
const {loading: labelNamesLoading, result} = useLabelNames(queryClient, profileType);
|
|
127
|
-
const {response: labelNamesResponse, error: labelNamesError} = result;
|
|
128
139
|
|
|
129
140
|
const {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
: [];
|
|
140
|
-
}, [labelNamesError, labelNamesResponse]);
|
|
141
|
+
labelNames,
|
|
142
|
+
labelValues,
|
|
143
|
+
labelNameMappings,
|
|
144
|
+
isLabelNamesLoading,
|
|
145
|
+
isLabelValuesLoading,
|
|
146
|
+
currentLabelName,
|
|
147
|
+
setCurrentLabelName,
|
|
148
|
+
shouldHandlePrefixes,
|
|
149
|
+
} = useLabels();
|
|
141
150
|
|
|
142
151
|
const value = currentQuery.matchersString();
|
|
143
152
|
|
|
@@ -168,18 +177,36 @@ const MatchersInput = ({
|
|
|
168
177
|
return label.toLowerCase().slice(0, inputLength) === inputValue;
|
|
169
178
|
});
|
|
170
179
|
|
|
171
|
-
matches.forEach(m =>
|
|
172
|
-
|
|
180
|
+
matches.forEach(m => {
|
|
181
|
+
const suggestion = {
|
|
173
182
|
type: s.type,
|
|
174
183
|
typeahead: s.typeahead,
|
|
175
184
|
value: m,
|
|
176
|
-
}
|
|
177
|
-
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
if (shouldHandlePrefixes) {
|
|
188
|
+
const mapping = labelNameMappings.find(l => l.displayName === m);
|
|
189
|
+
if (mapping != null) {
|
|
190
|
+
(suggestion as any).fullName = mapping.fullName;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
suggestionSections.labelNames.push(suggestion);
|
|
195
|
+
});
|
|
178
196
|
}
|
|
179
197
|
|
|
180
198
|
if (s.type === 'labelValue') {
|
|
181
|
-
|
|
182
|
-
|
|
199
|
+
let labelNameForQuery = s.labelName;
|
|
200
|
+
|
|
201
|
+
if (shouldHandlePrefixes) {
|
|
202
|
+
const mapping = labelNameMappings.find(l => l.displayName === s.labelName);
|
|
203
|
+
if (mapping != null) {
|
|
204
|
+
labelNameForQuery = mapping.fullName;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (currentLabelName === null || labelNameForQuery !== currentLabelName) {
|
|
209
|
+
setCurrentLabelName(labelNameForQuery);
|
|
183
210
|
return;
|
|
184
211
|
}
|
|
185
212
|
|
|
@@ -197,7 +224,17 @@ const MatchersInput = ({
|
|
|
197
224
|
}
|
|
198
225
|
});
|
|
199
226
|
return suggestionSections;
|
|
200
|
-
}, [
|
|
227
|
+
}, [
|
|
228
|
+
currentQuery,
|
|
229
|
+
lastCompleted,
|
|
230
|
+
labelNames,
|
|
231
|
+
labelValues,
|
|
232
|
+
currentLabelName,
|
|
233
|
+
value,
|
|
234
|
+
shouldHandlePrefixes,
|
|
235
|
+
labelNameMappings,
|
|
236
|
+
setCurrentLabelName,
|
|
237
|
+
]);
|
|
201
238
|
|
|
202
239
|
const resetLastCompleted = (): void => setLastCompleted(new Suggestion('', '', ''));
|
|
203
240
|
|
|
@@ -269,16 +306,23 @@ const MatchersInput = ({
|
|
|
269
306
|
id="matchers-input"
|
|
270
307
|
/>
|
|
271
308
|
<SuggestionsList
|
|
272
|
-
isLabelNamesLoading={
|
|
309
|
+
isLabelNamesLoading={isLabelNamesLoading}
|
|
273
310
|
suggestions={suggestionSections}
|
|
274
311
|
applySuggestion={applySuggestion}
|
|
275
312
|
inputRef={inputRef.current}
|
|
276
313
|
runQuery={runQuery}
|
|
277
314
|
focusedInput={focusedInput}
|
|
278
|
-
isLabelValuesLoading={
|
|
315
|
+
isLabelValuesLoading={isLabelValuesLoading && lastCompleted.type === 'literal'}
|
|
316
|
+
shouldTrimPrefix={shouldHandlePrefixes}
|
|
279
317
|
/>
|
|
280
318
|
</div>
|
|
281
319
|
);
|
|
282
320
|
};
|
|
283
321
|
|
|
284
|
-
export default
|
|
322
|
+
export default function MatchersInputWithProvider(props: MatchersInputProps): JSX.Element {
|
|
323
|
+
return (
|
|
324
|
+
<LabelsProvider queryClient={props.queryClient} profileType={props.profileType}>
|
|
325
|
+
<MatchersInput {...props} />
|
|
326
|
+
</LabelsProvider>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
@@ -87,9 +87,9 @@ export const IcicleChartRootNode = React.memo(function IcicleChartRootNodeNonMem
|
|
|
87
87
|
groupByMetadata?.get(row) as StructRow<Record<string, Binary>>
|
|
88
88
|
).toJSON();
|
|
89
89
|
|
|
90
|
-
const tsStr = arrowToString(groupByFields.
|
|
90
|
+
const tsStr = arrowToString(groupByFields.time_nanos) as string;
|
|
91
91
|
|
|
92
|
-
const tsNanos = BigInt(parseInt(tsStr, 10))
|
|
92
|
+
const tsNanos = BigInt(parseInt(tsStr, 10));
|
|
93
93
|
const durationStr = arrowToString(groupByFields.duration) as string;
|
|
94
94
|
const duration = parseInt(durationStr, 10);
|
|
95
95
|
|
|
@@ -30,6 +30,7 @@ import {type NavigateFunction} from '@parca/utilities';
|
|
|
30
30
|
import {ProfileSelection} from '..';
|
|
31
31
|
import {useLabelNames} from '../MatchersInput/index';
|
|
32
32
|
import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
|
|
33
|
+
import {UtilizationLabelsProvider} from '../contexts/UtilizationLabelsContext';
|
|
33
34
|
import {useDefaultSumBy, useSumBySelection} from '../useSumBy';
|
|
34
35
|
import {MetricsGraphSection} from './MetricsGraphSection';
|
|
35
36
|
import {QueryControls} from './QueryControls';
|
|
@@ -64,6 +65,12 @@ export interface UtilizationMetrics {
|
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
export interface UtilizationLabels {
|
|
69
|
+
utilizationLabelNames?: string[];
|
|
70
|
+
utilizationFetchLabelValues?: (key: string) => Promise<string[]>;
|
|
71
|
+
utilizationLabelValues?: string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
67
74
|
interface ProfileSelectorProps extends ProfileSelectorFeatures {
|
|
68
75
|
queryClient: QueryServiceClient;
|
|
69
76
|
querySelection: QuerySelection;
|
|
@@ -78,6 +85,7 @@ interface ProfileSelectorProps extends ProfileSelectorFeatures {
|
|
|
78
85
|
suffix?: string;
|
|
79
86
|
utilizationMetrics?: UtilizationMetrics[];
|
|
80
87
|
utilizationMetricsLoading?: boolean;
|
|
88
|
+
utilizationLabels?: UtilizationLabels;
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
export interface IProfileTypesResult {
|
|
@@ -123,6 +131,7 @@ const ProfileSelector = ({
|
|
|
123
131
|
setDisplayHideMetricsGraphButton,
|
|
124
132
|
utilizationMetrics,
|
|
125
133
|
utilizationMetricsLoading,
|
|
134
|
+
utilizationLabels,
|
|
126
135
|
}: ProfileSelectorProps): JSX.Element => {
|
|
127
136
|
const {
|
|
128
137
|
loading: profileTypesLoading,
|
|
@@ -268,62 +277,64 @@ const ProfileSelector = ({
|
|
|
268
277
|
const sumByRef = useRef(null);
|
|
269
278
|
|
|
270
279
|
return (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
<
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
280
|
+
<UtilizationLabelsProvider value={utilizationLabels}>
|
|
281
|
+
<>
|
|
282
|
+
<div className="mb-2 flex">
|
|
283
|
+
<QueryControls
|
|
284
|
+
showProfileTypeSelector={showProfileTypeSelector}
|
|
285
|
+
showSumBySelector={showSumBySelector}
|
|
286
|
+
disableExplorativeQuerying={disableExplorativeQuerying}
|
|
287
|
+
profileTypesData={profileTypesData}
|
|
288
|
+
profileTypesLoading={profileTypesLoading}
|
|
289
|
+
selectedProfileName={selectedProfileName}
|
|
290
|
+
setProfileName={setProfileName}
|
|
291
|
+
setMatchersString={setMatchersString}
|
|
292
|
+
setQueryExpression={setQueryExpression}
|
|
293
|
+
query={query}
|
|
294
|
+
queryBrowserRef={queryBrowserRef}
|
|
295
|
+
timeRangeSelection={timeRangeSelection}
|
|
296
|
+
setTimeRangeSelection={setTimeRangeSelection}
|
|
297
|
+
searchDisabled={searchDisabled}
|
|
298
|
+
queryBrowserMode={queryBrowserMode as string}
|
|
299
|
+
setQueryBrowserMode={setQueryBrowserMode}
|
|
300
|
+
advancedModeForQueryBrowser={advancedModeForQueryBrowser}
|
|
301
|
+
setAdvancedModeForQueryBrowser={setAdvancedModeForQueryBrowser}
|
|
302
|
+
queryClient={queryClient}
|
|
303
|
+
sumByRef={sumByRef}
|
|
304
|
+
labels={labels}
|
|
305
|
+
sumBySelection={sumBySelection ?? []}
|
|
306
|
+
setUserSumBySelection={setUserSumBySelection}
|
|
307
|
+
profileType={profileType}
|
|
308
|
+
profileTypesError={error}
|
|
309
|
+
/>
|
|
310
|
+
{comparing && (
|
|
311
|
+
<div>
|
|
312
|
+
<IconButton onClick={() => closeProfile()} icon={<CloseIcon />} />
|
|
313
|
+
</div>
|
|
314
|
+
)}
|
|
315
|
+
</div>
|
|
316
|
+
<MetricsGraphSection
|
|
317
|
+
showMetricsGraph={showMetricsGraph}
|
|
318
|
+
setDisplayHideMetricsGraphButton={setDisplayHideMetricsGraphButton}
|
|
319
|
+
heightStyle={heightStyle}
|
|
320
|
+
querySelection={querySelection}
|
|
321
|
+
profileSelection={profileSelection}
|
|
322
|
+
comparing={comparing}
|
|
323
|
+
sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
|
|
324
|
+
defaultSumByLoading={defaultSumByLoading}
|
|
292
325
|
queryClient={queryClient}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
326
|
+
queryExpressionString={queryExpressionString}
|
|
327
|
+
setTimeRangeSelection={setTimeRangeSelection}
|
|
328
|
+
selectQuery={selectQuery}
|
|
329
|
+
selectProfile={selectProfile}
|
|
330
|
+
query={query}
|
|
331
|
+
setQueryExpression={setQueryExpression}
|
|
332
|
+
setNewQueryExpression={setNewQueryExpression}
|
|
333
|
+
utilizationMetrics={utilizationMetrics}
|
|
334
|
+
utilizationMetricsLoading={utilizationMetricsLoading}
|
|
299
335
|
/>
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
<IconButton onClick={() => closeProfile()} icon={<CloseIcon />} />
|
|
303
|
-
</div>
|
|
304
|
-
)}
|
|
305
|
-
</div>
|
|
306
|
-
<MetricsGraphSection
|
|
307
|
-
showMetricsGraph={showMetricsGraph}
|
|
308
|
-
setDisplayHideMetricsGraphButton={setDisplayHideMetricsGraphButton}
|
|
309
|
-
heightStyle={heightStyle}
|
|
310
|
-
querySelection={querySelection}
|
|
311
|
-
profileSelection={profileSelection}
|
|
312
|
-
comparing={comparing}
|
|
313
|
-
sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
|
|
314
|
-
defaultSumByLoading={defaultSumByLoading}
|
|
315
|
-
queryClient={queryClient}
|
|
316
|
-
queryExpressionString={queryExpressionString}
|
|
317
|
-
setTimeRangeSelection={setTimeRangeSelection}
|
|
318
|
-
selectQuery={selectQuery}
|
|
319
|
-
selectProfile={selectProfile}
|
|
320
|
-
query={query}
|
|
321
|
-
setQueryExpression={setQueryExpression}
|
|
322
|
-
setNewQueryExpression={setNewQueryExpression}
|
|
323
|
-
utilizationMetrics={utilizationMetrics}
|
|
324
|
-
utilizationMetricsLoading={utilizationMetricsLoading}
|
|
325
|
-
/>
|
|
326
|
-
</>
|
|
336
|
+
</>
|
|
337
|
+
</UtilizationLabelsProvider>
|
|
327
338
|
);
|
|
328
339
|
};
|
|
329
340
|
|
|
@@ -22,7 +22,8 @@ import {useGrpcMetadata} from '@parca/components';
|
|
|
22
22
|
import {Query} from '@parca/parser';
|
|
23
23
|
import {sanitizeLabelValue} from '@parca/utilities';
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import {LabelProvider, useLabels} from '../contexts/SimpleMatchersLabelContext';
|
|
26
|
+
import {useUtilizationLabels} from '../contexts/UtilizationLabelsContext';
|
|
26
27
|
import Select, {type SelectItem} from './Select';
|
|
27
28
|
|
|
28
29
|
interface Props {
|
|
@@ -42,10 +43,16 @@ interface QueryRow {
|
|
|
42
43
|
isLoading: boolean;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
const transformLabelsForSelect = (
|
|
46
|
+
export const transformLabelsForSelect = (
|
|
47
|
+
labelNames: string[],
|
|
48
|
+
shouldTrimPrefix = false
|
|
49
|
+
): SelectItem[] => {
|
|
46
50
|
return labelNames.map(labelName => ({
|
|
47
51
|
key: labelName,
|
|
48
|
-
element: {
|
|
52
|
+
element: {
|
|
53
|
+
active: <>{shouldTrimPrefix ? labelName.split('.').pop() : labelName}</>,
|
|
54
|
+
expanded: <>{shouldTrimPrefix ? labelName.split('.').pop() : labelName}</>,
|
|
55
|
+
},
|
|
49
56
|
}));
|
|
50
57
|
};
|
|
51
58
|
|
|
@@ -99,19 +106,17 @@ const operatorOptions = [
|
|
|
99
106
|
const SimpleMatchers = ({
|
|
100
107
|
queryClient,
|
|
101
108
|
setMatchersString,
|
|
102
|
-
// runQuery,
|
|
103
109
|
currentQuery,
|
|
104
110
|
profileType,
|
|
105
111
|
queryBrowserRef,
|
|
106
112
|
}: Props): JSX.Element => {
|
|
113
|
+
const utilizationLabels = useUtilizationLabels();
|
|
107
114
|
const [queryRows, setQueryRows] = useState<QueryRow[]>([
|
|
108
115
|
{labelName: '', operator: '=', labelValue: '', labelValues: [], isLoading: false},
|
|
109
116
|
]);
|
|
110
117
|
const reactQueryClient = useQueryClient();
|
|
111
118
|
const metadata = useGrpcMetadata();
|
|
112
119
|
|
|
113
|
-
const {loading: labelNamesLoading, result} = useLabelNames(queryClient, profileType);
|
|
114
|
-
const {response: labelNamesResponse, error: labelNamesError} = result;
|
|
115
120
|
const [showAll, setShowAll] = useState(false);
|
|
116
121
|
|
|
117
122
|
const visibleRows = showAll ? queryRows : queryRows.slice(0, 3);
|
|
@@ -121,14 +126,6 @@ const SimpleMatchers = ({
|
|
|
121
126
|
|
|
122
127
|
const currentMatchers = currentQuery.matchersString();
|
|
123
128
|
|
|
124
|
-
const labelNameFromMatchers = useMemo(() => {
|
|
125
|
-
if (currentQuery === undefined) return [];
|
|
126
|
-
|
|
127
|
-
const matchers = currentQuery.matchers;
|
|
128
|
-
|
|
129
|
-
return matchers.map(matcher => matcher.key);
|
|
130
|
-
}, [currentQuery]);
|
|
131
|
-
|
|
132
129
|
const fetchLabelValues = useCallback(
|
|
133
130
|
async (labelName: string): Promise<string[]> => {
|
|
134
131
|
if (labelName == null || labelName === '' || profileType == null || profileType === '') {
|
|
@@ -158,6 +155,13 @@ const SimpleMatchers = ({
|
|
|
158
155
|
[queryClient, metadata, profileType, reactQueryClient]
|
|
159
156
|
);
|
|
160
157
|
|
|
158
|
+
const fetchLabelValuesUtilization = useCallback(
|
|
159
|
+
async (labelName: string): Promise<string[]> => {
|
|
160
|
+
return (await utilizationLabels?.utilizationFetchLabelValues?.(labelName)) ?? [];
|
|
161
|
+
},
|
|
162
|
+
[utilizationLabels]
|
|
163
|
+
);
|
|
164
|
+
|
|
161
165
|
const updateMatchersString = useCallback(
|
|
162
166
|
(rows: QueryRow[]) => {
|
|
163
167
|
const matcherString = rows
|
|
@@ -184,7 +188,10 @@ const SimpleMatchers = ({
|
|
|
184
188
|
const trimmedLabelName = labelName.trim();
|
|
185
189
|
if (trimmedLabelName === '') return null;
|
|
186
190
|
|
|
187
|
-
const labelValues =
|
|
191
|
+
const labelValues =
|
|
192
|
+
utilizationLabels?.utilizationFetchLabelValues !== undefined
|
|
193
|
+
? await fetchLabelValuesUtilization(trimmedLabelName)
|
|
194
|
+
: await fetchLabelValues(trimmedLabelName);
|
|
188
195
|
const sanitizedLabelValue =
|
|
189
196
|
labelValue.startsWith('"') && labelValue.endsWith('"')
|
|
190
197
|
? labelValue.slice(1, -1)
|
|
@@ -205,20 +212,15 @@ const SimpleMatchers = ({
|
|
|
205
212
|
};
|
|
206
213
|
|
|
207
214
|
void fetchAndSetQueryRows();
|
|
208
|
-
}, [
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
: [];
|
|
216
|
-
}, [labelNamesError, labelNamesResponse]);
|
|
215
|
+
}, [
|
|
216
|
+
currentMatchers,
|
|
217
|
+
fetchLabelValues,
|
|
218
|
+
updateMatchersString,
|
|
219
|
+
fetchLabelValuesUtilization,
|
|
220
|
+
utilizationLabels,
|
|
221
|
+
]);
|
|
217
222
|
|
|
218
|
-
const labelNameOptions =
|
|
219
|
-
const uniqueLabelNames = Array.from(new Set([...labelNames, ...labelNameFromMatchers]));
|
|
220
|
-
return transformLabelsForSelect(uniqueLabelNames);
|
|
221
|
-
}, [labelNames, labelNameFromMatchers]);
|
|
223
|
+
const {labelNameOptions, isLoading: labelNamesLoading} = useLabels();
|
|
222
224
|
|
|
223
225
|
const updateRow = useCallback(
|
|
224
226
|
async (index: number, field: keyof QueryRow, value: string): Promise<void> => {
|
|
@@ -232,7 +234,10 @@ const SimpleMatchers = ({
|
|
|
232
234
|
updatedRows[index].isLoading = true;
|
|
233
235
|
setQueryRows([...updatedRows]);
|
|
234
236
|
|
|
235
|
-
const labelValues =
|
|
237
|
+
const labelValues =
|
|
238
|
+
utilizationLabels?.utilizationFetchLabelValues !== undefined
|
|
239
|
+
? await fetchLabelValuesUtilization(value)
|
|
240
|
+
: await fetchLabelValues(value);
|
|
236
241
|
updatedRows[index].labelValues = labelValues;
|
|
237
242
|
updatedRows[index].isLoading = false;
|
|
238
243
|
}
|
|
@@ -240,7 +245,13 @@ const SimpleMatchers = ({
|
|
|
240
245
|
setQueryRows([...updatedRows]);
|
|
241
246
|
updateMatchersString(updatedRows);
|
|
242
247
|
},
|
|
243
|
-
[
|
|
248
|
+
[
|
|
249
|
+
queryRows,
|
|
250
|
+
fetchLabelValues,
|
|
251
|
+
updateMatchersString,
|
|
252
|
+
fetchLabelValuesUtilization,
|
|
253
|
+
utilizationLabels,
|
|
254
|
+
]
|
|
244
255
|
);
|
|
245
256
|
|
|
246
257
|
const handleUpdateRow = useCallback(
|
|
@@ -290,7 +301,10 @@ const SimpleMatchers = ({
|
|
|
290
301
|
setQueryRows([...updatedRows]);
|
|
291
302
|
|
|
292
303
|
try {
|
|
293
|
-
const labelValues =
|
|
304
|
+
const labelValues =
|
|
305
|
+
utilizationLabels?.utilizationFetchLabelValues !== undefined
|
|
306
|
+
? await fetchLabelValuesUtilization(updatedRows[index].labelName)
|
|
307
|
+
: await fetchLabelValues(updatedRows[index].labelName);
|
|
294
308
|
updatedRows[index].labelValues = labelValues;
|
|
295
309
|
} catch (error) {
|
|
296
310
|
console.error('Error fetching label values:', error);
|
|
@@ -303,7 +317,7 @@ const SimpleMatchers = ({
|
|
|
303
317
|
}
|
|
304
318
|
};
|
|
305
319
|
},
|
|
306
|
-
[queryRows, fetchLabelValues]
|
|
320
|
+
[queryRows, fetchLabelValues, fetchLabelValuesUtilization, utilizationLabels]
|
|
307
321
|
);
|
|
308
322
|
|
|
309
323
|
const isRowRegex = (row: QueryRow): boolean => row.operator === '=~' || row.operator === '!~';
|
|
@@ -375,4 +389,22 @@ const SimpleMatchers = ({
|
|
|
375
389
|
);
|
|
376
390
|
};
|
|
377
391
|
|
|
378
|
-
export default
|
|
392
|
+
export default function SimpleMathersWithProvider(props: Props): JSX.Element {
|
|
393
|
+
const labelNameFromMatchers = useMemo(() => {
|
|
394
|
+
if (props.currentQuery === undefined) return [];
|
|
395
|
+
|
|
396
|
+
const matchers = props.currentQuery.matchers;
|
|
397
|
+
|
|
398
|
+
return matchers.map(matcher => matcher.key);
|
|
399
|
+
}, [props.currentQuery]);
|
|
400
|
+
|
|
401
|
+
return (
|
|
402
|
+
<LabelProvider
|
|
403
|
+
queryClient={props.queryClient}
|
|
404
|
+
profileType={props.profileType}
|
|
405
|
+
labelNameFromMatchers={labelNameFromMatchers}
|
|
406
|
+
>
|
|
407
|
+
<SimpleMatchers {...props} />
|
|
408
|
+
</LabelProvider>
|
|
409
|
+
);
|
|
410
|
+
}
|