@parca/profile 0.16.465 → 0.16.466
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 +4 -0
- package/dist/MetricsGraph/UtilizationMetrics/index.d.ts +26 -0
- package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +1 -0
- package/dist/MetricsGraph/UtilizationMetrics/index.js +211 -0
- package/dist/MetricsGraph/index.d.ts +5 -0
- package/dist/MetricsGraph/index.d.ts.map +1 -1
- package/dist/ProfileMetricsGraph/hooks/useQueryRange.d.ts +10 -0
- package/dist/ProfileMetricsGraph/hooks/useQueryRange.d.ts.map +1 -0
- package/dist/ProfileMetricsGraph/hooks/useQueryRange.js +64 -0
- package/dist/ProfileMetricsGraph/index.d.ts +1 -8
- package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
- package/dist/ProfileMetricsGraph/index.js +4 -52
- package/dist/ProfileSelector/MetricsGraphSection.d.ts +4 -2
- package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
- package/dist/ProfileSelector/MetricsGraphSection.js +6 -5
- package/dist/ProfileSelector/index.d.ts +13 -1
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +2 -2
- package/package.json +2 -2
- package/src/MetricsGraph/UtilizationMetrics/index.tsx +512 -0
- package/src/MetricsGraph/index.tsx +2 -1
- package/src/ProfileMetricsGraph/hooks/useQueryRange.ts +94 -0
- package/src/ProfileMetricsGraph/index.tsx +6 -84
- package/src/ProfileSelector/MetricsGraphSection.tsx +46 -26
- package/src/ProfileSelector/index.tsx +17 -1
|
@@ -11,26 +11,19 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {useEffect
|
|
14
|
+
import {useEffect} from 'react';
|
|
15
15
|
|
|
16
|
-
import {RpcError} from '@protobuf-ts/runtime-rpc';
|
|
17
16
|
import {AnimatePresence, motion} from 'framer-motion';
|
|
18
17
|
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
DateTimeRange,
|
|
22
|
-
MetricsGraphSkeleton,
|
|
23
|
-
useGrpcMetadata,
|
|
24
|
-
useParcaContext,
|
|
25
|
-
useURLState,
|
|
26
|
-
} from '@parca/components';
|
|
18
|
+
import {Label, QueryServiceClient} from '@parca/client';
|
|
19
|
+
import {DateTimeRange, MetricsGraphSkeleton, useParcaContext} from '@parca/components';
|
|
27
20
|
import {Query} from '@parca/parser';
|
|
28
|
-
import {capitalizeOnlyFirstLetter
|
|
21
|
+
import {capitalizeOnlyFirstLetter} from '@parca/utilities';
|
|
29
22
|
|
|
30
23
|
import {MergedProfileSelection, ProfileSelection} from '..';
|
|
31
24
|
import MetricsGraph from '../MetricsGraph';
|
|
32
25
|
import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
|
|
33
|
-
import
|
|
26
|
+
import {useQueryRange} from './hooks/useQueryRange';
|
|
34
27
|
|
|
35
28
|
interface ProfileMetricsEmptyStateProps {
|
|
36
29
|
message: string;
|
|
@@ -76,78 +69,6 @@ interface ProfileMetricsGraphProps {
|
|
|
76
69
|
comparing?: boolean;
|
|
77
70
|
}
|
|
78
71
|
|
|
79
|
-
export interface IQueryRangeState {
|
|
80
|
-
response: QueryRangeResponse | null;
|
|
81
|
-
isLoading: boolean;
|
|
82
|
-
error: RpcError | null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const getStepCountFromScreenWidth = (pixelsPerPoint: number): number => {
|
|
86
|
-
let width =
|
|
87
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
88
|
-
window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
|
89
|
-
|
|
90
|
-
// subtract the padding around the graph
|
|
91
|
-
width = width - (20 + 24 + 68) * 2;
|
|
92
|
-
return Math.round(width / pixelsPerPoint);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export const useQueryRange = (
|
|
96
|
-
client: QueryServiceClient,
|
|
97
|
-
queryExpression: string,
|
|
98
|
-
start: number,
|
|
99
|
-
end: number,
|
|
100
|
-
sumBy: string[],
|
|
101
|
-
skip = false
|
|
102
|
-
): IQueryRangeState => {
|
|
103
|
-
const metadata = useGrpcMetadata();
|
|
104
|
-
const [stepCountStr, setStepCount] = useURLState('step_count');
|
|
105
|
-
|
|
106
|
-
const defaultStepCount = useMemo(() => {
|
|
107
|
-
return getStepCountFromScreenWidth(10);
|
|
108
|
-
}, []);
|
|
109
|
-
|
|
110
|
-
const stepCount = useMemo(() => {
|
|
111
|
-
if (stepCountStr != null) {
|
|
112
|
-
return parseInt(stepCountStr as string, 10);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return defaultStepCount;
|
|
116
|
-
}, [stepCountStr, defaultStepCount]);
|
|
117
|
-
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
if (stepCountStr == null) {
|
|
120
|
-
setStepCount(defaultStepCount.toString());
|
|
121
|
-
}
|
|
122
|
-
}, [stepCountStr, defaultStepCount, setStepCount]);
|
|
123
|
-
|
|
124
|
-
const {data, isLoading, error} = useGrpcQuery<QueryRangeResponse | undefined>({
|
|
125
|
-
key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
|
|
126
|
-
queryFn: async () => {
|
|
127
|
-
const stepDuration = getStepDuration(start, end, stepCount);
|
|
128
|
-
const {response} = await client.queryRange(
|
|
129
|
-
{
|
|
130
|
-
query: queryExpression,
|
|
131
|
-
start: Timestamp.fromDate(new Date(start)),
|
|
132
|
-
end: Timestamp.fromDate(new Date(end)),
|
|
133
|
-
step: Duration.create(stepDuration),
|
|
134
|
-
limit: 0,
|
|
135
|
-
sumBy,
|
|
136
|
-
},
|
|
137
|
-
{meta: metadata}
|
|
138
|
-
);
|
|
139
|
-
return response;
|
|
140
|
-
},
|
|
141
|
-
options: {
|
|
142
|
-
retry: false,
|
|
143
|
-
enabled: !skip,
|
|
144
|
-
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
return {isLoading, error: error as RpcError | null, response: data ?? null};
|
|
149
|
-
};
|
|
150
|
-
|
|
151
72
|
const ProfileMetricsGraph = ({
|
|
152
73
|
queryClient,
|
|
153
74
|
queryExpression,
|
|
@@ -184,6 +105,7 @@ const ProfileMetricsGraph = ({
|
|
|
184
105
|
}, [perf, response]);
|
|
185
106
|
|
|
186
107
|
const series = response?.series;
|
|
108
|
+
|
|
187
109
|
const dataAvailable = series !== null && series !== undefined && series?.length > 0;
|
|
188
110
|
|
|
189
111
|
const loading = metricsGraphLoading;
|
|
@@ -18,8 +18,9 @@ import {DateTimeRange} from '@parca/components';
|
|
|
18
18
|
import {Query} from '@parca/parser';
|
|
19
19
|
|
|
20
20
|
import {MergedProfileSelection, ProfileSelection} from '..';
|
|
21
|
+
import UtilizationMetricsGraph from '../MetricsGraph/UtilizationMetrics';
|
|
21
22
|
import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
|
|
22
|
-
import {QuerySelection} from './index';
|
|
23
|
+
import {QuerySelection, type UtilizationMetrics as UtilizationMetricsType} from './index';
|
|
23
24
|
|
|
24
25
|
interface MetricsGraphSectionProps {
|
|
25
26
|
showMetricsGraph: boolean;
|
|
@@ -38,6 +39,8 @@ interface MetricsGraphSectionProps {
|
|
|
38
39
|
query: Query;
|
|
39
40
|
setNewQueryExpression: (queryExpression: string) => void;
|
|
40
41
|
setQueryExpression: (updateTs?: boolean) => void;
|
|
42
|
+
utilizationMetrics?: UtilizationMetricsType[];
|
|
43
|
+
utilizationMetricsLoading?: boolean;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export function MetricsGraphSection({
|
|
@@ -56,6 +59,8 @@ export function MetricsGraphSection({
|
|
|
56
59
|
selectProfile,
|
|
57
60
|
query,
|
|
58
61
|
setNewQueryExpression,
|
|
62
|
+
utilizationMetrics,
|
|
63
|
+
utilizationMetricsLoading,
|
|
59
64
|
}: MetricsGraphSectionProps): JSX.Element {
|
|
60
65
|
const handleTimeRangeChange = (range: DateTimeRange): void => {
|
|
61
66
|
const from = range.getFromMs();
|
|
@@ -141,31 +146,46 @@ export function MetricsGraphSection({
|
|
|
141
146
|
{showMetricsGraph ? 'Hide' : 'Show'} Metrics Graph
|
|
142
147
|
</button>
|
|
143
148
|
{showMetricsGraph && (
|
|
144
|
-
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
149
|
+
<>
|
|
150
|
+
<div style={{height: heightStyle}}>
|
|
151
|
+
{querySelection.expression !== '' &&
|
|
152
|
+
querySelection.from !== undefined &&
|
|
153
|
+
querySelection.to !== undefined ? (
|
|
154
|
+
<>
|
|
155
|
+
{utilizationMetrics !== undefined ? (
|
|
156
|
+
<UtilizationMetricsGraph
|
|
157
|
+
data={utilizationMetrics}
|
|
158
|
+
addLabelMatcher={addLabelMatcher}
|
|
159
|
+
setTimeRange={handleTimeRangeChange}
|
|
160
|
+
utilizationMetricsLoading={utilizationMetricsLoading}
|
|
161
|
+
/>
|
|
162
|
+
) : (
|
|
163
|
+
<>
|
|
164
|
+
<ProfileMetricsGraph
|
|
165
|
+
queryClient={queryClient}
|
|
166
|
+
queryExpression={querySelection.expression}
|
|
167
|
+
from={querySelection.from}
|
|
168
|
+
to={querySelection.to}
|
|
169
|
+
profile={profileSelection}
|
|
170
|
+
comparing={comparing}
|
|
171
|
+
sumBy={querySelection.sumBy ?? sumBy ?? []}
|
|
172
|
+
sumByLoading={defaultSumByLoading}
|
|
173
|
+
setTimeRange={handleTimeRangeChange}
|
|
174
|
+
addLabelMatcher={addLabelMatcher}
|
|
175
|
+
onPointClick={handlePointClick}
|
|
176
|
+
/>
|
|
177
|
+
</>
|
|
178
|
+
)}
|
|
179
|
+
</>
|
|
180
|
+
) : (
|
|
181
|
+
profileSelection === null && (
|
|
182
|
+
<div className="p-2">
|
|
183
|
+
<ProfileMetricsEmptyState message="Please select a profile type and click 'Search' to begin." />
|
|
184
|
+
</div>
|
|
185
|
+
)
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
</>
|
|
169
189
|
)}
|
|
170
190
|
</div>
|
|
171
191
|
);
|
|
@@ -53,6 +53,17 @@ interface ProfileSelectorFeatures {
|
|
|
53
53
|
disableProfileTypesDropdown?: boolean;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export interface UtilizationMetrics {
|
|
57
|
+
timestamp: number;
|
|
58
|
+
value: number;
|
|
59
|
+
resource: {
|
|
60
|
+
[key: string]: string;
|
|
61
|
+
};
|
|
62
|
+
attributes: {
|
|
63
|
+
[key: string]: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
interface ProfileSelectorProps extends ProfileSelectorFeatures {
|
|
57
68
|
queryClient: QueryServiceClient;
|
|
58
69
|
querySelection: QuerySelection;
|
|
@@ -65,6 +76,8 @@ interface ProfileSelectorProps extends ProfileSelectorFeatures {
|
|
|
65
76
|
navigateTo: NavigateFunction;
|
|
66
77
|
setDisplayHideMetricsGraphButton: Dispatch<SetStateAction<boolean>>;
|
|
67
78
|
suffix?: string;
|
|
79
|
+
utilizationMetrics?: UtilizationMetrics[];
|
|
80
|
+
utilizationMetricsLoading?: boolean;
|
|
68
81
|
}
|
|
69
82
|
|
|
70
83
|
export interface IProfileTypesResult {
|
|
@@ -108,6 +121,8 @@ const ProfileSelector = ({
|
|
|
108
121
|
showProfileTypeSelector = true,
|
|
109
122
|
disableExplorativeQuerying = false,
|
|
110
123
|
setDisplayHideMetricsGraphButton,
|
|
124
|
+
utilizationMetrics,
|
|
125
|
+
utilizationMetricsLoading,
|
|
111
126
|
}: ProfileSelectorProps): JSX.Element => {
|
|
112
127
|
const {
|
|
113
128
|
loading: profileTypesLoading,
|
|
@@ -288,7 +303,6 @@ const ProfileSelector = ({
|
|
|
288
303
|
</div>
|
|
289
304
|
)}
|
|
290
305
|
</div>
|
|
291
|
-
|
|
292
306
|
<MetricsGraphSection
|
|
293
307
|
showMetricsGraph={showMetricsGraph}
|
|
294
308
|
setDisplayHideMetricsGraphButton={setDisplayHideMetricsGraphButton}
|
|
@@ -306,6 +320,8 @@ const ProfileSelector = ({
|
|
|
306
320
|
query={query}
|
|
307
321
|
setQueryExpression={setQueryExpression}
|
|
308
322
|
setNewQueryExpression={setNewQueryExpression}
|
|
323
|
+
utilizationMetrics={utilizationMetrics}
|
|
324
|
+
utilizationMetricsLoading={utilizationMetricsLoading}
|
|
309
325
|
/>
|
|
310
326
|
</>
|
|
311
327
|
);
|