Skip to content

Commit

Permalink
Revert "Merge pull request #518 from grafana/svennergr/use-detected-f…
Browse files Browse the repository at this point in the history
…ields-api" (#534)

This reverts commit a38f364, reversing
changes made to 4bfeb6e.
  • Loading branch information
svennergr authored Jul 9, 2024
1 parent 6f68f14 commit 963130d
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 134 deletions.
145 changes: 64 additions & 81 deletions src/Components/ServiceScene/Breakdowns/FieldsBreakdownScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React from 'react';

import { GrafanaTheme2, ReducerID, SelectableValue } from '@grafana/data';
import {
AdHocFiltersVariable,
CustomVariable,
PanelBuilders,
SceneComponentProps,
Expand All @@ -24,35 +23,35 @@ import { Alert, Button, DrawStyle, LoadingPlaceholder, StackingMode, useStyles2
import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics';
import { getFilterBreakdownValueScene } from 'services/fields';
import { getQueryRunner, setLeverColorOverrides } from 'services/panel';
import { buildLokiQuery, renderLogQLStreamSelector } from 'services/query';
import { getSortByPreference } from 'services/store';
import { buildLokiQuery } from 'services/query';
import {
ALL_VARIABLE_VALUE,
LOG_EXPR_WITHOUT_STREAM_SELECTOR,
VAR_FIELDS,
LOG_STREAM_SELECTOR_EXPR,
VAR_FIELD_GROUP_BY,
VAR_FIELDS,
VAR_LABELS,
} from 'services/variables';
import { ServiceScene } from '../ServiceScene';
import { BreakdownSearchScene, getLabelValue } from './BreakdownSearchScene';
import { ByFrameRepeater } from './ByFrameRepeater';
import { FieldSelector } from './FieldSelector';
import { LayoutSwitcher } from './LayoutSwitcher';
import { SortByScene, SortCriteriaChanged } from './SortByScene';
import { StatusWrapper } from './StatusWrapper';
import { DetectedField } from 'models/DetectedField';
import { BreakdownSearchScene, getLabelValue } from './BreakdownSearchScene';
import { SortByScene, SortCriteriaChanged } from './SortByScene';
import { getSortByPreference } from 'services/store';

export interface FieldsBreakdownSceneState extends SceneObjectState {
body?: SceneObject;
search: BreakdownSearchScene;
sort: SortByScene;
fields: Array<SelectableValue<DetectedField>>;
fields: Array<SelectableValue<string>>;

fieldLabel?: string;
value?: string;
loading?: boolean;
error?: string;
blockingMessage?: string;

changeFields?: (n: DetectedField[]) => void;
changeFields?: (n: string[]) => void;
}

export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneState> {
Expand Down Expand Up @@ -104,17 +103,17 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
}

private updateFields() {
const serviceScene = sceneGraph.getAncestor(this, ServiceScene);
const logsScene = sceneGraph.getAncestor(this, ServiceScene);

this.setState({
fields: [
{ label: 'All', value: DetectedField.All },
...(serviceScene.state.fields ?? []).map((f) => ({
label: f.label,
{ label: 'All', value: ALL_VARIABLE_VALUE },
...(logsScene.state.fields ?? []).map((f) => ({
label: f,
value: f,
})),
],
loading: serviceScene.state.loading,
loading: logsScene.state.loading,
});

this.updateBody();
Expand All @@ -131,16 +130,16 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt

private onReferencedVariableValueChanged() {
const variable = this.getVariable();
variable.changeValueTo(DetectedField.All.label);
variable.changeValueTo(ALL_VARIABLE_VALUE);
this.updateBody();
}

private hideFieldByLabel(field: string) {
private hideField(field: string) {
// TODO: store in localstorage that this field was hidden?
const fields = this.state.fields.filter((f) => f.value?.label !== field);
const fields = this.state.fields.filter((f) => f.value !== field);
this.setState({ fields });

this.state.changeFields?.(fields.filter((f) => f.value?.type !== ALL_VARIABLE_VALUE).map((f) => f.value!));
this.state.changeFields?.(fields.filter((f) => f.value !== ALL_VARIABLE_VALUE).map((f) => f.value!));
}

private handleSortByChange = (event: SortCriteriaChanged) => {
Expand All @@ -159,26 +158,18 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
};

private updateBody() {
const fieldLabelVariable = this.getVariable();
const labelVariable = sceneGraph.lookupVariable(VAR_LABELS, this);
if (!(labelVariable instanceof AdHocFiltersVariable)) {
return;
}
const streamSelector = renderLogQLStreamSelector(labelVariable.state.filters);
const detectedField = getFieldByLabel(this.state.fields, fieldLabelVariable.getValueText());

const variable = this.getVariable();
const stateUpdate: Partial<FieldsBreakdownSceneState> = {
fieldLabel: fieldLabelVariable.getValueText(),
value: String(variable.state.value),
blockingMessage: undefined,
};

if (this.state.loading === false && this.state.fields.length === 1) {
stateUpdate.body = this.buildEmptyLayout();
} else {
stateUpdate.body =
fieldLabelVariable.hasAllValue() || !detectedField || detectedField.type === ALL_VARIABLE_VALUE
? this.buildFieldsLayout(this.state.fields, streamSelector)
: buildValuesLayout(detectedField, streamSelector);
stateUpdate.body = variable.hasAllValue()
? this.buildFieldsLayout(this.state.fields)
: buildValuesLayout(variable);
}

this.setState(stateUpdate);
Expand Down Expand Up @@ -212,25 +203,25 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
});
}

private buildFieldsLayout(detectedFields: Array<SelectableValue<DetectedField>>, streamSelector: string) {
private buildFieldsLayout(options: Array<SelectableValue<string>>) {
const children: SceneFlexItemLike[] = [];

for (const option of detectedFields) {
const { value: detectedField } = option;
if (detectedField?.type === ALL_VARIABLE_VALUE || !detectedField) {
for (const option of options) {
const { value: optionValue } = option;
if (optionValue === ALL_VARIABLE_VALUE || !optionValue) {
continue;
}

const query = buildLokiQuery(getExpr(detectedField, streamSelector), {
legendFormat: `{{${detectedField.label}}}`,
refId: detectedField.label,
const query = buildLokiQuery(getExpr(optionValue), {
legendFormat: `{{${optionValue}}}`,
refId: optionValue,
});
const queryRunner = getQueryRunner(query);
let body = PanelBuilders.timeseries().setTitle(detectedField.label).setData(queryRunner);
let body = PanelBuilders.timeseries().setTitle(optionValue).setData(queryRunner);

if (!isAvgFieldType(detectedField.type)) {
if (!isAvgField(optionValue)) {
body = body
.setHeaderActions(new SelectLabelAction({ field: detectedField }))
.setHeaderActions(new SelectLabelAction({ labelName: String(optionValue) }))
.setCustomFieldConfig('stacking', { mode: StackingMode.Normal })
.setCustomFieldConfig('fillOpacity', 100)
.setCustomFieldConfig('lineWidth', 0)
Expand All @@ -245,7 +236,7 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
queryRunner.getResultsStream().subscribe((result) => {
if (result.data.errors && result.data.errors.length > 0) {
const val = result.data.errors[0].refId!;
this.hideFieldByLabel(val);
this.hideField(val);
gridItem.setState({ isHidden: true });
}
});
Expand Down Expand Up @@ -276,48 +267,42 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
});
}

public onFieldSelectorChange = (detectedField?: DetectedField) => {
if (!detectedField) {
public onFieldSelectorChange = (value?: string) => {
if (!value) {
return;
}

const fieldLabelVariable = this.getVariable();
const variable = this.getVariable();
reportAppInteraction(
USER_EVENTS_PAGES.service_details,
USER_EVENTS_ACTIONS.service_details.select_field_in_breakdown_clicked,
{
field: detectedField.label,
previousField: fieldLabelVariable.getValueText(),
field: value,
previousField: variable.getValueText(),
view: 'fields',
}
);

fieldLabelVariable.changeValueTo(detectedField.label);
variable.changeValueTo(value);
};

public static Component = ({ model }: SceneComponentProps<FieldsBreakdownScene>) => {
const { fields, body, loading, fieldLabel, blockingMessage, search, sort } = model.useState();
const { fields, body, loading, value, blockingMessage, search, sort } = model.useState();
const styles = useStyles2(getStyles);
const detectedField = fieldLabel ? getFieldByLabel(fields, fieldLabel) : undefined;

return (
<div className={styles.container}>
<StatusWrapper {...{ isLoading: loading, blockingMessage }}>
<div className={styles.controls}>
{body instanceof LayoutSwitcher && <body.Selector model={body} />}
{!loading && detectedField?.type !== ALL_VARIABLE_VALUE && (
{!loading && value !== ALL_VARIABLE_VALUE && (
<>
<sort.Component model={sort} />
<search.Component model={search} />
</>
)}
{!loading && fields.length > 1 && (
<FieldSelector<DetectedField>
label="Field"
options={fields}
value={detectedField}
onChange={model.onFieldSelectorChange}
/>
<FieldSelector label="Field" options={fields} value={value} onChange={model.onFieldSelectorChange} />
)}
</div>
<div className={styles.content}>{body && <body.Component model={body} />}</div>
Expand All @@ -333,10 +318,6 @@ const emptyStateStyles = {
}),
};

function getFieldByLabel(fields: Array<SelectableValue<DetectedField>>, fieldLabel: string) {
return fields.find((f) => f?.label === fieldLabel)?.value;
}

function getStyles(theme: GrafanaTheme2) {
return {
container: css({
Expand All @@ -361,21 +342,28 @@ function getStyles(theme: GrafanaTheme2) {
};
}

function isAvgFieldType(fieldType: string) {
return ['duration', 'bytes'].includes(fieldType);
const avgFields = ['duration', 'count', 'total', 'bytes'];

function isAvgField(field: string) {
return avgFields.includes(field);
}

function getExpr(field: DetectedField, streamSelector: string) {
if (isAvgFieldType(field.type)) {
return `avg_over_time(${streamSelector} ${LOG_EXPR_WITHOUT_STREAM_SELECTOR} | ${field.parsers[0]} | ${field.label}!="" | unwrap ${field.type}(${field.label}) [$__auto]) by ()`;
function getExpr(field: string) {
if (isAvgField(field)) {
return (
`avg_over_time(${LOG_STREAM_SELECTOR_EXPR} | unwrap ` +
(field === 'duration' ? `duration` : field === 'bytes' ? `bytes` : ``) +
`(${field}) [$__auto]) by ()`
);
}
return `sum by (${field.label}) (count_over_time(${streamSelector} ${LOG_EXPR_WITHOUT_STREAM_SELECTOR} | ${field.parsers[0]} | drop __error__ | ${field.label}!="" [$__auto]))`;
return `sum by (${field}) (count_over_time(${LOG_STREAM_SELECTOR_EXPR} | drop __error__ | ${field}!="" [$__auto]))`;
}

const GRID_TEMPLATE_COLUMNS = 'repeat(auto-fit, minmax(400px, 1fr))';

function buildValuesLayout(field: DetectedField, streamSelector: string) {
const query = buildLokiQuery(getExpr(field, streamSelector), { legendFormat: `{{${field.label}}}` });
function buildValuesLayout(variable: CustomVariable) {
const tagKey = variable.getValueText();
const query = buildLokiQuery(getExpr(tagKey), { legendFormat: `{{${tagKey}}}` });

const { sortBy, direction } = getSortByPreference('fields', ReducerID.stdDev, 'desc');

Expand All @@ -393,7 +381,7 @@ function buildValuesLayout(field: DetectedField, streamSelector: string) {
children: [
new SceneFlexItem({
minHeight: 300,
body: PanelBuilders.timeseries().setTitle(field.label).build(),
body: PanelBuilders.timeseries().setTitle(variable.getValueText()).build(),
}),
],
}),
Expand Down Expand Up @@ -443,7 +431,7 @@ function buildValuesLayout(field: DetectedField, streamSelector: string) {
});
}

export function buildFieldsBreakdownActionScene(changeFieldNumber: (n: DetectedField[]) => void) {
export function buildFieldsBreakdownActionScene(changeFieldNumber: (n: string[]) => void) {
return new SceneFlexLayout({
children: [
new SceneFlexItem({
Expand All @@ -454,21 +442,16 @@ export function buildFieldsBreakdownActionScene(changeFieldNumber: (n: DetectedF
}

interface SelectLabelActionState extends SceneObjectState {
field: DetectedField;
labelName: string;
}
export class SelectLabelAction extends SceneObjectBase<SelectLabelActionState> {
public onClick = () => {
getFieldsBreakdownSceneFor(this).onFieldSelectorChange(this.state.field);
getFieldsBreakdownSceneFor(this).onFieldSelectorChange(this.state.labelName);
};

public static Component = ({ model }: SceneComponentProps<SelectLabelAction>) => {
return (
<Button
variant="secondary"
size="sm"
onClick={model.onClick}
aria-label={`Select ${model.useState().field.label}`}
>
<Button variant="secondary" size="sm" onClick={model.onClick} aria-label={`Select ${model.useState().labelName}`}>
Select
</Button>
);
Expand Down
Loading

0 comments on commit 963130d

Please sign in to comment.