mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Pyroscope: Preselect default profile type or app in the query editor dropdown (#70624)
This commit is contained in:
parent
a2dad8a636
commit
0259ae2630
@ -1,12 +1,11 @@
|
|||||||
import { defaults } from 'lodash';
|
import deepEqual from 'fast-deep-equal';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { CoreApp, QueryEditorProps, TimeRange } from '@grafana/data';
|
import { CoreApp, QueryEditorProps, TimeRange } from '@grafana/data';
|
||||||
import { ButtonCascader, CascaderOption } from '@grafana/ui';
|
import { ButtonCascader, CascaderOption } from '@grafana/ui';
|
||||||
|
|
||||||
import { defaultGrafanaPyroscope, defaultPhlareQueryType, GrafanaPyroscope } from '../dataquery.gen';
|
import { normalizeQuery, PhlareDataSource } from '../datasource';
|
||||||
import { PhlareDataSource } from '../datasource';
|
|
||||||
import { BackendType, PhlareDataSourceOptions, ProfileTypeMessage, Query } from '../types';
|
import { BackendType, PhlareDataSourceOptions, ProfileTypeMessage, Query } from '../types';
|
||||||
|
|
||||||
import { EditorRow } from './EditorRow';
|
import { EditorRow } from './EditorRow';
|
||||||
@ -16,31 +15,18 @@ import { QueryOptions } from './QueryOptions';
|
|||||||
|
|
||||||
export type Props = QueryEditorProps<PhlareDataSource, Query, PhlareDataSourceOptions>;
|
export type Props = QueryEditorProps<PhlareDataSource, Query, PhlareDataSourceOptions>;
|
||||||
|
|
||||||
export const defaultQuery: Partial<GrafanaPyroscope> = {
|
|
||||||
...defaultGrafanaPyroscope,
|
|
||||||
queryType: defaultPhlareQueryType,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function QueryEditor(props: Props) {
|
export function QueryEditor(props: Props) {
|
||||||
let query = normalizeQuery(props.query, props.app);
|
const { onChange, onRunQuery, datasource, query, range, app } = props;
|
||||||
|
|
||||||
function handleRunQuery(value: string) {
|
function handleRunQuery(value: string) {
|
||||||
props.onChange({ ...props.query, labelSelector: value });
|
onChange({ ...query, labelSelector: value });
|
||||||
props.onRunQuery();
|
onRunQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { profileTypes, onProfileTypeChange, selectedProfileName } = useProfileTypes(
|
const { profileTypes, onProfileTypeChange, selectedProfileName } = useProfileTypes(datasource, query, onChange);
|
||||||
props.datasource,
|
const { labels, getLabelValues, onLabelSelectorChange } = useLabels(range, datasource, query, onChange);
|
||||||
props.query,
|
useNormalizeQuery(query, profileTypes, onChange, app);
|
||||||
props.onChange,
|
|
||||||
props.datasource.backendType
|
|
||||||
);
|
|
||||||
const { labels, getLabelValues, onLabelSelectorChange } = useLabels(
|
|
||||||
props.range,
|
|
||||||
props.datasource,
|
|
||||||
props.query,
|
|
||||||
props.onChange
|
|
||||||
);
|
|
||||||
const cascaderOptions = useCascaderOptions(profileTypes);
|
const cascaderOptions = useCascaderOptions(profileTypes);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -64,6 +50,42 @@ export function QueryEditor(props: Props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useNormalizeQuery(
|
||||||
|
query: Query,
|
||||||
|
profileTypes: ProfileTypeMessage[],
|
||||||
|
onChange: (value: Query) => void,
|
||||||
|
app?: CoreApp
|
||||||
|
) {
|
||||||
|
useEffect(() => {
|
||||||
|
const normalizedQuery = normalizeQuery(query, app);
|
||||||
|
// Query can be stored with some old type, or we can have query from different pyro datasource
|
||||||
|
const selectedProfile = query.profileTypeId && profileTypes.find((p) => p.id === query.profileTypeId);
|
||||||
|
if (profileTypes.length && !selectedProfile) {
|
||||||
|
normalizedQuery.profileTypeId = defaultProfileType(profileTypes);
|
||||||
|
}
|
||||||
|
// Makes sure we don't have an infinite loop updates because the normalization creates a new object
|
||||||
|
if (!deepEqual(query, normalizedQuery)) {
|
||||||
|
onChange(normalizedQuery);
|
||||||
|
}
|
||||||
|
}, [app, query, profileTypes, onChange]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultProfileType(profileTypes: ProfileTypeMessage[]): string {
|
||||||
|
const cpuProfiles = profileTypes.filter((p) => p.id.indexOf('cpu') >= 0);
|
||||||
|
if (cpuProfiles.length) {
|
||||||
|
// Prefer cpu time profile if available instead of samples
|
||||||
|
const cpuTimeProfile = cpuProfiles.find((p) => p.id.indexOf('samples') === -1);
|
||||||
|
if (cpuTimeProfile) {
|
||||||
|
return cpuTimeProfile.id;
|
||||||
|
}
|
||||||
|
// Fallback to first cpu profile type
|
||||||
|
return cpuProfiles[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to first profile type from response data
|
||||||
|
return profileTypes[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
function useLabels(
|
function useLabels(
|
||||||
range: TimeRange | undefined,
|
range: TimeRange | undefined,
|
||||||
datasource: PhlareDataSource,
|
datasource: PhlareDataSource,
|
||||||
@ -138,12 +160,7 @@ function useCascaderOptions(profileTypes: ProfileTypeMessage[]) {
|
|||||||
}, [profileTypes]);
|
}, [profileTypes]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function useProfileTypes(
|
function useProfileTypes(datasource: PhlareDataSource, query: Query, onChange: (value: Query) => void) {
|
||||||
datasource: PhlareDataSource,
|
|
||||||
query: Query,
|
|
||||||
onChange: (value: Query) => void,
|
|
||||||
backendType: BackendType = 'phlare'
|
|
||||||
) {
|
|
||||||
const [profileTypes, setProfileTypes] = useState<ProfileTypeMessage[]>([]);
|
const [profileTypes, setProfileTypes] = useState<ProfileTypeMessage[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -160,23 +177,20 @@ function useProfileTypes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const id = selectedOptions[selectedOptions.length - 1].value;
|
const id = selectedOptions[selectedOptions.length - 1].value;
|
||||||
|
|
||||||
// Probably cannot happen but makes TS happy
|
|
||||||
if (typeof id !== 'string') {
|
|
||||||
throw new Error('id is not string');
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange({ ...query, profileTypeId: id });
|
onChange({ ...query, profileTypeId: id });
|
||||||
},
|
},
|
||||||
[onChange, query]
|
[onChange, query]
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectedProfileName = useProfileName(profileTypes, query.profileTypeId, backendType);
|
const selectedProfileName = useProfileName(profileTypes, query.profileTypeId, datasource.backendType);
|
||||||
|
|
||||||
return { profileTypes, onProfileTypeChange, selectedProfileName };
|
return { profileTypes, onProfileTypeChange, selectedProfileName };
|
||||||
}
|
}
|
||||||
|
|
||||||
function useProfileName(profileTypes: ProfileTypeMessage[], profileTypeId: string, backendType: BackendType) {
|
function useProfileName(
|
||||||
|
profileTypes: ProfileTypeMessage[],
|
||||||
|
profileTypeId: string,
|
||||||
|
backendType: BackendType = 'phlare'
|
||||||
|
) {
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
if (!profileTypes) {
|
if (!profileTypes) {
|
||||||
return 'Loading';
|
return 'Loading';
|
||||||
@ -192,13 +206,3 @@ function useProfileName(profileTypes: ProfileTypeMessage[], profileTypeId: strin
|
|||||||
return profile.label;
|
return profile.label;
|
||||||
}, [profileTypeId, profileTypes, backendType]);
|
}, [profileTypeId, profileTypes, backendType]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeQuery(query: Query, app?: CoreApp | string) {
|
|
||||||
let normalized = defaults(query, defaultQuery);
|
|
||||||
if (app !== CoreApp.Explore && normalized.queryType === 'both') {
|
|
||||||
// In dashboards and other places, we can't show both types of graphs at the same time.
|
|
||||||
// This will also be a default when having 'both' query and adding it from explore to dashboard
|
|
||||||
normalized.queryType = 'profile';
|
|
||||||
}
|
|
||||||
return normalized;
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ import { Observable, of } from 'rxjs';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AbstractQuery,
|
AbstractQuery,
|
||||||
|
CoreApp,
|
||||||
DataQueryRequest,
|
DataQueryRequest,
|
||||||
DataQueryResponse,
|
DataQueryResponse,
|
||||||
DataSourceInstanceSettings,
|
DataSourceInstanceSettings,
|
||||||
@ -12,7 +13,7 @@ import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/run
|
|||||||
|
|
||||||
import { extractLabelMatchers, toPromLikeExpr } from '../prometheus/language_utils';
|
import { extractLabelMatchers, toPromLikeExpr } from '../prometheus/language_utils';
|
||||||
|
|
||||||
import { normalizeQuery } from './QueryEditor/QueryEditor';
|
import { defaultGrafanaPyroscope, defaultPhlareQueryType } from './dataquery.gen';
|
||||||
import { PhlareDataSourceOptions, Query, ProfileTypeMessage, BackendType } from './types';
|
import { PhlareDataSourceOptions, Query, ProfileTypeMessage, BackendType } from './types';
|
||||||
|
|
||||||
export class PhlareDataSource extends DataSourceWithBackend<Query, PhlareDataSourceOptions> {
|
export class PhlareDataSource extends DataSourceWithBackend<Query, PhlareDataSourceOptions> {
|
||||||
@ -101,6 +102,25 @@ export class PhlareDataSource extends DataSourceWithBackend<Query, PhlareDataSou
|
|||||||
labelMatchers: extractLabelMatchers(tokens),
|
labelMatchers: extractLabelMatchers(tokens),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefaultQuery(app: CoreApp): Partial<Query> {
|
||||||
|
return defaultQuery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultQuery: Partial<Query> = {
|
||||||
|
...defaultGrafanaPyroscope,
|
||||||
|
queryType: defaultPhlareQueryType,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function normalizeQuery(query: Query, app?: CoreApp | string) {
|
||||||
|
let normalized = { ...query, ...defaultQuery };
|
||||||
|
if (app !== CoreApp.Explore && normalized.queryType === 'both') {
|
||||||
|
// In dashboards and other places, we can't show both types of graphs at the same time.
|
||||||
|
// This will also be a default when having 'both' query and adding it from explore to dashboard
|
||||||
|
normalized.queryType = 'profile';
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
const grammar: Grammar = {
|
const grammar: Grammar = {
|
||||||
|
Loading…
Reference in New Issue
Block a user