From f32afbcb0a38d2264847e816e42ff8814b42cae5 Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Tue, 11 Jun 2024 08:43:39 -0500 Subject: [PATCH 01/15] BarChart: Fix bar widths changing when toggling series (#89010) --- .../barchart-series-toggle.json | 414 ++++++++++++++++++ devenv/jsonnet/dev-dashboards.libsonnet | 1 + .../plugins/panel/barchart/BarChartPanel.tsx | 4 +- .../app/plugins/panel/barchart/utils.test.ts | 1 + public/app/plugins/panel/barchart/utils.ts | 12 +- 5 files changed, 427 insertions(+), 5 deletions(-) create mode 100644 devenv/dev-dashboards/panel-barchart/barchart-series-toggle.json diff --git a/devenv/dev-dashboards/panel-barchart/barchart-series-toggle.json b/devenv/dev-dashboards/panel-barchart/barchart-series-toggle.json new file mode 100644 index 00000000000..8264f5a0315 --- /dev/null +++ b/devenv/dev-dashboards/panel-barchart/barchart-series-toggle.json @@ -0,0 +1,414 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 271, + "links": [], + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1\nStockholm, 10\nNew York, 19\nLondon, 10\nNegative, 15\nLong value, 15", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "One series", + "type": "barchart" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 2, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, 5\nLong value, 15,10", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Two series", + "type": "barchart" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 11 + }, + "id": 3, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "normal", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, 5\nLong value, 15,10", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Two series", + "type": "barchart" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 11 + }, + "id": 4, + "options": { + "barRadius": 0, + "barWidth": 0.7, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "normal", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, 5\nLong value, 15,10", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Two series", + "type": "barchart" + } + ], + "schemaVersion": 39, + "tags": [ + "gdev", + "panel-tests", + "barchart", + "graph-ng" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "BarChart - Panel Tests - Series toggle / bar widths", + "uid": "adoero0hbka9sf", + "version": 4, + "weekStart": "" +} \ No newline at end of file diff --git a/devenv/jsonnet/dev-dashboards.libsonnet b/devenv/jsonnet/dev-dashboards.libsonnet index 6f9c0296685..6c9eb8afb30 100644 --- a/devenv/jsonnet/dev-dashboards.libsonnet +++ b/devenv/jsonnet/dev-dashboards.libsonnet @@ -17,6 +17,7 @@ "bar_gauge_demo": (import '../dev-dashboards/panel-bargauge/bar_gauge_demo.json'), "barchart-autosizing": (import '../dev-dashboards/panel-barchart/barchart-autosizing.json'), "barchart-label-rotation-skipping": (import '../dev-dashboards/panel-barchart/barchart-label-rotation-skipping.json'), + "barchart-series-toggle": (import '../dev-dashboards/panel-barchart/barchart-series-toggle.json'), "barchart-thresholds-mappings": (import '../dev-dashboards/panel-barchart/barchart-thresholds-mappings.json'), "barchart-tooltips-legends": (import '../dev-dashboards/panel-barchart/barchart-tooltips-legends.json'), "candlestick": (import '../dev-dashboards/panel-candlestick/candlestick.json'), diff --git a/public/app/plugins/panel/barchart/BarChartPanel.tsx b/public/app/plugins/panel/barchart/BarChartPanel.tsx index b1b11abd435..13555415e9d 100644 --- a/public/app/plugins/panel/barchart/BarChartPanel.tsx +++ b/public/app/plugins/panel/barchart/BarChartPanel.tsx @@ -92,12 +92,13 @@ export const BarChartPanel = (props: PanelProps) => { const xGroupsCount = vizSeries[0]?.length ?? 0; const seriesCount = vizSeries[0]?.fields.length ?? 0; + const totalSeries = info.series[0].fields.length - 1; let { builder, prepData } = useMemo( () => { return xGroupsCount === 0 ? { builder: null, prepData: null } - : prepConfig({ series: vizSeries, color: info.color, orientation, options, timeZone, theme }); + : prepConfig({ series: vizSeries, totalSeries, color: info.color, orientation, options, timeZone, theme }); }, // eslint-disable-next-line react-hooks/exhaustive-deps [ @@ -105,6 +106,7 @@ export const BarChartPanel = (props: PanelProps) => { timeZone, props.data.structureRev, + totalSeries, seriesCount, xGroupsCount, diff --git a/public/app/plugins/panel/barchart/utils.test.ts b/public/app/plugins/panel/barchart/utils.test.ts index 606d0e41229..f2407cb3a79 100644 --- a/public/app/plugins/panel/barchart/utils.test.ts +++ b/public/app/plugins/panel/barchart/utils.test.ts @@ -94,6 +94,7 @@ describe('BarChart utils', () => { describe('preparePlotConfigBuilder', () => { const config: PrepConfigOpts = { series: [mockDataFrame()], + totalSeries: 2, // color?: Field | null; timeZone: defaultTimeZone, theme: createTheme(), diff --git a/public/app/plugins/panel/barchart/utils.ts b/public/app/plugins/panel/barchart/utils.ts index 54994587323..117d43e327a 100644 --- a/public/app/plugins/panel/barchart/utils.ts +++ b/public/app/plugins/panel/barchart/utils.ts @@ -148,7 +148,8 @@ export function prepSeries( } export interface PrepConfigOpts { - series: DataFrame[]; + series: DataFrame[]; // series with hideFrom.viz: false + totalSeries: number; // total series count (including hidden) color?: Field | null; orientation: VizOrientation; options: Options; @@ -156,7 +157,7 @@ export interface PrepConfigOpts { theme: GrafanaTheme2; } -export const prepConfig = ({ series, color, orientation, options, timeZone, theme }: PrepConfigOpts) => { +export const prepConfig = ({ series, totalSeries, color, orientation, options, timeZone, theme }: PrepConfigOpts) => { let { showValue, groupWidth, @@ -205,8 +206,11 @@ export const prepConfig = ({ series, color, orientation, options, timeZone, them const vizOrientation = getScaleOrientation(orientation); // Use bar width when only one field - if (frame.fields.length === 2) { - groupWidth = barWidth; + if (frame.fields.length === 2 && stacking === StackingMode.None) { + if (totalSeries === 1) { + groupWidth = barWidth; + } + barWidth = 1; } From ff0c9bd66a8f1b1a81710db4e452107afecd5cf0 Mon Sep 17 00:00:00 2001 From: Matias Chomicki Date: Tue, 11 Jun 2024 15:54:41 +0200 Subject: [PATCH 02/15] Log: Added panel support for filtering callbacks (#88980) * chore: update stale comment * Logs Panel: add props for interactive callbacks * LogsPanel: type guard unknown props * chore: add comments * chore: rename popover filtering callbacks prop names * chore: format panelcfg * Formatting * LogsPanel: add default label filter handlers using panel context * Formatting * chore: add tests for new props --- .../logs/panelcfg/x/LogsPanelCfg_types.gen.ts | 8 +++ public/app/features/explore/Explore.tsx | 11 ++- public/app/features/explore/Logs/Logs.tsx | 8 +-- .../features/explore/Logs/LogsContainer.tsx | 8 +-- .../explore/Logs/PopoverMenu.test.tsx | 24 +++---- .../app/features/explore/Logs/PopoverMenu.tsx | 18 ++--- .../features/logs/components/LogRows.test.tsx | 4 +- .../app/features/logs/components/LogRows.tsx | 10 +-- .../app/plugins/panel/logs/LogsPanel.test.tsx | 57 ++++++++++++++++ public/app/plugins/panel/logs/LogsPanel.tsx | 68 ++++++++++++++++++- public/app/plugins/panel/logs/panelcfg.cue | 24 ++++--- public/app/plugins/panel/logs/panelcfg.gen.ts | 8 +++ public/app/plugins/panel/logs/types.ts | 28 ++++++++ 13 files changed, 222 insertions(+), 54 deletions(-) diff --git a/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts index 5ec7f5e4314..250f6d83143 100644 --- a/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts @@ -15,6 +15,14 @@ export const pluginVersion = "11.1.0-pre"; export interface Options { dedupStrategy: common.LogsDedupStrategy; enableLogDetails: boolean; + isFilterLabelActive?: unknown; + /** + * TODO: figure out how to define callbacks + */ + onClickFilterLabel?: unknown; + onClickFilterOutLabel?: unknown; + onClickFilterOutString?: unknown; + onClickFilterString?: unknown; prettifyLogMessage: boolean; showCommonLabels: boolean; showLabels: boolean; diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index a890bcf48c1..a7a31d5075b 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -188,8 +188,7 @@ export class Explore extends React.PureComponent { /** * Used by Logs details. - * Returns true if all queries have the filter, otherwise false. - * TODO: In the future, we would like to return active filters based the query that produced the log line. + * Returns true if the query identified by `refId` has a filter with the provided key and value. * @alpha */ isFilterLabelActive = async (key: string, value: string | number, refId?: string) => { @@ -235,14 +234,14 @@ export class Explore extends React.PureComponent { /** * Used by Logs Popover Menu. */ - onClickFilterValue = (value: string | number, refId?: string) => { + onClickFilterString = (value: string | number, refId?: string) => { this.onModifyQueries({ type: 'ADD_STRING_FILTER', options: { value: value.toString() } }, refId); }; /** * Used by Logs Popover Menu. */ - onClickFilterOutValue = (value: string | number, refId?: string) => { + onClickFilterOutString = (value: string | number, refId?: string) => { this.onModifyQueries({ type: 'ADD_STRING_FILTER_OUT', options: { value: value.toString() } }, refId); }; @@ -437,8 +436,8 @@ export class Explore extends React.PureComponent { splitOpenFn={this.onSplitOpen('logs')} scrollElement={this.scrollElement} isFilterLabelActive={this.isFilterLabelActive} - onClickFilterValue={this.onClickFilterValue} - onClickFilterOutValue={this.onClickFilterOutValue} + onClickFilterString={this.onClickFilterString} + onClickFilterOutString={this.onClickFilterOutString} /> ); diff --git a/public/app/features/explore/Logs/Logs.tsx b/public/app/features/explore/Logs/Logs.tsx index e49468555f5..3a912190381 100644 --- a/public/app/features/explore/Logs/Logs.tsx +++ b/public/app/features/explore/Logs/Logs.tsx @@ -109,8 +109,8 @@ interface Props extends Themeable2 { isFilterLabelActive?: (key: string, value: string, refId?: string) => Promise; logsFrames?: DataFrame[]; range: TimeRange; - onClickFilterValue?: (value: string, refId?: string) => void; - onClickFilterOutValue?: (value: string, refId?: string) => void; + onClickFilterString?: (value: string, refId?: string) => void; + onClickFilterOutString?: (value: string, refId?: string) => void; loadMoreLogs?(range: AbsoluteTimeRange): void; } @@ -943,8 +943,8 @@ class UnthemedLogs extends PureComponent { scrollIntoView={this.scrollIntoView} isFilterLabelActive={this.props.isFilterLabelActive} containerRendered={!!this.state.logsContainer} - onClickFilterValue={this.props.onClickFilterValue} - onClickFilterOutValue={this.props.onClickFilterOutValue} + onClickFilterString={this.props.onClickFilterString} + onClickFilterOutString={this.props.onClickFilterOutString} /> diff --git a/public/app/features/explore/Logs/LogsContainer.tsx b/public/app/features/explore/Logs/LogsContainer.tsx index 48a173cd064..528daafa093 100644 --- a/public/app/features/explore/Logs/LogsContainer.tsx +++ b/public/app/features/explore/Logs/LogsContainer.tsx @@ -58,8 +58,8 @@ interface LogsContainerProps extends PropsFromRedux { splitOpenFn: SplitOpen; scrollElement?: HTMLDivElement; isFilterLabelActive: (key: string, value: string, refId?: string) => Promise; - onClickFilterValue: (value: string, refId?: string) => void; - onClickFilterOutValue: (value: string, refId?: string) => void; + onClickFilterString: (value: string, refId?: string) => void; + onClickFilterOutString: (value: string, refId?: string) => void; } type DataSourceInstance = @@ -350,8 +350,8 @@ class LogsContainer extends PureComponent diff --git a/public/app/features/explore/Logs/PopoverMenu.test.tsx b/public/app/features/explore/Logs/PopoverMenu.test.tsx index 879e9fc0be5..81b9371526a 100644 --- a/public/app/features/explore/Logs/PopoverMenu.test.tsx +++ b/public/app/features/explore/Logs/PopoverMenu.test.tsx @@ -15,9 +15,9 @@ test('Does not render if the filter functions are not defined', () => { }); test('Renders copy and line contains filter', async () => { - const onClickFilterValue = jest.fn(); + const onClickFilterString = jest.fn(); render( - {}} onClickFilterValue={onClickFilterValue} /> + {}} onClickFilterString={onClickFilterString} /> ); expect(screen.getByText('Copy selection')).toBeInTheDocument(); @@ -25,12 +25,12 @@ test('Renders copy and line contains filter', async () => { await userEvent.click(screen.getByText('Add as line contains filter')); - expect(onClickFilterValue).toHaveBeenCalledTimes(1); - expect(onClickFilterValue).toHaveBeenCalledWith('test', row.dataFrame.refId); + expect(onClickFilterString).toHaveBeenCalledTimes(1); + expect(onClickFilterString).toHaveBeenCalledWith('test', row.dataFrame.refId); }); test('Renders copy and line does not contain filter', async () => { - const onClickFilterOutValue = jest.fn(); + const onClickFilterOutString = jest.fn(); render( { y={0} row={row} close={() => {}} - onClickFilterOutValue={onClickFilterOutValue} + onClickFilterOutString={onClickFilterOutString} /> ); @@ -47,8 +47,8 @@ test('Renders copy and line does not contain filter', async () => { await userEvent.click(screen.getByText('Add as line does not contain filter')); - expect(onClickFilterOutValue).toHaveBeenCalledTimes(1); - expect(onClickFilterOutValue).toHaveBeenCalledWith('test', row.dataFrame.refId); + expect(onClickFilterOutString).toHaveBeenCalledTimes(1); + expect(onClickFilterOutString).toHaveBeenCalledWith('test', row.dataFrame.refId); }); test('Renders copy, line contains filter, and line does not contain filter', () => { @@ -59,8 +59,8 @@ test('Renders copy, line contains filter, and line does not contain filter', () y={0} row={row} close={() => {}} - onClickFilterValue={() => {}} - onClickFilterOutValue={() => {}} + onClickFilterString={() => {}} + onClickFilterOutString={() => {}} /> ); @@ -78,8 +78,8 @@ test('Can be dismissed with escape', async () => { y={0} row={row} close={close} - onClickFilterValue={() => {}} - onClickFilterOutValue={() => {}} + onClickFilterString={() => {}} + onClickFilterOutString={() => {}} /> ); diff --git a/public/app/features/explore/Logs/PopoverMenu.tsx b/public/app/features/explore/Logs/PopoverMenu.tsx index fe62668307c..6bc0265145c 100644 --- a/public/app/features/explore/Logs/PopoverMenu.tsx +++ b/public/app/features/explore/Logs/PopoverMenu.tsx @@ -11,8 +11,8 @@ interface PopoverMenuProps { selection: string; x: number; y: number; - onClickFilterValue?: (value: string, refId?: string) => void; - onClickFilterOutValue?: (value: string, refId?: string) => void; + onClickFilterString?: (value: string, refId?: string) => void; + onClickFilterOutString?: (value: string, refId?: string) => void; row: LogRowModel; close: () => void; } @@ -20,8 +20,8 @@ interface PopoverMenuProps { export const PopoverMenu = ({ x, y, - onClickFilterValue, - onClickFilterOutValue, + onClickFilterString, + onClickFilterOutString, selection, row, close, @@ -42,7 +42,7 @@ export const PopoverMenu = ({ }; }, [close]); - const supported = onClickFilterValue || onClickFilterOutValue; + const supported = onClickFilterString || onClickFilterOutString; if (!supported) { return null; @@ -59,21 +59,21 @@ export const PopoverMenu = ({ track('copy', selection.length, row.datasourceType); }} /> - {onClickFilterValue && ( + {onClickFilterString && ( { - onClickFilterValue(selection, row.dataFrame.refId); + onClickFilterString(selection, row.dataFrame.refId); close(); track('line_contains', selection.length, row.datasourceType); }} /> )} - {onClickFilterOutValue && ( + {onClickFilterOutString && ( { - onClickFilterOutValue(selection, row.dataFrame.refId); + onClickFilterOutString(selection, row.dataFrame.refId); close(); track('line_does_not_contain', selection.length, row.datasourceType); }} diff --git a/public/app/features/logs/components/LogRows.test.tsx b/public/app/features/logs/components/LogRows.test.tsx index f412cb22cf7..5bf9fcb53da 100644 --- a/public/app/features/logs/components/LogRows.test.tsx +++ b/public/app/features/logs/components/LogRows.test.tsx @@ -221,8 +221,8 @@ describe('Popover menu', () => { logsSortOrder={LogsSortOrder.Descending} enableLogDetails={true} displayedFields={[]} - onClickFilterOutValue={() => {}} - onClickFilterValue={() => {}} + onClickFilterOutString={() => {}} + onClickFilterString={() => {}} app={app} /> ); diff --git a/public/app/features/logs/components/LogRows.tsx b/public/app/features/logs/components/LogRows.tsx index 3049d5ba9d7..400266137f4 100644 --- a/public/app/features/logs/components/LogRows.tsx +++ b/public/app/features/logs/components/LogRows.tsx @@ -68,8 +68,8 @@ export interface Props extends Themeable2 { * Any overflowing content will be clipped at the table boundary. */ overflowingContent?: boolean; - onClickFilterValue?: (value: string, refId?: string) => void; - onClickFilterOutValue?: (value: string, refId?: string) => void; + onClickFilterString?: (value: string, refId?: string) => void; + onClickFilterOutString?: (value: string, refId?: string) => void; } interface State { @@ -107,7 +107,7 @@ class UnThemedLogRows extends PureComponent { if (!config.featureToggles.logRowsPopoverMenu || this.props.app !== CoreApp.Explore) { return false; } - return Boolean(this.props.onClickFilterOutValue || this.props.onClickFilterValue); + return Boolean(this.props.onClickFilterOutString || this.props.onClickFilterString); } handleSelection = (e: MouseEvent, row: LogRowModel): boolean => { @@ -217,8 +217,8 @@ class UnThemedLogRows extends PureComponent { row={this.state.selectedRow} selection={this.state.selection} {...this.state.popoverMenuCoordinates} - onClickFilterValue={rest.onClickFilterValue} - onClickFilterOutValue={rest.onClickFilterOutValue} + onClickFilterString={rest.onClickFilterString} + onClickFilterOutString={rest.onClickFilterOutString} /> )} diff --git a/public/app/plugins/panel/logs/LogsPanel.test.tsx b/public/app/plugins/panel/logs/LogsPanel.test.tsx index 2230f70dae1..400423a8842 100644 --- a/public/app/plugins/panel/logs/LogsPanel.test.tsx +++ b/public/app/plugins/panel/logs/LogsPanel.test.tsx @@ -324,6 +324,63 @@ describe('LogsPanel', () => { expect(jest.mocked(styles.getLogRowStyles).mock.calls.length).toBeGreaterThan(3); }); }); + + describe('Filters', () => { + const series = [ + createDataFrame({ + refId: 'A', + fields: [ + { + name: 'time', + type: FieldType.time, + values: ['2019-04-26T09:28:11.352440161Z'], + }, + { + name: 'message', + type: FieldType.string, + values: ['logline text'], + labels: { + app: 'common_app', + }, + }, + ], + }), + ]; + + it('allow to filter for a value or filter out a value', async () => { + const filterForMock = jest.fn(); + const filterOutMock = jest.fn(); + const isFilterLabelActiveMock = jest.fn(); + setup({ + data: { + series, + }, + options: { + showLabels: false, + showTime: false, + wrapLogMessage: false, + showCommonLabels: false, + prettifyLogMessage: false, + sortOrder: LogsSortOrder.Descending, + dedupStrategy: LogsDedupStrategy.none, + enableLogDetails: true, + onClickFilterLabel: filterForMock, + onClickFilterOutLabel: filterOutMock, + isFilterLabelActive: isFilterLabelActiveMock, + }, + }); + + expect(await screen.findByRole('row')).toBeInTheDocument(); + + await userEvent.click(screen.getByText('logline text')); + await userEvent.click(screen.getByLabelText('Filter for value in query A')); + expect(filterForMock).toHaveBeenCalledTimes(1); + await userEvent.click(screen.getByLabelText('Filter out value in query A')); + expect(filterOutMock).toHaveBeenCalledTimes(1); + + expect(isFilterLabelActiveMock).toHaveBeenCalledTimes(1); + }); + }); }); const setup = (propsOverrides?: {}) => { diff --git a/public/app/plugins/panel/logs/LogsPanel.tsx b/public/app/plugins/panel/logs/LogsPanel.tsx index 40aa8dab3f0..0bafecd0004 100644 --- a/public/app/plugins/panel/logs/LogsPanel.tsx +++ b/public/app/plugins/panel/logs/LogsPanel.tsx @@ -29,10 +29,34 @@ import { LogLabels } from '../../../features/logs/components/LogLabels'; import { LogRows } from '../../../features/logs/components/LogRows'; import { COMMON_LABELS, dataFrameToLogsModel, dedupLogRows } from '../../../features/logs/logsModel'; -import { Options } from './types'; +import { + isIsFilterLabelActive, + isOnClickFilterLabel, + isOnClickFilterOutLabel, + isOnClickFilterOutString, + isOnClickFilterString, + Options, +} from './types'; import { useDatasourcesFromTargets } from './useDatasourcesFromTargets'; -interface LogsPanelProps extends PanelProps {} +interface LogsPanelProps extends PanelProps { + /** + * Adds a key => value filter to the query referenced by the provided DataFrame refId. Used by Log details and Logs table. + * onClickFilterLabel?: (key: string, value: string, frame?: DataFrame) => void; + * + * Adds a negative key => value filter to the query referenced by the provided DataFrame refId. Used by Log details and Logs table. + * onClickFilterOutLabel?: (key: string, value: string, frame?: DataFrame) => void; + * + * Adds a string filter to the query referenced by the provided DataFrame refId. Used by the Logs popover menu. + * onClickFilterOutString?: (value: string, refId?: string) => void; + * + * Removes a string filter to the query referenced by the provided DataFrame refId. Used by the Logs popover menu. + * onClickFilterString?: (value: string, refId?: string) => void; + * + * Determines if a given key => value filter is active in a given query. Used by Log details. + * isFilterLabelActive?: (key: string, value: string, refId?: string) => Promise; + */ +} interface LogsPermalinkUrlState { logs?: { id?: string; @@ -55,6 +79,11 @@ export const LogsPanel = ({ dedupStrategy, enableLogDetails, showLogContextToggle, + onClickFilterLabel, + onClickFilterOutLabel, + onClickFilterOutString, + onClickFilterString, + isFilterLabelActive, }, id, }: LogsPanelProps) => { @@ -68,7 +97,7 @@ export const LogsPanel = ({ const [scrollElement, setScrollElement] = useState(null); let closeCallback = useRef<() => void>(); - const { eventBus } = usePanelContext(); + const { eventBus, onAddAdHocFilter } = usePanelContext(); const onLogRowHover = useCallback( (row?: LogRowModel) => { if (!row) { @@ -220,6 +249,28 @@ export const LogsPanel = ({ [scrollElement] ); + const defaultOnClickFilterLabel = useCallback( + (key: string, value: string) => { + onAddAdHocFilter?.({ + key, + value, + operator: '=', + }); + }, + [onAddAdHocFilter] + ); + + const defaultOnClickFilterOutLabel = useCallback( + (key: string, value: string) => { + onAddAdHocFilter?.({ + key, + value, + operator: '!=', + }); + }, + [onAddAdHocFilter] + ); + if (!data || logRows.length === 0) { return ; } @@ -275,6 +326,17 @@ export const LogsPanel = ({ onLogRowHover={onLogRowHover} app={CoreApp.Dashboard} onOpenContext={onOpenContext} + onClickFilterLabel={ + isOnClickFilterLabel(onClickFilterLabel) ? onClickFilterLabel : defaultOnClickFilterLabel + } + onClickFilterOutLabel={ + isOnClickFilterOutLabel(onClickFilterOutLabel) ? onClickFilterOutLabel : defaultOnClickFilterOutLabel + } + onClickFilterString={isOnClickFilterString(onClickFilterString) ? onClickFilterString : undefined} + onClickFilterOutString={ + isOnClickFilterOutString(onClickFilterOutString) ? onClickFilterOutString : undefined + } + isFilterLabelActive={isIsFilterLabelActive(isFilterLabelActive) ? isFilterLabelActive : undefined} /> {showCommonLabels && isAscending && renderCommonLabels()} diff --git a/public/app/plugins/panel/logs/panelcfg.cue b/public/app/plugins/panel/logs/panelcfg.cue index fd3719ec65c..481fb99efe6 100644 --- a/public/app/plugins/panel/logs/panelcfg.cue +++ b/public/app/plugins/panel/logs/panelcfg.cue @@ -26,15 +26,21 @@ composableKinds: PanelCfg: { version: [0, 0] schema: { Options: { - showLabels: bool - showCommonLabels: bool - showTime: bool - showLogContextToggle: bool - wrapLogMessage: bool - prettifyLogMessage: bool - enableLogDetails: bool - sortOrder: common.LogsSortOrder - dedupStrategy: common.LogsDedupStrategy + showLabels: bool + showCommonLabels: bool + showTime: bool + showLogContextToggle: bool + wrapLogMessage: bool + prettifyLogMessage: bool + enableLogDetails: bool + sortOrder: common.LogsSortOrder + dedupStrategy: common.LogsDedupStrategy + // TODO: figure out how to define callbacks + onClickFilterLabel?: _ + onClickFilterOutLabel?: _ + isFilterLabelActive?: _ + onClickFilterString?: _ + onClickFilterOutString?: _ } @cuetsy(kind="interface") } }] diff --git a/public/app/plugins/panel/logs/panelcfg.gen.ts b/public/app/plugins/panel/logs/panelcfg.gen.ts index 62e8fc956ed..c41589a2ce0 100644 --- a/public/app/plugins/panel/logs/panelcfg.gen.ts +++ b/public/app/plugins/panel/logs/panelcfg.gen.ts @@ -13,6 +13,14 @@ import * as common from '@grafana/schema'; export interface Options { dedupStrategy: common.LogsDedupStrategy; enableLogDetails: boolean; + isFilterLabelActive?: unknown; + /** + * TODO: figure out how to define callbacks + */ + onClickFilterLabel?: unknown; + onClickFilterOutLabel?: unknown; + onClickFilterOutString?: unknown; + onClickFilterString?: unknown; prettifyLogMessage: boolean; showCommonLabels: boolean; showLabels: boolean; diff --git a/public/app/plugins/panel/logs/types.ts b/public/app/plugins/panel/logs/types.ts index 1473f1fd934..711bd17c54f 100644 --- a/public/app/plugins/panel/logs/types.ts +++ b/public/app/plugins/panel/logs/types.ts @@ -1 +1,29 @@ +import { DataFrame } from '@grafana/data'; + export { Options } from './panelcfg.gen'; + +type onClickFilterLabelType = (key: string, value: string, frame?: DataFrame) => void; +type onClickFilterOutLabelType = (key: string, value: string, frame?: DataFrame) => void; +type onClickFilterValueType = (value: string, refId?: string) => void; +type onClickFilterOutStringType = (value: string, refId?: string) => void; +type isFilterLabelActiveType = (key: string, value: string, refId?: string) => Promise; + +export function isOnClickFilterLabel(callback: unknown): callback is onClickFilterLabelType { + return typeof callback === 'function'; +} + +export function isOnClickFilterOutLabel(callback: unknown): callback is onClickFilterOutLabelType { + return typeof callback === 'function'; +} + +export function isOnClickFilterString(callback: unknown): callback is onClickFilterValueType { + return typeof callback === 'function'; +} + +export function isOnClickFilterOutString(callback: unknown): callback is onClickFilterOutStringType { + return typeof callback === 'function'; +} + +export function isIsFilterLabelActive(callback: unknown): callback is isFilterLabelActiveType { + return typeof callback === 'function'; +} From c71f8b8b1f3460132b687b8901b2abce179bba2e Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Tue, 11 Jun 2024 15:13:49 +0100 Subject: [PATCH 03/15] Keybinds: Allow move time range shortcuts (t left / t right) to be chained (#88904) * Allow for chaining timeshift shortcuts * ignore repeated shortcuts --- .../app/core/services/mousetrap/Mousetrap.ts | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/public/app/core/services/mousetrap/Mousetrap.ts b/public/app/core/services/mousetrap/Mousetrap.ts index 3f7d1503135..39eddc3670f 100644 --- a/public/app/core/services/mousetrap/Mousetrap.ts +++ b/public/app/core/services/mousetrap/Mousetrap.ts @@ -627,6 +627,18 @@ export class Mousetrap { // keep a list of which sequences were matches for later doNotReset[seq] = 1; this._fireCallback(callbacks[i].callback, e, callbacks[i].combo, seq); + + // When matching a callback, don't reset other callbacks that starts with this prefix + // This allows chaining of multiple shortcuts that share a prefix. e.g. if we have + // `t left` and `t right`, allow user to hit `t left`, `right` without resetting the sequence + const suffixPrefixIndex = seq.lastIndexOf(character); + const sequencePrefix = seq.slice(0, suffixPrefixIndex); + for (const [seq, level] of Object.entries(this._sequenceLevels)) { + if (level > 0 && seq.startsWith(sequencePrefix)) { + doNotReset[seq] = 1; + } + } + continue; } @@ -637,6 +649,14 @@ export class Mousetrap { } } + // Don't reset a sequence if this character is the start of a sequence that has already progressed. + // This allows `t left` to be hit immediately after a `t right` + for (const callback of this._callbacks[character] ?? []) { + if (callback.action === e.type && callback.seq && callback.level === 0) { + doNotReset[callback.seq] = 1; + } + } + // if the key you pressed matches the type of sequence without // being a modifier (ie "keyup" or "keypress") then we should // reset all sequences that were not matched by this event @@ -675,6 +695,11 @@ export class Mousetrap { } const event: KeyboardEvent = rawEvent; + // Don't trigger shortcuts when a key is just held down + if (event.repeat) { + return; + } + // normalize e.which for key events // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion if (typeof event.which !== 'number') { @@ -743,9 +768,9 @@ export class Mousetrap { this._ignoreNextKeyup = characterFromEvent(e); } - // weird race condition if a sequence ends with the key - // another sequence begins with - setTimeout(this._resetSequences, 10); + // Reset the sequence timer and allow for this shortcut to be + // triggered again just by repeating the last key + this._resetSequenceTimer(); }; // loop through keys one at a time and bind the appropriate callback From 994ddf7e6f37523f1472d3bf832f76241440c724 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Tue, 11 Jun 2024 15:45:10 +0100 Subject: [PATCH 04/15] RecentlyDeleted: Remove search box (#89043) * RecentlyDeleted: Remove search box * i18n --- .../components/BrowseFilters.tsx | 2 +- .../manage-dashboards/RecentlyDeletedPage.tsx | 10 -------- .../search/page/components/ActionRow.tsx | 24 +++++++++---------- public/locales/en-US/grafana.json | 5 ---- public/locales/pseudo-LOCALE/grafana.json | 5 ---- 5 files changed, 13 insertions(+), 33 deletions(-) diff --git a/public/app/features/browse-dashboards/components/BrowseFilters.tsx b/public/app/features/browse-dashboards/components/BrowseFilters.tsx index 43e64d57cc2..2e339fbd012 100644 --- a/public/app/features/browse-dashboards/components/BrowseFilters.tsx +++ b/public/app/features/browse-dashboards/components/BrowseFilters.tsx @@ -11,11 +11,11 @@ export function BrowseFilters() {
{ return ( - stateManager.onQueryChange(e)} - /> void; onSortChange: (value?: string) => void; onStarredFilterChange?: (event: FormEvent) => void; onTagFilterChange: (tags: string[]) => void; getTagOptions: () => Promise; getSortOptions: () => Promise; - sortPlaceholder?: string; onDatasourceChange: (ds?: string) => void; onPanelTypeChange: (pt?: string) => void; - includePanels: boolean; onSetIncludePanels: (v: boolean) => void; - state: SearchState; - showStarredFilter?: boolean; - hideLayout?: boolean; } export function getValidQueryLayout(q: SearchState): SearchLayout { @@ -52,20 +52,20 @@ export function getValidQueryLayout(q: SearchState): SearchLayout { } export const ActionRow = ({ + state, + showStarredFilter, + showLayout, + sortPlaceholder, onLayoutChange, onSortChange, onStarredFilterChange = () => {}, onTagFilterChange, getTagOptions, getSortOptions, - sortPlaceholder, onDatasourceChange, onPanelTypeChange, onSetIncludePanels, - state, - showStarredFilter, - hideLayout, -}: Props) => { +}: ActionRowProps) => { const styles = useStyles2(getStyles); const layout = getValidQueryLayout(state); @@ -113,7 +113,7 @@ export const ActionRow = ({ - {!hideLayout && ( + {showLayout && ( Date: Tue, 11 Jun 2024 17:17:01 +0200 Subject: [PATCH 05/15] Docs: fixes overview text height (#89044) * Docs: fixes overview text height * deletes word documentation --- docs/sources/_index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/sources/_index.md b/docs/sources/_index.md index f9bd01a6685..24ddc32ac79 100644 --- a/docs/sources/_index.md +++ b/docs/sources/_index.md @@ -16,13 +16,13 @@ labels: - enterprise - oss menuTitle: Grafana documentation -title: Grafana OSS and Enterprise documentation +title: Grafana OSS and Enterprise hero: - title: Grafana OSS and Enterprise documentation + title: Grafana OSS and Enterprise level: 1 - width: 110 + width: 100 image: /media/docs/grafana-cloud/infrastructure/grafanalogo.svg - height: 110 + height: 100 description: Query, visualize, alert on, and explore your metrics, logs, and traces wherever they are stored. cards: title_class: pt-0 lh-1 From 89dd710ca9969e9c4c46825f4bb7dba674de0322 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Tue, 11 Jun 2024 16:28:47 +0100 Subject: [PATCH 06/15] Chore: Add extra check in keybinds e2e test to prevent flake (#89034) add extra check --- e2e/various-suite/keybinds.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/e2e/various-suite/keybinds.spec.ts b/e2e/various-suite/keybinds.spec.ts index d054edad0b7..07635c4df74 100644 --- a/e2e/various-suite/keybinds.spec.ts +++ b/e2e/various-suite/keybinds.spec.ts @@ -33,10 +33,12 @@ describe('Keyboard shortcuts', () => { zone: 'Browser', }); e2e.components.RefreshPicker.runButtonV2().should('have.text', 'Run query'); + let expectedRange = `Time range selected: 2024-06-05 10:05:00 to 2024-06-05 10:06:00`; + e2e.components.TimePicker.openButton().should('have.attr', 'aria-label', expectedRange); cy.get('body').type('{ctrl}z'); e2e.components.RefreshPicker.runButtonV2().should('have.text', 'Run query'); - let expectedRange = `Time range selected: 2024-06-05 10:03:30 to 2024-06-05 10:07:30`; + expectedRange = `Time range selected: 2024-06-05 10:03:30 to 2024-06-05 10:07:30`; e2e.components.TimePicker.openButton().should('have.attr', 'aria-label', expectedRange); }); @@ -51,11 +53,13 @@ describe('Keyboard shortcuts', () => { zone: 'Browser', }); e2e.components.RefreshPicker.runButtonV2().should('have.text', 'Run query'); + let expectedRange = `Time range selected: 2024-06-05 10:05:00 to 2024-06-05 10:06:00`; + e2e.components.TimePicker.openButton().should('have.attr', 'aria-label', expectedRange); cy.log('Trying one shift-left'); cy.get('body').type('t{leftarrow}'); e2e.components.RefreshPicker.runButtonV2().should('have.text', 'Run query'); - let expectedRange = `Time range selected: 2024-06-05 10:04:00 to 2024-06-05 10:05:00`; // 1 min back + expectedRange = `Time range selected: 2024-06-05 10:04:00 to 2024-06-05 10:05:00`; // 1 min back e2e.components.TimePicker.openButton().should('have.attr', 'aria-label', expectedRange); cy.log('Trying two shift-lefts'); From 45c601acc423e290191c02b9b43f25366b3dcd11 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 11 Jun 2024 17:34:58 +0200 Subject: [PATCH 07/15] Grafana live: Add tests and missing code to amendTable (#89031) * Grafana live: Add tests and missing code to amendTable * Betterer --- .betterer.results | 3 +- .../live/data/amendTimeSeries.test.ts | 123 ++++++++++++++++++ .../app/features/live/data/amendTimeSeries.ts | 12 ++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 public/app/features/live/data/amendTimeSeries.test.ts diff --git a/.betterer.results b/.betterer.results index 98c6cf83ceb..9d4fbfb1720 100644 --- a/.betterer.results +++ b/.betterer.results @@ -4653,7 +4653,8 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Do not use any type assertions.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"] + [0, 0, 0, "Do not use any type assertions.", "4"], + [0, 0, 0, "Do not use any type assertions.", "5"] ], "public/app/features/logs/components/InfiniteScroll.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] diff --git a/public/app/features/live/data/amendTimeSeries.test.ts b/public/app/features/live/data/amendTimeSeries.test.ts new file mode 100644 index 00000000000..fce39e371da --- /dev/null +++ b/public/app/features/live/data/amendTimeSeries.test.ts @@ -0,0 +1,123 @@ +import { Table, amendTable } from "./amendTimeSeries"; + +describe('amendTable', () => { + it('should append nextTable when there is no overlap (nextTable after prevTable)', () => { + const prevTable: Table = [ + [1, 2, 5], + ['a', 'b', 'e'], + ]; + const nextTable: Table = [ + [6, 7, 8], + ['f', 'g', 'h'], + ]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual([ + [1, 2, 5, 6, 7, 8], + ['a', 'b', 'e', 'f', 'g', 'h'], + ]); + }); + + it('should prepend nextTable when there is no overlap (nextTable before prevTable)', () => { + const prevTable: Table = [ + [3, 4, 5], + ['c', 'd', 'e'], + ]; + const nextTable: Table = [ + [1, 2], + ['a', 'b'], + ]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual([ + [1, 2, 3, 4, 5], + ['a', 'b', 'c', 'd', 'e'], + ]); + }); + + it('should fully replace prevTable when nextTable covers entire range', () => { + const prevTable: Table = [ + [3, 4, 5], + ['c', 'd', 'e'], + ]; + const nextTable: Table = [ + [1, 2, 3, 4, 5, 6], + ['a', 'b', 'c', 'd', 'e', 'f'], + ]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual(nextTable); + }); + + it('should partially replace prevTable when nextTable is within range', () => { + const prevTable: Table = [ + [1, 2, 3, 4, 5], + ['a', 'b', 'c', 'd', 'e'], + ]; + const nextTable: Table = [ + [3, 4], + ['x', 'y'], + ]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual([ + [1, 2, 3, 4, 5], + ['a', 'b', 'x', 'y', 'e'], + ]); + }); + + it('should append nextTable with overlap', () => { + const prevTable: Table = [ + [1, 2, 5], + ['a', 'b', 'e'], + ]; + const nextTable: Table = [ + [2, 3, 6], + ['b', 'c', 'f'], + ]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual([ + [1, 2, 3, 6], + ['a', 'b', 'c', 'f'], + ]); + }); + + it('should prepend nextTable with overlap', () => { + const prevTable: Table = [ + [3, 4, 5], + ['c', 'd', 'e'], + ]; + const nextTable: Table = [ + [1, 4], + ['a', 'd'], + ]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual([ + [1, 4, 5], + ['a', 'd', 'e'], + ]); + }); + + it('should handle empty prevTable', () => { + const prevTable: Table = [[]]; + const nextTable: Table = [ + [1, 2, 3], + ['a', 'b', 'c'], + ]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual(nextTable); + }); + + it('should handle empty nextTable', () => { + const prevTable: Table = [ + [1, 2, 3], + ['a', 'b', 'c'], + ]; + const nextTable: Table = [[]]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual(prevTable); + }); + + it('should handle both tables being empty', () => { + const prevTable: Table = [[]]; + const nextTable: Table = [[]]; + const result = amendTable(prevTable, nextTable); + expect(result).toEqual([[]]); + }); +}); diff --git a/public/app/features/live/data/amendTimeSeries.ts b/public/app/features/live/data/amendTimeSeries.ts index ead006ef89b..bdc6c2e13dc 100644 --- a/public/app/features/live/data/amendTimeSeries.ts +++ b/public/app/features/live/data/amendTimeSeries.ts @@ -35,6 +35,18 @@ export function amendTable(prevTable: Table, nextTable: Table): Table { } // partial replace else if (nStart > pStart && nEnd < pEnd) { + // partial replace + let startIdx = closestIdx(nStart, prevTimes); + startIdx = prevTimes[startIdx] < nStart ? startIdx + 1 : startIdx; + let endIdx = closestIdx(nEnd, prevTimes); + endIdx = prevTimes[endIdx] > nEnd ? endIdx - 1 : endIdx; + + outTable = prevTable.map((_, i) => + prevTable[i] + .slice(0, startIdx) + .concat(nextTable[i]) + .concat(prevTable[i].slice(endIdx + 1)) + ) as Table; } // append, with overlap else if (nStart >= pStart) { From d004f8a98de3d714ebb6628e4179ab827a8f3d0c Mon Sep 17 00:00:00 2001 From: Alexander Weaver Date: Tue, 11 Jun 2024 10:37:10 -0500 Subject: [PATCH 08/15] Alerting: Recording rules understands errors embedded in dataframes (#88946) * Make MakeDependencyError public for tests in another package * Create tests for errors in eval results * Extract logic to pull frame errors out into exported function * Maybe we can drop cyclomatic complexity lint suppression now? * extract frame errors and fail recording rules if frames contain error * Fix up retry logic to actually work * Do not retry non retryable errors --- pkg/expr/errors.go | 2 +- pkg/expr/graph.go | 2 +- pkg/services/ngalert/eval/eval.go | 74 ++++++++++------- pkg/services/ngalert/eval/eval_test.go | 79 +++++++++++++++++++ .../ngalert/schedule/recording_rule.go | 62 +++++++++------ 5 files changed, 165 insertions(+), 54 deletions(-) diff --git a/pkg/expr/errors.go b/pkg/expr/errors.go index 63c216fb735..3f162b7026c 100644 --- a/pkg/expr/errors.go +++ b/pkg/expr/errors.go @@ -63,7 +63,7 @@ var DependencyError = errutil.NewBase( depErrStr, errutil.WithPublic(depErrStr)) -func makeDependencyError(refID, depRefID string) error { +func MakeDependencyError(refID, depRefID string) error { data := errutil.TemplateData{ Public: map[string]interface{}{ "refId": refID, diff --git a/pkg/expr/graph.go b/pkg/expr/graph.go index d3d25e8e13a..369ff116a94 100644 --- a/pkg/expr/graph.go +++ b/pkg/expr/graph.go @@ -90,7 +90,7 @@ func (dp *DataPipeline) execute(c context.Context, now time.Time, s *Service) (m if res, ok := vars[neededVar]; ok { if res.Error != nil { errResult := mathexp.Results{ - Error: makeDependencyError(node.RefID(), neededVar), + Error: MakeDependencyError(node.RefID(), neededVar), } vars[node.RefID()] = errResult hasDepError = true diff --git a/pkg/services/ngalert/eval/eval.go b/pkg/services/ngalert/eval/eval.go index 896e4c1c1b8..b22b4e3fa16 100644 --- a/pkg/services/ngalert/eval/eval.go +++ b/pkg/services/ngalert/eval/eval.go @@ -141,9 +141,6 @@ type ExecutionResults struct { // Results contains the results of all queries, reduce and math expressions Results map[string]data.Frames - // Errors contains a map of RefIDs that returned an error - Errors map[string]error - // NoData contains the DatasourceUID for RefIDs that returned no data. NoData map[string]string @@ -166,16 +163,12 @@ func (evalResults Results) HasErrors() bool { // HasNonRetryableErrors returns true if we have at least 1 result with: // 1. A `State` of `Error` // 2. The `Error` attribute is not nil -// 3. The `Error` type is of `&invalidEvalResultFormatError` or `ErrSeriesMustBeWide` +// 3. The `Error` matches IsNonRetryableError // Our thinking with this approach, is that we don't want to retry errors that have relation with invalid alert definition format. func (evalResults Results) HasNonRetryableErrors() bool { for _, r := range evalResults { if r.State == Error && r.Error != nil { - var nonRetryableError *invalidEvalResultFormatError - if errors.As(r.Error, &nonRetryableError) { - return true - } - if errors.Is(r.Error, expr.ErrSeriesMustBeWide) { + if IsNonRetryableError(r.Error) { return true } } @@ -183,6 +176,19 @@ func (evalResults Results) HasNonRetryableErrors() bool { return false } +// IsNonRetryableError indicates whether an error is considered persistent and not worth performing evaluation retries. +// Currently it is true if err is `&invalidEvalResultFormatError` or `ErrSeriesMustBeWide` +func IsNonRetryableError(err error) bool { + var nonRetryableError *invalidEvalResultFormatError + if errors.As(err, &nonRetryableError) { + return true + } + if errors.Is(err, expr.ErrSeriesMustBeWide) { + return true + } + return false +} + // HasErrors returns true when Results contains at least one element and all elements are errors func (evalResults Results) IsError() bool { for _, r := range evalResults { @@ -407,7 +413,6 @@ type NumberValueCapture struct { Value *float64 } -//nolint:gocyclo func queryDataResponseToExecutionResults(c models.Condition, execResp *backend.QueryDataResponse) ExecutionResults { // captures contains the values of all instant queries and expressions for each dimension captures := make(map[string]map[data.Fingerprint]NumberValueCapture) @@ -433,17 +438,10 @@ func queryDataResponseToExecutionResults(c models.Condition, execResp *backend.Q } result := ExecutionResults{Results: make(map[string]data.Frames)} - for refID, res := range execResp.Responses { - if res.Error != nil { - if result.Errors == nil { - result.Errors = make(map[string]error) - } - result.Errors[refID] = res.Error - if refID == c.Condition { - result.Error = res.Error - } - } + result.Error = FindConditionError(execResp, c.Condition) + + for refID, res := range execResp.Responses { // There are two possible frame formats for No Data: // // 1. A response with no frames @@ -526,30 +524,50 @@ func queryDataResponseToExecutionResults(c models.Condition, execResp *backend.Q } } + return result +} + +// FindConditionError extracts the error from a query response that caused the given condition to fail. +// If a condition failed because a node it depends on had an error, that error is returned instead. +// It returns nil if there are no errors related to the condition. +func FindConditionError(resp *backend.QueryDataResponse, condition string) error { + if resp == nil { + return nil + } + + errs := make(map[string]error) + for refID, node := range resp.Responses { + if node.Error != nil { + errs[refID] = node.Error + } + } + + conditionErr := errs[condition] + // If the error of the condition is an Error that indicates the condition failed // because one of its dependent query or expressions failed, then we follow // the dependency chain to an error that is not a dependency error. - if len(result.Errors) > 0 && result.Error != nil { - if errors.Is(result.Error, expr.DependencyError) { + if conditionErr != nil { + if errors.Is(conditionErr, expr.DependencyError) { var utilError errutil.Error - e := result.Error + e := conditionErr for { errors.As(e, &utilError) depRefID := utilError.PublicPayload["depRefId"].(string) - depError, ok := result.Errors[depRefID] + depError, ok := errs[depRefID] if !ok { - return result + return conditionErr } if !errors.Is(depError, expr.DependencyError) { - result.Error = depError - return result + conditionErr = depError + return conditionErr } e = depError } } } - return result + return conditionErr } // datasourceUIDsToRefIDs returns a sorted slice of Ref IDs for each Datasource UID. diff --git a/pkg/services/ngalert/eval/eval_test.go b/pkg/services/ngalert/eval/eval_test.go index 8888235fb72..8f0c0e473ad 100644 --- a/pkg/services/ngalert/eval/eval_test.go +++ b/pkg/services/ngalert/eval/eval_test.go @@ -894,6 +894,85 @@ func TestEvaluate(t *testing.T) { }, EvaluationString: "[ var='A' labels={foo=bar} value=10 ], [ var='B' labels={bar=baz, foo=bar} value=1 ]", }}, + }, { + name: "results contains error if condition frame has error", + cond: models.Condition{ + Condition: "B", + }, + resp: backend.QueryDataResponse{ + Responses: backend.Responses{ + "A": { + Frames: []*data.Frame{{ + RefID: "A", + Fields: []*data.Field{ + data.NewField( + "Value", + data.Labels{"foo": "bar"}, + []*float64{util.Pointer(10.0)}, + ), + }, + }}, + }, + "B": { + Frames: []*data.Frame{{ + RefID: "B", + Fields: []*data.Field{ + data.NewField( + "Value", + data.Labels{"foo": "bar", "bar": "baz"}, + []*float64{util.Pointer(1.0)}, + ), + }, + }}, + Error: errors.New("some frame error"), + }, + }, + }, + expected: Results{{ + State: Error, + Error: errors.New("some frame error"), + EvaluationString: "", + }}, + }, { + name: "results contain underlying error if condition frame has error that depends on another node", + cond: models.Condition{ + Condition: "B", + }, + resp: backend.QueryDataResponse{ + Responses: backend.Responses{ + "A": { + Frames: []*data.Frame{{ + RefID: "A", + Fields: []*data.Field{ + data.NewField( + "Value", + data.Labels{"foo": "bar"}, + []*float64{util.Pointer(10.0)}, + ), + }, + }}, + Error: errors.New("another error depends on me"), + }, + "B": { + Frames: []*data.Frame{{ + RefID: "B", + Fields: []*data.Field{ + data.NewField( + "Value", + data.Labels{"foo": "bar", "bar": "baz"}, + []*float64{util.Pointer(1.0)}, + ), + }, + }}, + Error: expr.MakeDependencyError("B", "A"), + }, + }, + }, + expected: Results{{ + State: Error, + Error: errors.New("another error depends on me"), + EvaluationString: "", + }}, }} for _, tc := range cases { diff --git a/pkg/services/ngalert/schedule/recording_rule.go b/pkg/services/ngalert/schedule/recording_rule.go index 01f4824e778..b84a98b6314 100644 --- a/pkg/services/ngalert/schedule/recording_rule.go +++ b/pkg/services/ngalert/schedule/recording_rule.go @@ -116,7 +116,10 @@ func (r *recordingRule) doEvaluate(ctx context.Context, ev *Evaluation) { logger := r.logger.FromContext(ctx).New("now", ev.scheduledAt, "fingerprint", ev.Fingerprint()) orgID := fmt.Sprint(ev.rule.OrgID) evalDuration := r.metrics.EvalDuration.WithLabelValues(orgID) + evalAttemptTotal := r.metrics.EvalAttemptTotal.WithLabelValues(orgID) + evalAttemptFailures := r.metrics.EvalAttemptFailures.WithLabelValues(orgID) evalTotal := r.metrics.EvalTotal.WithLabelValues(orgID) + evalTotalFailures := r.metrics.EvalFailures.WithLabelValues(orgID) evalStart := r.clock.Now() defer func() { @@ -139,6 +142,7 @@ func (r *recordingRule) doEvaluate(ctx context.Context, ev *Evaluation) { )) defer span.End() + var latestError error for attempt := int64(1); attempt <= r.maxAttempts; attempt++ { logger := logger.New("attempt", attempt) if ctx.Err() != nil { @@ -147,49 +151,59 @@ func (r *recordingRule) doEvaluate(ctx context.Context, ev *Evaluation) { return } + evalAttemptTotal.Inc() err := r.tryEvaluation(ctx, ev, logger) + latestError = err if err == nil { - return + break } logger.Error("Failed to evaluate rule", "attempt", attempt, "error", err) - select { - case <-ctx.Done(): - logger.Error("Context has been cancelled while backing off", "attempt", attempt) - return - case <-time.After(retryDelay): - continue + evalAttemptFailures.Inc() + + if eval.IsNonRetryableError(err) { + break } + + if attempt < r.maxAttempts { + select { + case <-ctx.Done(): + logger.Error("Context has been cancelled while backing off", "attempt", attempt) + return + case <-time.After(retryDelay): + continue + } + } + } + + if latestError != nil { + evalTotalFailures.Inc() + span.SetStatus(codes.Error, "rule evaluation failed") + span.RecordError(latestError) + if r.maxAttempts > 0 { + logger.Error("Recording rule evaluation failed after all attempts", "lastError", latestError) + } + } else { + logger.Debug("Recording rule evaluation succeeded") } } func (r *recordingRule) tryEvaluation(ctx context.Context, ev *Evaluation, logger log.Logger) error { - orgID := fmt.Sprint(ev.rule.OrgID) - evalAttemptTotal := r.metrics.EvalAttemptTotal.WithLabelValues(orgID) - evalAttemptFailures := r.metrics.EvalAttemptFailures.WithLabelValues(orgID) - evalTotalFailures := r.metrics.EvalFailures.WithLabelValues(orgID) - evalStart := r.clock.Now() evalCtx := eval.NewContext(ctx, SchedulerUserFor(ev.rule.OrgID)) result, err := r.buildAndExecutePipeline(ctx, evalCtx, ev, logger) evalDur := r.clock.Now().Sub(evalStart) - - evalAttemptTotal.Inc() - span := trace.SpanFromContext(ctx) - - // TODO: In some cases, err can be nil but the dataframe itself contains embedded error frames. Parse these out like we do when evaluating alert rules. - // TODO: (Maybe, refactor something in eval package so we can use shared code for this) if err != nil { - evalAttemptFailures.Inc() - // TODO: Only errors embedded in the frame can be considered retryable. - // TODO: Since we are not handling these yet per the above TODO, we can blindly consider all errors to be non-retryable for now, and just exit. - evalTotalFailures.Inc() - span.SetStatus(codes.Error, "rule evaluation failed") - span.RecordError(err) return fmt.Errorf("server side expressions pipeline returned an error: %w", err) } + // There might be errors in the pipeline results, even if the query succeeded. + if err := eval.FindConditionError(result, ev.rule.Record.From); err != nil { + return fmt.Errorf("the query failed with an error: %w", err) + } + logger.Info("Recording rule evaluated", "results", result, "duration", evalDur) + span := trace.SpanFromContext(ctx) span.AddEvent("rule evaluated", trace.WithAttributes( attribute.Int64("results", int64(len(result.Responses))), )) From 667fea6623d733d0a7be7a4ce34e96d9d5124b09 Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Tue, 11 Jun 2024 18:34:58 +0200 Subject: [PATCH 09/15] Alerting: use hash of labels instead of labels string as the alert state cache key (#88956) * Alerting: use hash instead of labels as the cache key * Use data.Labels.Fingerprint to calculate the cache key --- pkg/services/ngalert/backtesting/engine.go | 2 +- .../ngalert/backtesting/engine_test.go | 17 ++++--- .../ngalert/models/instance_labels.go | 4 ++ .../ngalert/models/instance_labels_test.go | 14 ++++++ .../ngalert/schedule/alert_rule_test.go | 2 +- pkg/services/ngalert/state/cache.go | 16 +++---- pkg/services/ngalert/state/manager.go | 10 ++-- .../ngalert/state/manager_private_test.go | 38 ++++++--------- pkg/services/ngalert/state/manager_test.go | 46 +++++++------------ pkg/services/ngalert/state/persister_sync.go | 2 +- pkg/services/ngalert/state/state.go | 2 +- 11 files changed, 72 insertions(+), 81 deletions(-) diff --git a/pkg/services/ngalert/backtesting/engine.go b/pkg/services/ngalert/backtesting/engine.go index 30d4c8135b9..2a53ad61582 100644 --- a/pkg/services/ngalert/backtesting/engine.go +++ b/pkg/services/ngalert/backtesting/engine.go @@ -90,7 +90,7 @@ func (e *Engine) Test(ctx context.Context, user identity.Requester, rule *models start := time.Now() tsField := data.NewField("Time", nil, make([]time.Time, length)) - valueFields := make(map[string]*data.Field) + valueFields := make(map[data.Fingerprint]*data.Field) err = evaluator.Eval(ruleCtx, from, time.Duration(rule.IntervalSeconds)*time.Second, length, func(idx int, currentTime time.Time, results eval.Results) error { if idx >= length { diff --git a/pkg/services/ngalert/backtesting/engine_test.go b/pkg/services/ngalert/backtesting/engine_test.go index 26bb317aaad..9b0bf8304f5 100644 --- a/pkg/services/ngalert/backtesting/engine_test.go +++ b/pkg/services/ngalert/backtesting/engine_test.go @@ -160,9 +160,10 @@ func TestNewBacktestingEvaluator(t *testing.T) { func TestEvaluatorTest(t *testing.T) { states := []eval.State{eval.Normal, eval.Alerting, eval.Pending} generateState := func(prefix string) *state.State { + labels := models.GenerateAlertLabels(rand.Intn(5)+1, prefix+"-") return &state.State{ - CacheID: "state-" + prefix, - Labels: models.GenerateAlertLabels(rand.Intn(5)+1, prefix+"-"), + CacheID: labels.Fingerprint(), + Labels: labels, State: states[rand.Intn(len(states))], } } @@ -201,10 +202,11 @@ func TestEvaluatorTest(t *testing.T) { var states []state.StateTransition for _, s := range allStates { + labels := models.GenerateAlertLabels(rand.Intn(5)+1, s.String()+"-") states = append(states, state.StateTransition{ State: &state.State{ - CacheID: "state-" + s.String(), - Labels: models.GenerateAlertLabels(rand.Intn(5)+1, s.String()+"-"), + CacheID: labels.Fingerprint(), + Labels: labels, State: s, StateReason: util.GenerateShortUID(), }, @@ -226,7 +228,7 @@ func TestEvaluatorTest(t *testing.T) { require.Equal(t, data.FieldTypeTime, timestampField.Type()) }) - fieldByState := make(map[string]*data.Field, len(states)) + fieldByState := make(map[data.Fingerprint]*data.Field, len(states)) t.Run("should contain a field per state", func(t *testing.T) { for _, s := range states { @@ -269,11 +271,12 @@ func TestEvaluatorTest(t *testing.T) { from := time.Unix(0, 0) to := from.Add(5 * ruleInterval) + labels := models.GenerateAlertLabels(rand.Intn(5)+1, "test-") states := []state.StateTransition{ { State: &state.State{ - CacheID: "state-1", - Labels: models.GenerateAlertLabels(rand.Intn(5)+1, "test-"), + CacheID: labels.Fingerprint(), + Labels: labels, State: eval.Normal, StateReason: util.GenerateShortUID(), }, diff --git a/pkg/services/ngalert/models/instance_labels.go b/pkg/services/ngalert/models/instance_labels.go index 468ea3da032..1904515cbd9 100644 --- a/pkg/services/ngalert/models/instance_labels.go +++ b/pkg/services/ngalert/models/instance_labels.go @@ -65,6 +65,10 @@ func (il *InstanceLabels) StringAndHash() (string, string, error) { return string(b), fmt.Sprintf("%x", h.Sum(nil)), nil } +func (il *InstanceLabels) Fingerprint() data.Fingerprint { + return data.Labels(*il).Fingerprint() +} + // The following is based on SDK code, copied for now // tupleLables is an alternative representation of Labels (map[string]string) that can be sorted diff --git a/pkg/services/ngalert/models/instance_labels_test.go b/pkg/services/ngalert/models/instance_labels_test.go index 513841798de..e6783438f6d 100644 --- a/pkg/services/ngalert/models/instance_labels_test.go +++ b/pkg/services/ngalert/models/instance_labels_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/require" ) @@ -39,6 +40,19 @@ func TestTupleLabelsToLabels(t *testing.T) { }) } +func TestInstanceLabelsFingerprint(t *testing.T) { + t.Run("returns labels fingerprint", func(t *testing.T) { + labels := InstanceLabels{ + "foo": "bar", + "baz": "qux", + } + + fingerprint := labels.Fingerprint() + expectedFingerprint := data.Labels(labels).Fingerprint() + require.Equal(t, expectedFingerprint, fingerprint) + }) +} + func BenchmarkTupleLabelsToLabels(b *testing.B) { b.Run("10 labels", func(b *testing.B) { in := make(tupleLabels, 0, 10) diff --git a/pkg/services/ngalert/schedule/alert_rule_test.go b/pkg/services/ngalert/schedule/alert_rule_test.go index 49b118e0b75..12eb90ec661 100644 --- a/pkg/services/ngalert/schedule/alert_rule_test.go +++ b/pkg/services/ngalert/schedule/alert_rule_test.go @@ -531,7 +531,7 @@ func TestRuleRoutine(t *testing.T) { for i := 0; i < 2; i++ { states = append(states, &state.State{ AlertRuleUID: rule.UID, - CacheID: util.GenerateShortUID(), + CacheID: data.Labels(rule.Labels).Fingerprint(), OrgID: rule.OrgID, State: s, StartsAt: sch.clock.Now(), diff --git a/pkg/services/ngalert/state/cache.go b/pkg/services/ngalert/state/cache.go index ae82381310e..25e8a083b67 100644 --- a/pkg/services/ngalert/state/cache.go +++ b/pkg/services/ngalert/state/cache.go @@ -20,7 +20,7 @@ import ( ) type ruleStates struct { - states map[string]*State + states map[data.Fingerprint]*State } type cache struct { @@ -89,7 +89,7 @@ func (c *cache) getOrCreate(ctx context.Context, log log.Logger, alertRule *ngMo } var states *ruleStates if states, ok = orgStates[stateCandidate.AlertRuleUID]; !ok { - states = &ruleStates{states: make(map[string]*State)} + states = &ruleStates{states: make(map[data.Fingerprint]*State)} c.states[stateCandidate.OrgID][stateCandidate.AlertRuleUID] = states } return states.getOrAdd(stateCandidate) @@ -199,18 +199,14 @@ func calculateState(ctx context.Context, log log.Logger, alertRule *ngModels.Ale log.Warn("Evaluation result contains either reserved labels or labels declared in the rules. Those labels from the result will be ignored", "labels", dupes) } - il := ngModels.InstanceLabels(lbs) - id, err := il.StringKey() - if err != nil { - log.Error("Error getting cacheId for entry", "error", err) - } + cacheID := lbs.Fingerprint() // For new states, we set StartsAt & EndsAt to EvaluatedAt as this is the // expected value for a Normal state during state transition. newState := State{ AlertRuleUID: alertRule.UID, OrgID: alertRule.OrgID, - CacheID: id, + CacheID: cacheID, Labels: lbs, Annotations: annotations, EvaluationDuration: result.EvaluationDuration, @@ -279,12 +275,12 @@ func (c *cache) set(entry *State) { c.states[entry.OrgID] = make(map[string]*ruleStates) } if _, ok := c.states[entry.OrgID][entry.AlertRuleUID]; !ok { - c.states[entry.OrgID][entry.AlertRuleUID] = &ruleStates{states: make(map[string]*State)} + c.states[entry.OrgID][entry.AlertRuleUID] = &ruleStates{states: make(map[data.Fingerprint]*State)} } c.states[entry.OrgID][entry.AlertRuleUID].states[entry.CacheID] = entry } -func (c *cache) get(orgID int64, alertRuleUID, stateId string) *State { +func (c *cache) get(orgID int64, alertRuleUID string, stateId data.Fingerprint) *State { c.mtxStates.RLock() defer c.mtxStates.RUnlock() ruleStates, ok := c.states[orgID][alertRuleUID] diff --git a/pkg/services/ngalert/state/manager.go b/pkg/services/ngalert/state/manager.go index fd44ef1a27b..10050b60037 100644 --- a/pkg/services/ngalert/state/manager.go +++ b/pkg/services/ngalert/state/manager.go @@ -180,15 +180,12 @@ func (st *Manager) Warm(ctx context.Context, rulesReader RuleReader) { rulesStates, ok := orgStates[entry.RuleUID] if !ok { - rulesStates = &ruleStates{states: make(map[string]*State)} + rulesStates = &ruleStates{states: make(map[data.Fingerprint]*State)} orgStates[entry.RuleUID] = rulesStates } lbs := map[string]string(entry.Labels) - cacheID, err := entry.Labels.StringKey() - if err != nil { - st.log.Error("Error getting cacheId for entry", "error", err) - } + cacheID := entry.Labels.Fingerprint() var resultFp data.Fingerprint if entry.ResultFingerprint != "" { fp, err := strconv.ParseUint(entry.ResultFingerprint, 16, 64) @@ -214,11 +211,12 @@ func (st *Manager) Warm(ctx context.Context, rulesReader RuleReader) { statesCount++ } } + st.cache.setAllStates(states) st.log.Info("State cache has been initialized", "states", statesCount, "duration", time.Since(startTime)) } -func (st *Manager) Get(orgID int64, alertRuleUID, stateId string) *State { +func (st *Manager) Get(orgID int64, alertRuleUID string, stateId data.Fingerprint) *State { return st.cache.get(orgID, alertRuleUID, stateId) } diff --git a/pkg/services/ngalert/state/manager_private_test.go b/pkg/services/ngalert/state/manager_private_test.go index dd284c18391..8740186deac 100644 --- a/pkg/services/ngalert/state/manager_private_test.go +++ b/pkg/services/ngalert/state/manager_private_test.go @@ -276,7 +276,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { if !ok { // skip if nothing to assert continue } - expectedTransitionsMap := make(map[string]StateTransition, len(expectedTransitions)) + expectedTransitionsMap := make(map[data.Fingerprint]StateTransition, len(expectedTransitions)) for i := range expectedTransitions { patchState(alertRule, expectedTransitions[i].State) expectedTransitionsMap[expectedTransitions[i].CacheID] = expectedTransitions[i] @@ -294,7 +294,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { } } if len(expectedTransitionsMap) > 0 { - vals := make([]string, 0, len(expectedTransitionsMap)) + vals := make([]data.Fingerprint, 0, len(expectedTransitionsMap)) for _, s := range expectedTransitionsMap { vals = append(vals, s.CacheID) } @@ -2524,15 +2524,6 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { ngmodels.KeepLastErrState: baseRuleWith(ngmodels.RuleMuts.WithErrorExecAs(ngmodels.KeepLastErrState)), } - cacheID := func(lbls data.Labels) string { - l := ngmodels.InstanceLabels(lbls) - r, err := l.StringKey() - if err != nil { - panic(err) - } - return r - } - type errorTestCase struct { desc string ruleMutators []ngmodels.AlertRuleMutator @@ -2587,7 +2578,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { { PreviousState: eval.Normal, State: &State{ - CacheID: cacheID(labels["system + rule"]), + CacheID: labels["system + rule"].Fingerprint(), Labels: labels["system + rule + datasource-error"], State: eval.Error, Error: datasourceError, @@ -2748,7 +2739,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { { PreviousState: eval.Normal, State: &State{ - CacheID: cacheID(labels["system + rule"]), + CacheID: labels["system + rule"].Fingerprint(), Labels: labels["system + rule + datasource-error"], State: eval.Error, Error: datasourceError, @@ -2881,7 +2872,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { { PreviousState: eval.Normal, State: &State{ - CacheID: cacheID(labels["system + rule"]), + CacheID: labels["system + rule"].Fingerprint(), Labels: labels["system + rule + datasource-error"], State: eval.Error, Error: datasourceError, @@ -3028,7 +3019,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { { PreviousState: eval.Error, State: &State{ - CacheID: cacheID(labels["system + rule"]), + CacheID: labels["system + rule"].Fingerprint(), Labels: labels["system + rule + datasource-error"], Error: datasourceError, State: eval.Normal, @@ -3148,7 +3139,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { { PreviousState: eval.Normal, State: &State{ - CacheID: cacheID(labels["system + rule"]), + CacheID: labels["system + rule"].Fingerprint(), Labels: labels["system + rule + datasource-error"], State: eval.Error, Error: datasourceError, @@ -3244,7 +3235,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { { PreviousState: eval.Pending, State: &State{ - CacheID: cacheID(labels["system + rule"]), + CacheID: labels["system + rule"].Fingerprint(), Labels: labels["system + rule + datasource-error"], State: eval.Error, Error: datasourceError, @@ -3330,7 +3321,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { { PreviousState: eval.Pending, State: &State{ - CacheID: cacheID(labels["system + rule"]), + CacheID: labels["system + rule"].Fingerprint(), Labels: labels["system + rule + datasource-error"], State: eval.Error, Error: datasourceError, @@ -3539,14 +3530,11 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { } func setCacheID(s *State) *State { - if s.CacheID != "" { + if s.CacheID != 0 { return s } - il := ngmodels.InstanceLabels(s.Labels) - id, err := il.StringKey() - if err != nil { - panic(err) - } - s.CacheID = id + + s.CacheID = s.Labels.Fingerprint() + return s } diff --git a/pkg/services/ngalert/state/manager_test.go b/pkg/services/ngalert/state/manager_test.go index cc116a8b8e9..edb836b4dac 100644 --- a/pkg/services/ngalert/state/manager_test.go +++ b/pkg/services/ngalert/state/manager_test.go @@ -973,13 +973,9 @@ func TestProcessEvalResults(t *testing.T) { expectedAnnotations: 1, expectedStates: []*state.State{ { - CacheID: func() string { + CacheID: func() data.Fingerprint { lbls := models.InstanceLabels(labels["system + rule + labels1"]) - r, err := lbls.StringKey() - if err != nil { - panic(err) - } - return r + return lbls.Fingerprint() }(), Labels: mergeLabels(labels["system + rule + labels1"], data.Labels{ "datasource_uid": "datasource_uid_1", @@ -1317,7 +1313,7 @@ func TestProcessEvalResults(t *testing.T) { states := st.GetStatesForRuleUID(tc.alertRule.OrgID, tc.alertRule.UID) assert.Len(t, states, len(tc.expectedStates)) - expectedStates := make(map[string]*state.State, len(tc.expectedStates)) + expectedStates := make(map[data.Fingerprint]*state.State, len(tc.expectedStates)) for _, s := range tc.expectedStates { // patch all optional fields of the expected state setCacheID(s) @@ -1404,12 +1400,11 @@ func TestProcessEvalResults(t *testing.T) { require.NotEmpty(t, states) - savedStates := make(map[string]models.AlertInstance) + savedStates := make(map[data.Fingerprint]models.AlertInstance) for _, op := range instanceStore.RecordedOps() { switch q := op.(type) { case models.AlertInstance: - cacheId, err := q.Labels.StringKey() - require.NoError(t, err) + cacheId := q.Labels.Fingerprint() savedStates[cacheId] = q } } @@ -1578,7 +1573,7 @@ func TestStaleResultsHandler(t *testing.T) { } func TestStaleResults(t *testing.T) { - getCacheID := func(t *testing.T, rule *models.AlertRule, result eval.Result) string { + getCacheID := func(t *testing.T, rule *models.AlertRule, result eval.Result) data.Fingerprint { t.Helper() labels := data.Labels{} for key, value := range rule.Labels { @@ -1588,14 +1583,12 @@ func TestStaleResults(t *testing.T) { labels[key] = value } lbls := models.InstanceLabels(labels) - key, err := lbls.StringKey() - require.NoError(t, err) - return key + return lbls.Fingerprint() } - checkExpectedStates := func(t *testing.T, actual []*state.State, expected map[string]struct{}) map[string]*state.State { + checkExpectedStates := func(t *testing.T, actual []*state.State, expected map[data.Fingerprint]struct{}) map[data.Fingerprint]*state.State { t.Helper() - result := make(map[string]*state.State) + result := make(map[data.Fingerprint]*state.State) require.Len(t, actual, len(expected)) for _, currentState := range actual { _, ok := expected[currentState.CacheID] @@ -1604,7 +1597,7 @@ func TestStaleResults(t *testing.T) { } return result } - checkExpectedStateTransitions := func(t *testing.T, actual []state.StateTransition, expected map[string]struct{}) { + checkExpectedStateTransitions := func(t *testing.T, actual []state.StateTransition, expected map[data.Fingerprint]struct{}) { t.Helper() require.Len(t, actual, len(expected)) for _, currentState := range actual { @@ -1643,7 +1636,7 @@ func TestStaleResults(t *testing.T) { state2 := getCacheID(t, rule, initResults[1]) state3 := getCacheID(t, rule, initResults[2]) - initStates := map[string]struct{}{ + initStates := map[data.Fingerprint]struct{}{ state1: {}, state2: {}, state3: {}, @@ -1687,7 +1680,7 @@ func TestStaleResults(t *testing.T) { t.Run("should remove stale states from cache", func(t *testing.T) { currentStates = st.GetStatesForRuleUID(rule.OrgID, rule.UID) - checkExpectedStates(t, currentStates, map[string]struct{}{ + checkExpectedStates(t, currentStates, map[data.Fingerprint]struct{}{ getCacheID(t, rule, results[0]): {}, }) }) @@ -1782,7 +1775,7 @@ func TestDeleteStateByRuleUID(t *testing.T) { } for _, tc := range testCases { - expectedStatesMap := make(map[string]*state.State, len(tc.expectedStates)) + expectedStatesMap := make(map[data.Fingerprint]*state.State, len(tc.expectedStates)) for _, expectedState := range tc.expectedStates { s := setCacheID(expectedState) expectedStatesMap[s.CacheID] = s @@ -1992,20 +1985,15 @@ func TestResetStateByRuleUID(t *testing.T) { } func setCacheID(s *state.State) *state.State { - if s.CacheID != "" { + if s.CacheID != 0 { return s } - il := models.InstanceLabels(s.Labels) - id, err := il.StringKey() - if err != nil { - panic(err) - } - s.CacheID = id + s.CacheID = s.Labels.Fingerprint() return s } -func stateSliceToMap(states []*state.State) map[string]*state.State { - result := make(map[string]*state.State, len(states)) +func stateSliceToMap(states []*state.State) map[data.Fingerprint]*state.State { + result := make(map[data.Fingerprint]*state.State, len(states)) for _, s := range states { setCacheID(s) result[s.CacheID] = s diff --git a/pkg/services/ngalert/state/persister_sync.go b/pkg/services/ngalert/state/persister_sync.go index bc8c5cf1673..90ddd350498 100644 --- a/pkg/services/ngalert/state/persister_sync.go +++ b/pkg/services/ngalert/state/persister_sync.go @@ -56,7 +56,7 @@ func (a *SyncStatePersister) deleteAlertStates(ctx context.Context, states []Sta for _, s := range states { key, err := s.GetAlertInstanceKey() if err != nil { - a.log.Error("Failed to delete alert instance with invalid labels", "cacheID", s.CacheID, "error", err) + a.log.Error("Failed to delete alert instance with invalid labels", "cacheID", s.CacheID, "labels", s.Labels.String(), "error", err) continue } toDelete = append(toDelete, key) diff --git a/pkg/services/ngalert/state/state.go b/pkg/services/ngalert/state/state.go index cb6cf0767a9..cc349d7ff43 100644 --- a/pkg/services/ngalert/state/state.go +++ b/pkg/services/ngalert/state/state.go @@ -27,7 +27,7 @@ type State struct { // CacheID is a unique, opaque identifier for the state, and is used to find the state // in the state cache. It tends to be derived from the state's labels. - CacheID string + CacheID data.Fingerprint // State represents the current state. State eval.State From 2d370f398356723565b56e7ec0d3137d81d1a67b Mon Sep 17 00:00:00 2001 From: Haris Rozajac <58232930+harisrozajac@users.noreply.github.com> Date: Tue, 11 Jun 2024 11:15:36 -0600 Subject: [PATCH 10/15] Explore: Logs pinning in content outline (#88316) * wip: working version * add delete buttons, put pinned logs on top, * Use already available onPinLine prop * cleanup * Fix alignment of pinned log * Limit to 3 pinned log lines * Format tooltip message * Lower to font size and adjust padding so pinned log title is fully visible * Add internationalization support in Explore Logs * Update json for i18n * Test remove button * Open content outline after pinning a log * Remove console.log statements * Minor changes * Conflict stuff --- .betterer.results | 4 +- .../ContentOutline/ContentOutline.test.tsx | 17 ++++- .../explore/ContentOutline/ContentOutline.tsx | 19 ++++-- .../ContentOutline/ContentOutlineContext.tsx | 45 +++++++++++-- .../ContentOutline/ContentOutlineItem.tsx | 7 ++ .../ContentOutlineItemButton.tsx | 30 +++++++-- public/app/features/explore/Explore.tsx | 3 + public/app/features/explore/Logs/Logs.tsx | 65 ++++++++++++++++++- .../features/explore/Logs/LogsContainer.tsx | 3 + .../app/features/logs/components/LogRow.tsx | 4 +- .../logs/components/LogRowMenuCell.tsx | 7 +- .../logs/components/LogRowMessage.tsx | 4 ++ .../app/features/logs/components/LogRows.tsx | 5 +- public/app/plugins/panel/logs/panelcfg.cue | 18 ++--- public/locales/en-US/grafana.json | 6 ++ public/locales/pseudo-LOCALE/grafana.json | 6 ++ 16 files changed, 207 insertions(+), 36 deletions(-) diff --git a/.betterer.results b/.betterer.results index 9d4fbfb1720..fda1df4c2d5 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3891,9 +3891,7 @@ exports[`better eslint`] = { ], "public/app/features/explore/Logs/Logs.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/explore/Logs/LogsFeedback.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] diff --git a/public/app/features/explore/ContentOutline/ContentOutline.test.tsx b/public/app/features/explore/ContentOutline/ContentOutline.test.tsx index d28093e331a..b79c823859c 100644 --- a/public/app/features/explore/ContentOutline/ContentOutline.test.tsx +++ b/public/app/features/explore/ContentOutline/ContentOutline.test.tsx @@ -11,6 +11,8 @@ jest.mock('./ContentOutlineContext', () => ({ const scrollIntoViewMock = jest.fn(); const scrollerMock = document.createElement('div'); +const unregisterMock = jest.fn(); + const setup = (mergeSingleChild = false) => { HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; @@ -50,6 +52,7 @@ const setup = (mergeSingleChild = false) => { title: 'Item 2-1', ref: document.createElement('div'), level: 'child', + onRemove: () => unregisterMock('item-2-1'), }, { id: 'item-2-2', @@ -62,7 +65,7 @@ const setup = (mergeSingleChild = false) => { }, ], register: jest.fn(), - unregister: jest.fn(), + unregister: unregisterMock, }); return render(); @@ -143,4 +146,16 @@ describe('', () => { await userEvent.click(button); expect(button.getAttribute('aria-controls')).toBe(sectionContent.id); }); + + it('deletes item on delete button click', async () => { + setup(); + const expandSectionChevrons = screen.getAllByRole('button', { name: 'Content outline item collapse button' }); + // chevron for the second item + const button = expandSectionChevrons[1]; + await userEvent.click(button); + const deleteButtons = screen.getAllByTestId('content-outline-item-delete-button'); + await userEvent.click(deleteButtons[0]); + + expect(unregisterMock).toHaveBeenCalledWith('item-2-1'); + }); }); diff --git a/public/app/features/explore/ContentOutline/ContentOutline.tsx b/public/app/features/explore/ContentOutline/ContentOutline.tsx index bcf3bb147da..05d5adda510 100644 --- a/public/app/features/explore/ContentOutline/ContentOutline.tsx +++ b/public/app/features/explore/ContentOutline/ContentOutline.tsx @@ -47,9 +47,11 @@ export function ContentOutline({ scroller, panelId }: { scroller: HTMLElement | (item) => item.children && !(item.mergeSingleChild && item.children?.length === 1) && item.children.length > 0 ); + const outlineItemsHaveDeleteButton = outlineItems.some((item) => item.children?.some((child) => child.onRemove)); + const [sectionsExpanded, setSectionsExpanded] = useState(() => { return outlineItems.reduce((acc: { [key: string]: boolean }, item) => { - acc[item.id] = false; + acc[item.id] = !!item.expanded; return acc; }, {}); }); @@ -58,6 +60,10 @@ export function ContentOutline({ scroller, panelId }: { scroller: HTMLElement | let scrollValue = 0; let el: HTMLElement | null | undefined = ref; + if (!el) { + return; + } + do { scrollValue += el?.offsetTop || 0; el = el?.offsetParent instanceof HTMLElement ? el.offsetParent : undefined; @@ -158,7 +164,7 @@ export function ContentOutline({ scroller, panelId }: { scroller: HTMLElement | title={contentOutlineExpanded ? item.title : undefined} contentOutlineExpanded={contentOutlineExpanded} className={cx(styles.buttonStyles, { - [styles.justifyCenter]: !contentOutlineExpanded, + [styles.justifyCenter]: !contentOutlineExpanded && !outlineItemsHaveDeleteButton, [styles.sectionHighlighter]: isChildActive(item, activeSectionChildId) && !contentOutlineExpanded, })} indentStyle={cx({ @@ -196,7 +202,7 @@ export function ContentOutline({ scroller, panelId }: { scroller: HTMLElement | contentOutlineExpanded={contentOutlineExpanded} icon={contentOutlineExpanded ? undefined : item.icon} className={cx(styles.buttonStyles, { - [styles.justifyCenter]: !contentOutlineExpanded, + [styles.justifyCenter]: !contentOutlineExpanded && !outlineItemsHaveDeleteButton, [styles.sectionHighlighter]: isChildActive(item, activeSectionChildId) && !contentOutlineExpanded, })} @@ -211,6 +217,7 @@ export function ContentOutline({ scroller, panelId }: { scroller: HTMLElement | isActive={shouldBeActive(child, activeSectionId, activeSectionChildId, sectionsExpanded)} extraHighlight={child.highlight} color={child.color} + onRemove={child.onRemove ? () => child.onRemove?.(child.id) : undefined} />
))} @@ -257,10 +264,10 @@ const getStyles = (theme: GrafanaTheme2, expanded: boolean) => { marginRight: expanded ? theme.spacing(0.5) : undefined, }), indentRoot: css({ - paddingLeft: theme.spacing(4), + paddingLeft: theme.spacing(3), }), indentChild: css({ - paddingLeft: expanded ? theme.spacing(7) : theme.spacing(4), + paddingLeft: expanded ? theme.spacing(5) : theme.spacing(2.75), }), itemWrapper: css({ display: 'flex', @@ -275,7 +282,7 @@ const getStyles = (theme: GrafanaTheme2, expanded: boolean) => { borderRight: `1px solid ${theme.colors.border.medium}`, content: '""', height: '100%', - left: 48, + left: theme.spacing(4.75), position: 'absolute', transform: 'translateX(50%)', }, diff --git a/public/app/features/explore/ContentOutline/ContentOutlineContext.tsx b/public/app/features/explore/ContentOutline/ContentOutlineContext.tsx index dc579cc8404..44b2a989a60 100644 --- a/public/app/features/explore/ContentOutline/ContentOutlineContext.tsx +++ b/public/app/features/explore/ContentOutline/ContentOutlineContext.tsx @@ -18,6 +18,7 @@ export interface ContentOutlineContextProps { unregister: (id: string) => void; unregisterAllChildren: (parentId: string, childType: ITEM_TYPES) => void; updateOutlineItems: (newItems: ContentOutlineItemContextProps[]) => void; + updateItem: (id: string, properties: Partial>) => void; } interface ContentOutlineContextProviderProps { @@ -141,8 +142,11 @@ export function ContentOutlineContextProvider({ children, refreshDependencies }: ref = parent.ref; } - const childrenUpdated = [...(parent.children || []), { ...outlineItem, id, ref }]; - childrenUpdated.sort(sortElementsByDocumentPosition); + let childrenUpdated = [{ ...outlineItem, id, ref }, ...(parent.children || [])]; + + if (!outlineItem.childOnTop) { + childrenUpdated = sortItems(childrenUpdated); + } newItems[parentIndex] = { ...parent, @@ -175,6 +179,20 @@ export function ContentOutlineContextProvider({ children, refreshDependencies }: setOutlineItems(newItems); }, []); + const updateItem = useCallback((id: string, properties: Partial>) => { + setOutlineItems((prevItems) => + prevItems.map((item) => { + if (item.id === id) { + return { + ...item, + ...properties, + }; + } + return item; + }) + ); + }, []); + const unregisterAllChildren = useCallback((parentId: string, childType: ITEM_TYPES) => { setOutlineItems((prevItems) => prevItems.map((item) => { @@ -190,7 +208,8 @@ export function ContentOutlineContextProvider({ children, refreshDependencies }: setOutlineItems((prevItems) => { const newItems = [...prevItems]; for (const item of newItems) { - item.children?.sort(sortElementsByDocumentPosition); + const sortedItems = sortItems(item.children || []); + item.children = sortedItems; } return newItems; }); @@ -198,14 +217,14 @@ export function ContentOutlineContextProvider({ children, refreshDependencies }: return ( {children} ); } -export function sortElementsByDocumentPosition(a: ContentOutlineItemContextProps, b: ContentOutlineItemContextProps) { +function sortElementsByDocumentPosition(a: ContentOutlineItemContextProps, b: ContentOutlineItemContextProps) { if (a.ref && b.ref) { const diff = a.ref.compareDocumentPosition(b.ref); if (diff === Node.DOCUMENT_POSITION_PRECEDING) { @@ -217,6 +236,22 @@ export function sortElementsByDocumentPosition(a: ContentOutlineItemContextProps return 0; } +function sortItems(outlineItems: ContentOutlineItemContextProps[]): ContentOutlineItemContextProps[] { + const [skipSort, sortable] = outlineItems.reduce< + [ContentOutlineItemContextProps[], ContentOutlineItemContextProps[]] + >( + (acc, item) => { + item.childOnTop ? acc[0].push(item) : acc[1].push(item); + return acc; + }, + [[], []] + ); + + sortable.sort(sortElementsByDocumentPosition); + + return [...skipSort, ...sortable]; +} + export function useContentOutlineContext() { return useContext(ContentOutlineContext); } diff --git a/public/app/features/explore/ContentOutline/ContentOutlineItem.tsx b/public/app/features/explore/ContentOutline/ContentOutlineItem.tsx index 4b0abe9981e..6db6252ed2a 100644 --- a/public/app/features/explore/ContentOutline/ContentOutlineItem.tsx +++ b/public/app/features/explore/ContentOutline/ContentOutlineItem.tsx @@ -35,6 +35,13 @@ export interface ContentOutlineItemBaseProps { * Client can additionally mark filter actions as highlighted */ highlight?: boolean; + onRemove?: (id: string) => void; + /** + * Child that will always be on top of the list + * e.g. pinned log in Logs section + */ + childOnTop?: boolean; + expanded?: boolean; } interface ContentOutlineItemProps extends ContentOutlineItemBaseProps { diff --git a/public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx b/public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx index 8afd4c2ea17..4b45e2efa79 100644 --- a/public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx +++ b/public/app/features/explore/ContentOutline/ContentOutlineItemButton.tsx @@ -2,7 +2,7 @@ import { cx, css } from '@emotion/css'; import React, { ButtonHTMLAttributes, useEffect, useRef, useState } from 'react'; import { IconName, isIconName, GrafanaTheme2 } from '@grafana/data'; -import { Icon, Tooltip, useTheme2 } from '@grafana/ui'; +import { Button, Icon, Tooltip, useTheme2 } from '@grafana/ui'; import { TooltipPlacement } from '@grafana/ui/src/components/Tooltip'; type CommonProps = { @@ -20,6 +20,7 @@ type CommonProps = { sectionId?: string; toggleCollapsed?: () => void; color?: string; + onRemove?: () => void; }; export type ContentOutlineItemButtonProps = CommonProps & ButtonHTMLAttributes; @@ -39,6 +40,7 @@ export function ContentOutlineItemButton({ sectionId, toggleCollapsed, color, + onRemove, ...rest }: ContentOutlineItemButtonProps) { const theme = useTheme2(); @@ -83,6 +85,15 @@ export function ContentOutlineItemButton({ )} + {onRemove && ( + @@ -964,7 +1023,7 @@ class UnthemedLogs extends PureComponent {
{scanText}
diff --git a/public/app/features/explore/Logs/LogsContainer.tsx b/public/app/features/explore/Logs/LogsContainer.tsx index 528daafa093..aef5c4792e8 100644 --- a/public/app/features/explore/Logs/LogsContainer.tsx +++ b/public/app/features/explore/Logs/LogsContainer.tsx @@ -60,6 +60,7 @@ interface LogsContainerProps extends PropsFromRedux { isFilterLabelActive: (key: string, value: string, refId?: string) => Promise; onClickFilterString: (value: string, refId?: string) => void; onClickFilterOutString: (value: string, refId?: string) => void; + onPinLineCallback?: () => void; } type DataSourceInstance = @@ -282,6 +283,7 @@ class LogsContainer extends PureComponent diff --git a/public/app/features/logs/components/LogRow.tsx b/public/app/features/logs/components/LogRow.tsx index 163b53f2bec..fe161e3111e 100644 --- a/public/app/features/logs/components/LogRow.tsx +++ b/public/app/features/logs/components/LogRow.tsx @@ -15,7 +15,7 @@ import { } from '@grafana/data'; import { reportInteraction } from '@grafana/runtime'; import { DataQuery, TimeZone } from '@grafana/schema'; -import { withTheme2, Themeable2, Icon, Tooltip } from '@grafana/ui'; +import { withTheme2, Themeable2, Icon, Tooltip, PopoverContent } from '@grafana/ui'; import { checkLogsError, escapeUnescapedString } from '../utils'; @@ -60,6 +60,7 @@ interface Props extends Themeable2 { isFilterLabelActive?: (key: string, value: string, refId?: string) => Promise; onPinLine?: (row: LogRowModel) => void; onUnpinLine?: (row: LogRowModel) => void; + pinLineButtonTooltipTitle?: PopoverContent; pinned?: boolean; containerRendered?: boolean; handleTextSelection?: (e: MouseEvent, row: LogRowModel) => boolean; @@ -305,6 +306,7 @@ class UnThemedLogRow extends PureComponent { styles={styles} onPinLine={this.props.onPinLine} onUnpinLine={this.props.onUnpinLine} + pinLineButtonTooltipTitle={this.props.pinLineButtonTooltipTitle} pinned={this.props.pinned} mouseIsOver={this.state.mouseIsOver} onBlur={this.onMouseLeave} diff --git a/public/app/features/logs/components/LogRowMenuCell.tsx b/public/app/features/logs/components/LogRowMenuCell.tsx index 9313a8e8256..756e19813f7 100644 --- a/public/app/features/logs/components/LogRowMenuCell.tsx +++ b/public/app/features/logs/components/LogRowMenuCell.tsx @@ -2,7 +2,7 @@ import React, { FocusEvent, SyntheticEvent, useCallback } from 'react'; import { LogRowContextOptions, LogRowModel, getDefaultTimeRange, locationUtil, urlUtil } from '@grafana/data'; import { DataQuery } from '@grafana/schema'; -import { ClipboardButton, IconButton } from '@grafana/ui'; +import { ClipboardButton, IconButton, PopoverContent } from '@grafana/ui'; import { getConfig } from 'app/core/config'; import { LogRowStyles } from './getLogRowStyles'; @@ -20,10 +20,12 @@ interface Props { onPermalinkClick?: (row: LogRowModel) => Promise; onPinLine?: (row: LogRowModel) => void; onUnpinLine?: (row: LogRowModel) => void; + pinLineButtonTooltipTitle?: PopoverContent; pinned?: boolean; styles: LogRowStyles; mouseIsOver: boolean; onBlur: () => void; + onPinToContentOutlineClick?: (row: LogRowModel, onOpenContext: (row: LogRowModel) => void) => void; } export const LogRowMenuCell = React.memo( @@ -33,6 +35,7 @@ export const LogRowMenuCell = React.memo( onPermalinkClick, onPinLine, onUnpinLine, + pinLineButtonTooltipTitle, pinned, row, showContextToggle, @@ -145,7 +148,7 @@ export const LogRowMenuCell = React.memo( size="md" name="gf-pin" onClick={() => onPinLine && onPinLine(row)} - tooltip="Pin line" + tooltip={pinLineButtonTooltipTitle ?? 'Pin line'} tooltipPlacement="top" aria-label="Pin line" tabIndex={0} diff --git a/public/app/features/logs/components/LogRowMessage.tsx b/public/app/features/logs/components/LogRowMessage.tsx index 841923d3f17..5d99484c1b2 100644 --- a/public/app/features/logs/components/LogRowMessage.tsx +++ b/public/app/features/logs/components/LogRowMessage.tsx @@ -3,6 +3,7 @@ import Highlighter from 'react-highlight-words'; import { CoreApp, findHighlightChunksInText, LogRowContextOptions, LogRowModel } from '@grafana/data'; import { DataQuery } from '@grafana/schema'; +import { PopoverContent } from '@grafana/ui'; import { LogMessageAnsi } from './LogMessageAnsi'; import { LogRowMenuCell } from './LogRowMenuCell'; @@ -25,6 +26,7 @@ interface Props { onPermalinkClick?: (row: LogRowModel) => Promise; onPinLine?: (row: LogRowModel) => void; onUnpinLine?: (row: LogRowModel) => void; + pinLineButtonTooltipTitle?: PopoverContent; pinned?: boolean; styles: LogRowStyles; mouseIsOver: boolean; @@ -88,6 +90,7 @@ export const LogRowMessage = React.memo((props: Props) => { onPermalinkClick, onUnpinLine, onPinLine, + pinLineButtonTooltipTitle, pinned, mouseIsOver, onBlur, @@ -124,6 +127,7 @@ export const LogRowMessage = React.memo((props: Props) => { onPermalinkClick={onPermalinkClick} onPinLine={onPinLine} onUnpinLine={onUnpinLine} + pinLineButtonTooltipTitle={pinLineButtonTooltipTitle} pinned={pinned} styles={styles} mouseIsOver={mouseIsOver} diff --git a/public/app/features/logs/components/LogRows.tsx b/public/app/features/logs/components/LogRows.tsx index 400266137f4..41100a4c634 100644 --- a/public/app/features/logs/components/LogRows.tsx +++ b/public/app/features/logs/components/LogRows.tsx @@ -15,7 +15,7 @@ import { } from '@grafana/data'; import { config } from '@grafana/runtime'; import { DataQuery } from '@grafana/schema'; -import { withTheme2, Themeable2 } from '@grafana/ui'; +import { withTheme2, Themeable2, PopoverContent } from '@grafana/ui'; import { PopoverMenu } from '../../explore/Logs/PopoverMenu'; import { UniqueKeyMaker } from '../UniqueKeyMaker'; @@ -50,6 +50,7 @@ export interface Props extends Themeable2 { onClickHideField?: (key: string) => void; onPinLine?: (row: LogRowModel) => void; onUnpinLine?: (row: LogRowModel) => void; + pinLineButtonTooltipTitle?: PopoverContent; onLogRowHover?: (row?: LogRowModel) => void; onOpenContext?: (row: LogRowModel, onClose: () => void) => void; getRowContextQuery?: ( @@ -238,6 +239,7 @@ class UnThemedLogRows extends PureComponent { permalinkedRowId={this.props.permalinkedRowId} onPinLine={this.props.onPinLine} onUnpinLine={this.props.onUnpinLine} + pinLineButtonTooltipTitle={this.props.pinLineButtonTooltipTitle} pinned={this.props.pinnedRowId === row.uid} isFilterLabelActive={this.props.isFilterLabelActive} handleTextSelection={this.popoverMenuSupported() ? this.handleSelection : undefined} @@ -260,6 +262,7 @@ class UnThemedLogRows extends PureComponent { permalinkedRowId={this.props.permalinkedRowId} onPinLine={this.props.onPinLine} onUnpinLine={this.props.onUnpinLine} + pinLineButtonTooltipTitle={this.props.pinLineButtonTooltipTitle} pinned={this.props.pinnedRowId === row.uid} isFilterLabelActive={this.props.isFilterLabelActive} handleTextSelection={this.popoverMenuSupported() ? this.handleSelection : undefined} diff --git a/public/app/plugins/panel/logs/panelcfg.cue b/public/app/plugins/panel/logs/panelcfg.cue index 481fb99efe6..239de796ceb 100644 --- a/public/app/plugins/panel/logs/panelcfg.cue +++ b/public/app/plugins/panel/logs/panelcfg.cue @@ -26,15 +26,15 @@ composableKinds: PanelCfg: { version: [0, 0] schema: { Options: { - showLabels: bool - showCommonLabels: bool - showTime: bool - showLogContextToggle: bool - wrapLogMessage: bool - prettifyLogMessage: bool - enableLogDetails: bool - sortOrder: common.LogsSortOrder - dedupStrategy: common.LogsDedupStrategy + showLabels: bool + showCommonLabels: bool + showTime: bool + showLogContextToggle: bool + wrapLogMessage: bool + prettifyLogMessage: bool + enableLogDetails: bool + sortOrder: common.LogsSortOrder + dedupStrategy: common.LogsDedupStrategy // TODO: figure out how to define callbacks onClickFilterLabel?: _ onClickFilterOutLabel?: _ diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index dcacf1d1098..66793ded596 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -497,6 +497,12 @@ "title": "Add query to Query Library", "visibility": "Visibility" }, + "logs": { + "maximum-pinned-logs": "Maximum of {{PINNED_LOGS_LIMIT}} pinned logs reached. Unpin a log to add another.", + "no-logs-found": "No logs found.", + "scan-for-older-logs": "Scan for older logs", + "stop-scan": "Stop scan" + }, "rich-history": { "close-tooltip": "Close query history", "datasource-a-z": "Data source A-Z", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 2bb814bc727..f0455ab7fb1 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -497,6 +497,12 @@ "title": "Åđđ qūęřy ŧő Qūęřy Ŀįþřäřy", "visibility": "Vįşįþįľįŧy" }, + "logs": { + "maximum-pinned-logs": "Mäχįmūm őƒ {{PINNED_LOGS_LIMIT}} pįʼnʼnęđ ľőģş řęäčĥęđ. Ůʼnpįʼn ä ľőģ ŧő äđđ äʼnőŧĥęř.", + "no-logs-found": "Ńő ľőģş ƒőūʼnđ.", + "scan-for-older-logs": "Ŝčäʼn ƒőř őľđęř ľőģş", + "stop-scan": "Ŝŧőp şčäʼn" + }, "rich-history": { "close-tooltip": "Cľőşę qūęřy ĥįşŧőřy", "datasource-a-z": "Đäŧä şőūřčę Å-Ż", From eb76ea47a0f392e510e7cc213bcf25dc1fe07195 Mon Sep 17 00:00:00 2001 From: Jacob Valdemar Date: Tue, 11 Jun 2024 19:25:48 +0200 Subject: [PATCH 11/15] Alerting: Add ha_reconnect_timeout configuration option (#88823) * Docs: Update "Configure high availability" guide with ha_reconnect_timeout configuration --------- Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> --- conf/defaults.ini | 4 ++++ conf/sample.ini | 4 ++++ .../alerting/set-up/configure-high-availability/_index.md | 1 + docs/sources/setup-grafana/configure-grafana/_index.md | 6 ++++++ pkg/services/ngalert/notifier/multiorg_alertmanager.go | 2 +- pkg/setting/setting_unified_alerting.go | 6 ++++++ pkg/setting/setting_unified_alerting_test.go | 1 + 7 files changed, 23 insertions(+), 1 deletion(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 20449cc273a..966edbf4afc 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1265,6 +1265,10 @@ ha_label = # The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. ha_gossip_interval = 200ms +# Length of time to attempt to reconnect to a lost peer. Recommended to be short (<15m) when Grafana is running in a Kubernetes cluster. +# The string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. +ha_reconnect_timeout = 6h + # The interval between gossip full state syncs. Setting this interval lower (more frequent) will increase convergence speeds # across larger clusters at the expense of increased bandwidth usage. # The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. diff --git a/conf/sample.ini b/conf/sample.ini index 23478c1ad97..40a3279a378 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1251,6 +1251,10 @@ # The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. ;ha_gossip_interval = "200ms" +# Length of time to attempt to reconnect to a lost peer. Recommended to be short (<15m) when Grafana is running in a Kubernetes cluster. +# The string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. +;ha_reconnect_timeout = 6h + # The interval between gossip full state syncs. Setting this interval lower (more frequent) will increase convergence speeds # across larger clusters at the expense of increased bandwidth usage. # The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. diff --git a/docs/sources/alerting/set-up/configure-high-availability/_index.md b/docs/sources/alerting/set-up/configure-high-availability/_index.md index 539b4c1fc9b..a706223479e 100644 --- a/docs/sources/alerting/set-up/configure-high-availability/_index.md +++ b/docs/sources/alerting/set-up/configure-high-availability/_index.md @@ -147,4 +147,5 @@ The following metrics can be used for meta monitoring, exposed by the `/metrics` ha_peers = "grafana-alerting.grafana:9094" ha_advertise_address = "${POD_IP}:9094" ha_peer_timeout = 15s + ha_reconnect_timeout = 2m ``` diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 64c42453589..1527fd91830 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1635,6 +1635,12 @@ across cluster more quickly at the expense of increased bandwidth usage. The def The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. +### ha_reconnect_timeout + +Length of time to attempt to reconnect to a lost peer. When running Grafana in a Kubernetes cluster, set this duration to less than `15m`. + +The string is a possibly signed sequence of decimal numbers followed by a unit suffix (ms, s, m, h, d), such as `30s` or `1m`. + ### ha_push_pull_interval The interval between gossip full state syncs. Setting this interval lower (more frequent) will increase convergence speeds diff --git a/pkg/services/ngalert/notifier/multiorg_alertmanager.go b/pkg/services/ngalert/notifier/multiorg_alertmanager.go index 4f70a3b57d9..4dbc1ad8602 100644 --- a/pkg/services/ngalert/notifier/multiorg_alertmanager.go +++ b/pkg/services/ngalert/notifier/multiorg_alertmanager.go @@ -215,7 +215,7 @@ func (moa *MultiOrgAlertmanager) setupClustering(cfg *setting.Cfg) error { return fmt.Errorf("unable to initialize gossip mesh: %w", err) } - err = peer.Join(alertingCluster.DefaultReconnectInterval, alertingCluster.DefaultReconnectTimeout) + err = peer.Join(alertingCluster.DefaultReconnectInterval, cfg.UnifiedAlerting.HAReconnectTimeout) if err != nil { moa.logger.Error("Msg", "Unable to join gossip mesh while initializing cluster for high availability mode", "error", err) } diff --git a/pkg/setting/setting_unified_alerting.go b/pkg/setting/setting_unified_alerting.go index 228fb049dc1..6536bd442f8 100644 --- a/pkg/setting/setting_unified_alerting.go +++ b/pkg/setting/setting_unified_alerting.go @@ -18,6 +18,7 @@ const ( alertmanagerDefaultClusterAddr = "0.0.0.0:9094" alertmanagerDefaultPeerTimeout = 15 * time.Second alertmanagerDefaultGossipInterval = alertingCluster.DefaultGossipInterval + alertmanagerDefaultReconnectTimeout = alertingCluster.DefaultReconnectTimeout alertmanagerDefaultPushPullInterval = alertingCluster.DefaultPushPullInterval alertmanagerDefaultConfigPollInterval = time.Minute alertmanagerRedisDefaultMaxConns = 5 @@ -71,6 +72,7 @@ type UnifiedAlertingSettings struct { HAPeers []string HAPeerTimeout time.Duration HAGossipInterval time.Duration + HAReconnectTimeout time.Duration HAPushPullInterval time.Duration HALabel string HARedisClusterModeEnabled bool @@ -217,6 +219,10 @@ func (cfg *Cfg) ReadUnifiedAlertingSettings(iniFile *ini.File) error { if err != nil { return err } + uaCfg.HAReconnectTimeout, err = gtime.ParseDuration(valueAsString(ua, "ha_reconnect_timeout", (alertmanagerDefaultReconnectTimeout).String())) + if err != nil { + return err + } uaCfg.HAPushPullInterval, err = gtime.ParseDuration(valueAsString(ua, "ha_push_pull_interval", (alertmanagerDefaultPushPullInterval).String())) if err != nil { return err diff --git a/pkg/setting/setting_unified_alerting_test.go b/pkg/setting/setting_unified_alerting_test.go index 4f4a540b401..e60dedf8fb6 100644 --- a/pkg/setting/setting_unified_alerting_test.go +++ b/pkg/setting/setting_unified_alerting_test.go @@ -25,6 +25,7 @@ func TestCfg_ReadUnifiedAlertingSettings(t *testing.T) { require.Len(t, cfg.UnifiedAlerting.HAPeers, 0) require.Equal(t, 200*time.Millisecond, cfg.UnifiedAlerting.HAGossipInterval) require.Equal(t, time.Minute, cfg.UnifiedAlerting.HAPushPullInterval) + require.Equal(t, 6*time.Hour, cfg.UnifiedAlerting.HAReconnectTimeout) } // With peers set, it correctly parses them. From a559a66544f4bc470b087bed69352e0a9cd99b8c Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Tue, 11 Jun 2024 21:23:12 +0300 Subject: [PATCH 12/15] FeatureFlags: Update creation timestamps (#88966) --- pkg/services/featuremgmt/toggles-gitlog.csv | 332 ++ pkg/services/featuremgmt/toggles_gen.json | 3794 +++++++++--------- pkg/services/featuremgmt/toggles_gen_test.go | 52 + 3 files changed, 2282 insertions(+), 1896 deletions(-) create mode 100644 pkg/services/featuremgmt/toggles-gitlog.csv diff --git a/pkg/services/featuremgmt/toggles-gitlog.csv b/pkg/services/featuremgmt/toggles-gitlog.csv new file mode 100644 index 00000000000..d2ffa07d38f --- /dev/null +++ b/pkg/services/featuremgmt/toggles-gitlog.csv @@ -0,0 +1,332 @@ +#name,created,deleted,hash,author +newNavigation,2022-01-26T17:44:20Z,2022-06-16T09:48:38Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +trimDefaults,2022-01-26T17:44:20Z,2023-11-02T15:35:14Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +dashboardPreviews,2022-01-26T17:44:20Z,2023-04-13T17:42:24Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +live-config,2022-01-26T17:44:20Z,2023-02-03T21:21:48Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +accesscontrol,2022-01-26T17:44:20Z,2022-05-16T10:45:41Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +tempoServiceGraph,2022-01-26T17:44:20Z,2022-07-19T07:00:58Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +httpclientprovider_azure_auth,2022-01-26T17:44:20Z,2022-05-30T15:43:32Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +tempoBackendSearch,2022-01-26T17:44:20Z,2022-06-01T17:32:10Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +showFeatureFlagsInUI,2022-01-26T17:44:20Z,2023-02-09T00:01:34Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +queryOverLive,2022-01-26T17:44:20Z,,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +validatedQueries,2022-01-26T17:44:20Z,2022-05-16T21:17:05Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +fullRangeLogsVolume,2022-01-26T17:44:20Z,2022-02-15T08:05:03Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +prometheus_azure_auth,2022-01-26T17:44:20Z,2022-08-11T14:12:57Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +live-service-web-worker,2022-01-26T17:44:20Z,,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +envelopeEncryption,2022-01-26T17:44:20Z,2022-05-24T08:34:47Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +database_metrics,2022-01-26T17:44:20Z,2023-04-28T13:19:06Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +live-pipeline,2022-01-26T17:44:20Z,2023-03-22T18:09:44Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +service-accounts,2022-01-26T17:44:20Z,2022-04-21T09:41:37Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +tempoSearch,2022-01-26T17:44:20Z,2022-06-01T17:32:10Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +disable_http_request_histogram,2022-01-26T17:44:20Z,2022-06-01T12:33:59Z,5d66194ec5d8a7c174a075b6023dcbf58d17b861,Ryan McKinley +featureHighlights,2022-02-03T11:53:23Z,,a79c048344bddff7a868b040d9a08953917480f9,Alex Khomenko +lokiBackendMode,2022-02-07T07:43:48Z,2022-06-08T06:14:34Z,560c77390550e12e8c0be507c00f27cde0aa31e5,Gábor Farkas +swaggerUi,2022-02-08T12:38:43Z,2023-03-01T14:36:37Z,35fe58de374003bb4b077a878cc47ffc0a9d27b5,Sofia Papagiannaki +influxdbBackendMigration,2022-02-09T18:26:16Z,2023-01-17T14:11:26Z,10232c78577520ce7b3fc6bba86d2e48ee5bab88,Joey Tawadrous +dashboardPreviewsScheduler,2022-02-10T18:45:00Z,2022-04-24T21:55:10Z,0276b029fc656e61763f6ae045ad7826380a43fc,Artur Wierzbicki +migrationLocking,2022-02-15T16:54:27Z,2024-03-22T19:22:29Z,d718ee191834b3084b2e8c8c6cac473bd9b97681,Sofia Papagiannaki +panelTitleSearch,2022-02-15T18:26:03Z,,d665306ad1c2abe515c7ba48350904f749eafc18,Ryan McKinley +dashboardPreviewsAdmin,2022-02-17T08:34:07Z,2023-02-03T21:45:10Z,3e53a08090ad8ac61d7d316b97a93a7a5ea63a57,Artur Wierzbicki +annotationComments,2022-02-22T07:47:42Z,2023-03-11T12:28:12Z,28c30a34adbe94d0f08acdba3c1605143dce6c6c,Alexander Emelin +dashboardComments,2022-02-22T07:47:42Z,2023-03-11T12:28:12Z,28c30a34adbe94d0f08acdba3c1605143dce6c6c,Alexander Emelin +lokiLive,2022-03-01T22:46:52Z,2023-06-19T10:03:51Z,796bc27f75d52148d5b15cc8c4901276c344df2d,Ryan McKinley +fileStoreApi,2022-03-03T06:53:26Z,2022-03-11T18:08:19Z,a8b90d9a2524765c49923c48a7fcf0025c85b733,Artur Wierzbicki +azureMonitorResourcePickerForMetrics,2022-03-14T19:07:45Z,2023-01-30T16:19:03Z,275f33cf37bb4221ef120310a87c17d2e0ff5f35,Sarah Zinger +storageLocalUpload,2022-03-17T17:19:23Z,2022-07-18T17:44:42Z,1cfb9a4a1916d3eac473e5ccd0cb77ce281f044f,Ryan McKinley +storage,2022-03-17T17:19:23Z,,1cfb9a4a1916d3eac473e5ccd0cb77ce281f044f,Ryan McKinley +saveDashboardDrawer,2022-03-30T17:07:41Z,2022-05-02T16:29:22Z,edf384c730a448a5e90b21bbbce49bc0e70e8197,Ryan McKinley +accesscontrol-builtins,2022-03-31T09:40:57Z,2022-05-19T07:29:36Z,0d87de153a2b8406d03efe0cf43b5a926ce7acab,Gabriel MABILLE +alertProvisioning,2022-04-01T06:32:00Z,2022-06-05T05:45:36Z,b8e277ee4c070b64ba4cf024b18c460626d1a2d3,Alexander Weaver +explore2Dashboard,2022-04-07T08:26:01Z,2022-11-07T16:06:40Z,ca286a238d1e9ce1e5357fde8148476a9d618cee,Giordano Ricci +publicDashboards,2022-04-07T18:30:19Z,,4a4d87dbdcaf4dca753bcf29205ac036b6c59862,Jeff Levin +persistNotifications,2022-04-20T09:42:32Z,2022-05-26T11:03:04Z,c48d8d1d488e0ce5870c54eb5828660081743108,kay delaney +serviceAccounts,2022-04-21T09:41:37Z,2022-06-27T08:22:49Z,8677552dda7b63f5d44df6fe51ca8a28726c5a5c,Eric Leijonmarck +commandPalette,2022-04-21T21:50:34Z,2023-02-02T14:44:21Z,3e7db088acd1da32ea489b0756cdf4e15ec2da69,Kristina +export,2022-04-25T23:59:18Z,2023-03-01T17:42:53Z,e0aeb83786731769e870d79c0d8a21ef2c33b073,Ryan McKinley +savedItems,2022-04-26T10:27:01Z,2022-06-27T14:41:00Z,e420252d45cd48fdcf5cc68ad84f89e36d5c936c,Ashley Harrison +tracing,2022-04-26T13:31:27Z,2023-03-21T09:35:21Z,3b4d237ade03e67a971ca8899e8e0371e1d83f2a,kay delaney +cloudWatchDynamicLabels,2022-04-29T09:43:04Z,2023-05-02T08:48:17Z,7bb4f5cd9baff81b354e4bb65fd4b6141e3488f6,Shirley +datasourceQueryMultiStatus,2022-05-03T16:02:20Z,,4ecd57f49c75d72927be62aba23a15dca02f8eb6,Will Browne +azureMonitorExperimentalUI,2022-05-04T13:54:09Z,2022-07-14T13:07:31Z,b2644de6c8a08181cbe0a9f545393bd42753477c,Adam Simpson +traceToMetrics,2022-05-05T20:46:18Z,2024-02-22T13:30:41Z,c1b5ea3e54169c7a4bbd58bd8be2c4c192a093c0,Connor Lindsey +prometheusStreamingJSONParser,2022-05-13T18:28:54Z,2022-11-11T16:53:12Z,87e8521591534bcfd8a7c78fbd39b226c13a99b2,Todd Treece +validateDashboardsOnSave,2022-05-22T00:44:12Z,2023-09-06T15:25:44Z,a3402641d6cac01208179126dcbd2256ad500cd8,sam boyer +disableEnvelopeEncryption,2022-05-24T08:34:47Z,,3e4b4dba46e5310015c7de5c3de1f3fae5332cb8,Joan López de la Franca Beltran +prometheusWideSeries,2022-05-24T20:17:11Z,2023-08-24T14:47:19Z,94b9c524a81df2085ad769229ca09cfd1923a8e9,Todd Treece +prometheusAzureOverrideAudience,2022-05-30T15:43:32Z,2023-07-16T21:30:14Z,2b83cf46186f745e356a24e4c78a1fbc24b3051e,Sergey Kostrukov +canvasPanelNesting,2022-05-31T19:03:34Z,,e18e8002c4dda17e95cb4f04977a6aefcc5984a4,Adela Almasan +cloudMonitoringExperimentalUI,2022-06-09T13:56:32Z,2022-09-15T14:12:26Z,615132002044e56074be63c5b597b9982e0a2a21,Kevin Yu +tempoApmTable,2022-06-09T16:56:15Z,2023-02-01T15:56:34Z,4ed7ff2ed13f2b5ec79f0946670c945edc902d7b,Joey Tawadrous +logRequestsInstrumentedAsUnknown,2022-06-10T08:56:55Z,,2d6e69226be20a7f001c7792bda8df68d90287c6,Carl Bergquist +dataConnectionsConsole,2022-06-10T10:13:31Z,2024-02-29T09:29:41Z,9a85a2e4410266270a1cd59db1937c7ba248966e,Levente Balogh +internationalization,2022-06-10T11:02:52Z,2023-06-02T08:24:40Z,9a62849dc39e960640b5762cc5eace1baf29c0ee,Josh Hunt +autoMigrateGraphPanels,2022-06-11T00:12:56Z,2023-03-23T04:02:36Z,ae8dd737704bb6232c52e19090e593d9165f5e77,Ryan McKinley +lokiDataframeApi,2022-06-13T06:33:46Z,2023-04-13T13:22:09Z,8fd9cb48548313cf43ebcf5dcfa0daad0bfdfe0c,Gábor Farkas +topnav,2022-06-20T14:25:43Z,,3c3293df78344a782fb65e5f977fd148daa597ce,Torkel Ödegaard +customBranding,2022-06-22T15:05:52Z,2022-10-05T12:07:35Z,405df77e3e6abcf5264ba192abe33630bd64da20,Tania +useLegacyHeatmapPanel,2022-06-23T18:48:28Z,2022-11-23T18:46:21Z,dd5a3b77472884035de13ddd441f41d0dd007e4b,Ryan McKinley +scenes,2022-07-07T06:53:02Z,,935334cbdabef8b0516bfe6c8ed45dd063cce7e9,Torkel Ödegaard +disableSecretsCompatibility,2022-07-12T20:27:37Z,,2d8a91a8461098f83aa512c867aff5af17b7893e,Guilherme Caulada +dashboardsFromStorage,2022-07-14T22:36:17Z,2023-03-20T16:36:49Z,da1701ce576ab26506d6256567b73a597043623a,Ryan McKinley +exploreMixedDatasource,2022-07-27T14:40:59Z,2022-07-27T15:17:31Z,e2258120e742b31ecde50e8de93544220a0762a3,Kristina +prometheusStreamingJSONParserTest,2022-07-28T12:26:51Z,2022-11-07T18:20:00Z,d0e548c3e53ffdbc29f35e055d52aa63cca310db,Andrej Ocenas +fullstoryUserTracking,2022-08-16T08:32:00Z,2022-08-16T10:45:14Z,961479b1115d39f3918b60c49a48841916939f03,Ivan Ortega Alba +traceqlEditor,2022-08-24T16:57:59Z,2022-12-13T13:27:45Z,c8f2148f7504b393b8f9b32fe19e7f519fa66793,Andre Pereira +athenaAsyncQueryDataSupport,2022-09-05T15:39:45Z,2024-03-20T14:14:21Z,34fe7a1119c0f649d3e9df866353438ff2177fdc,Kevin Yu +redshiftAsyncQueryDataSupport,2022-09-05T15:39:45Z,2024-03-20T14:14:21Z,34fe7a1119c0f649d3e9df866353438ff2177fdc,Kevin Yu +increaseInMemDatabaseQueryCache,2022-09-12T07:50:54Z,2023-02-06T21:01:04Z,72ae4a5aa368d18793f778683e92ae4a3ac7a1db,Carl Bergquist +correlations,2022-09-16T13:14:27Z,,9b4cdfe6526a290e5708a8c0760d50efda607d5c,Piotr Jamróz +grpcServer,2022-09-26T20:25:34Z,,55aae797441d2beadc911e390f14c04bb3f101da,Alexander Emelin +alertingBigTransactions,2022-10-06T06:22:58Z,2023-04-06T16:06:25Z,b476ae62fba9b94dd272409c521812108750bf50,Joe Blubaugh +lokiMonacoEditor,2022-10-06T14:35:30Z,2023-02-06T10:18:01Z,729ce8bb72a5db53fbeb1b5856557652bb175c7a,Matias Chomicki +objectStore,2022-10-06T19:48:53Z,2022-11-30T22:52:15Z,609abf00d1c6bd065f163e06882aa2f09dc0fcb5,Ryan McKinley +flameGraph,2022-10-07T10:39:14Z,2023-03-01T11:32:39Z,74c809f54459d235f4d3e9b1c117f291cbf16339,Joey Tawadrous +queryLibrary,2022-10-07T18:31:45Z,2023-03-20T16:00:14Z,bf264d2f76a1efb902b41791e110077d5c3a5557,Artur Wierzbicki +newPanelChromeUI,2022-10-10T15:03:16Z,2023-09-12T08:54:41Z,129a5a29e78853bb1427c8dc7473125ba2a35981,Polina Boneva +mysqlAnsiQuotes,2022-10-12T11:43:35Z,,80ede174dd6e343722cc708ebc4c5f21fb9e90af,ying-jeanne +showDashboardValidationWarnings,2022-10-14T13:51:05Z,,2e16d5499e1cf29e67db5031feda09407f69550c,Josh Hunt +interFont,2022-10-15T14:22:33Z,2022-12-01T11:59:37Z,9f5e691994c9db15f5984b196ab55fd7213b7f72,Torkel Ödegaard +accessControlOnCall,2022-10-19T16:10:09Z,,717bd4a6c051d1447c9e35385a4ba176dd391c65,Gabriel MABILLE +newDBLibrary,2022-10-26T01:20:41Z,2023-11-14T14:51:35Z,a3acfb1a48126119fd4913efbb572de162264e92,Ryan McKinley +nestedFolders,2022-10-26T14:15:14Z,,b346ae03105af606521bbefec16722d590378646,Kristin Laemmert +datasourceLogger,2022-11-02T13:51:51Z,2023-02-07T11:49:16Z,06705a49e236dc3ab3b35db51b7bad8625e4866f,Carl Bergquist +promQueryBuilder,2022-11-03T17:34:01Z,2022-12-19T13:52:06Z,857e545c5ac71722c8b2d3d27621dfb453dbb8ff,Ryan McKinley +elasticsearchBackendMigration,2022-11-10T15:35:15Z,2023-04-12T12:20:43Z,261d620f1c46eb43282cc444ffb4633b77af4283,Ivana Huckova +prometheusBufferedClient,2022-11-11T16:53:12Z,2022-12-12T17:05:55Z,1c5039085bdf9252d0aa1666675f5313589c1703,ismail simsek +accessTokenExpirationCheck,2022-11-14T15:47:46Z,2023-12-15T12:20:17Z,4915d21c25891dae2ee7a4eec4fe0dd2659d614d,Misi +cloudWatchCrossAccountQuerying,2022-11-28T11:39:12Z,,254577ba568d8010dcc96446e788866d618c96c8,Erik Sundell +authnService,2022-11-29T09:57:47Z,2023-06-07T06:57:41Z,17ec4089dc75da0e54edf60a754a0fc9291275e3,Karl Persson +secureSocksDatasourceProxy,2022-11-30T05:50:59Z,2023-04-18T17:11:23Z,6805c951e94c626401770ed468c6afb462093c0d,Stephanie Hingtgen +entityStore,2022-11-30T22:52:15Z,2023-12-06T20:21:21Z,14a080ec122f8ff3f656f44a46683f5161c15e14,Ryan McKinley +sessionRemoteCache,2022-12-07T09:55:48Z,2023-02-06T12:51:12Z,df4f0343e587695f37fa7a6de04cc884af4df284,Jo +returnUnameHeader,2022-12-07T12:14:53Z,2023-02-07T09:04:20Z,1b676d0d49460572352af4639274a4e7fe03fc9b,Carl Bergquist +datasourceOnboarding,2022-12-07T23:29:38Z,2023-05-17T12:22:40Z,955bf55c6a6a710a2fd7ebdbdac2d9a826858eaf,kay delaney +k8s,2022-12-13T15:41:16Z,2023-04-26T13:52:13Z,cf055ab4ecda9db9427ae09560aa26553433a5c0,Ryan McKinley +alertingBacktesting,2022-12-14T14:44:14Z,,ad09feed837737c188e37fc6adfead7b8a45715e,Yuri Tseretyan +disablePrometheusExemplarSampling,2022-12-19T15:00:15Z,2024-01-30T16:00:04Z,f67b8fe0dc2a2d860e71887a4d2e6e25da80bd8f,Ludovic Viaud +supportBundles,2022-12-20T10:13:37Z,2023-02-10T09:12:04Z,2c7410c87d81b8a644e738fee2bb6f34355111c6,Jo +publicDashboardsEmailSharing,2023-01-03T19:45:15Z,,f0ee3ac80ae2af695c56e709af435c155a2fb33d,owensmallwood +alertingNoNormalState,2023-01-13T23:29:29Z,,9d57b1c72eb817c5fe1b43ad7bc9706a264b2da0,Yuri Tseretyan +azureMultipleResourcePicker,2023-01-20T15:29:23Z,2023-01-30T16:19:03Z,b1efd911c1f7bfabe1b8231b059470c72dce4712,Andres Martinez Gotor +editPanelCSVDragAndDrop,2023-01-24T09:43:44Z,,4167214e3535f41c85004c6aca86fcb9ae8956dd,Zoltán Bedi +topNavCommandPalette,2023-01-24T12:41:09Z,2023-02-24T12:14:53Z,88347caf5fcc99c21c8a07367b9e846947367636,Josh Hunt +k8sDashboards,2023-01-25T19:10:16Z,2023-02-09T17:54:00Z,4965cf2edae6eae8e341e6c93c3a1aa56417efb3,Ryan McKinley +logsContextDatasourceUi,2023-01-27T14:12:01Z,,7c02d9bb8a864aab35c676f1a44c6c5be9eb6c09,Sven Grossmann +logsSampleInExplore,2023-01-27T16:30:25Z,2023-07-24T12:24:43Z,d5294eb8fa71ba62546d00ebce7b9376ab6f2b1a,Ivana Huckova +apiserver,2023-02-01T20:28:19Z,2023-02-09T17:54:00Z,d4f4a83574dd03a5419fa890b916d09d03cb9acd,Ryan McKinley +lokiQuerySplitting,2023-02-09T17:27:02Z,,94241f6676cf36824a180c11c2824ad41c81a470,Matias Chomicki +individualCookiePreferences,2023-02-21T10:19:07Z,,0caacb3333152015bf0369f54315a26ff6f60f88,Emil Tullstedt +newTraceView,2023-02-28T15:41:40Z,2023-04-21T10:31:24Z,82bcfb492867138bcc4744f88d36eb081c1b6811,Joey +drawerDataSourcePicker,2023-03-01T10:26:19Z,2023-04-14T11:01:10Z,dc1600ff14acc3deecc5de02f6584dcf20d3c8b1,Oscar Kilhed +traceqlSearch,2023-03-06T16:31:08Z,2023-07-24T15:26:10Z,fd37ff29b57520036fdc02f8a93a7d74770fd153,Andre Pereira +prometheusMetricEncyclopedia,2023-03-07T18:41:05Z,,9b6e531549e145d1a1a0ede870a6163c69b0bfd5,Brendan O'Handley +timeSeriesTable,2023-03-10T12:41:06Z,2023-10-13T08:00:42Z,548a5054ad8fa5c199bd01e20a02fbbe558d52fe,Domas +lokiQuerySplittingConfig,2023-03-20T15:51:36Z,,68551ac9ca5b92621e6ab895c759822797bbf418,Sven Grossmann +onlyExternalOrgRoleSync,2023-03-22T17:41:59Z,2023-07-25T09:51:47Z,3cd952b8bad8d23daee10fcc303a6d19c8b6d0a0,Eric Leijonmarck +autoMigrateOldPanels,2023-03-23T04:02:36Z,,baf5a1d1411ee7e5f297aba099e735ebeee3ab83,Ryan McKinley +clientTokenRotation,2023-03-23T13:39:04Z,2024-02-16T14:03:37Z,382b24742ab8b124af072533f74027a1a103b037,Karl Persson +disableAngular,2023-03-23T15:43:45Z,,58704390269c8eeedd32c571245d5ef08f0ffb82,Ryan McKinley +disableElasticsearchBackendExploreQuery,2023-03-27T13:52:27Z,2023-04-12T12:20:43Z,f3da91f53fa32a8e870124e9514a010b97359529,Ivana Huckova +emptyDashboardPage,2023-03-28T09:42:23Z,2024-01-25T14:04:29Z,221c5efedc593fb42d87c849b3d0827e325fc6cd,Polina Boneva +prometheusDataplane,2023-03-29T15:26:32Z,,674144c8e84cb4aca3bfc448f08b88bb516d3b77,Kyle Brandt +alertStateHistoryLokiOnly,2023-03-30T18:53:21Z,,b2abb6328677a90e3f3bc8b706d22734696d02e3,Alexander Weaver +alertStateHistoryLokiPrimary,2023-03-30T18:53:21Z,,b2abb6328677a90e3f3bc8b706d22734696d02e3,Alexander Weaver +alertStateHistoryLokiSecondary,2023-03-30T18:53:21Z,,b2abb6328677a90e3f3bc8b706d22734696d02e3,Alexander Weaver +unifiedRequestLog,2023-03-31T13:38:09Z,,be9361cb9ec2f1ad0bc0cda139c56e3cd46deb16,Emil Tullstedt +prometheusResourceBrowserCache,2023-04-03T14:07:17Z,2023-10-11T13:18:56Z,96e9e807396b600c3325f4d3580eeef0710f6050,Galen Kistler +renderAuthJWT,2023-04-03T16:53:38Z,,87a0c95164074dce6a877ea3faa3c7ac0e705ad3,Joan López de la Franca Beltran +showTraceId,2023-04-05T07:13:24Z,2023-07-20T05:59:38Z,4ded937c79a38087576b3224bf6f2ba59345571e,Gábor Farkas +pyroscopeFlameGraph,2023-04-05T12:57:24Z,2023-08-02T15:01:12Z,37eaf5019751aa76568574757830d63b764bbb1f,Andrej Ocenas +dataplaneFrontendFallback,2023-04-07T21:13:19Z,,af31c77331c89c6a1f8e00747c44c3f39bb737bb,Ryan McKinley +externalServiceAuth,2023-04-11T13:21:19Z,2024-02-26T10:29:09Z,5df9e64986e8c0324b4da96de5b55e533c3990ed,Gabriel MABILLE +disableElasticsearchBackendQuerying,2023-04-12T12:20:43Z,2023-04-14T09:24:35Z,303777e682758076d6a8e1df6b82f97b3b2393a7,Ivana Huckova +disableSSEDataplane,2023-04-12T16:24:34Z,,e78be44e1a38b7afb85909b807b6a2e9e5a98830,Kyle Brandt +useCachingService,2023-04-12T16:30:33Z,2024-01-17T21:53:25Z,5626461b3c358f5c5c5c690b82977c09744d09b4,Michael Mandrus +lokiMetricDataplane,2023-04-13T13:07:08Z,,531caec60263738ed6b31878b1ec093de635e4e2,Gábor Farkas +authenticationConfigUI,2023-04-13T14:07:43Z,2023-06-14T16:58:15Z,7476219b0c2876f60bc9c6e28b22190f239e1426,Alexander Zobnin +enableElasticsearchBackendQuerying,2023-04-14T09:24:35Z,2024-06-05T15:03:29Z,f48c858ca22b3f246d1033670f14605adb13cdf3,Ivana Huckova +advancedDataSourcePicker,2023-04-14T11:01:10Z,2024-02-06T09:20:07Z,7bc31ab04b5c87f06bdb383c7f4a6195e70d53d1,Ivan Ortega Alba +opensearchDetectVersion,2023-04-14T17:59:31Z,2023-05-16T14:22:23Z,1b0cee491a06fa135936b883406a6b11e6221322,Isabella Siu +pluginsAPIManifestKey,2023-04-18T14:12:05Z,2023-06-16T09:20:30Z,98c695c68fb3f0d276da0d98b1e78186e459db50,Andres Martinez Gotor +newTraceViewHeader,2023-04-21T10:31:24Z,2023-07-19T13:31:58Z,6522bb377e1bc4b64fe0aebd7e34ec2111859e11,Joey +enableDatagridEditing,2023-04-24T14:46:31Z,,efd0e9cbea4a6c4c51337f14004f2df2f876fdb1,Victor Marin +faroDatasourceSelector,2023-05-05T00:35:10Z,,e7cbe0276e3a6f41c31dd8e1ac16ccaa90b184f3,Elliot Kirk +extraThemes,2023-05-10T13:37:04Z,,f8cf67347f069310b8cc6a2d9ff83345a81e3247,Torkel Ödegaard +dataSourcePageHeader,2023-05-23T13:18:00Z,2023-11-06T20:21:15Z,7f84e83ffee53446b4c458c29f7c1385dac09ce3,Taewoo K +alertingNotificationsPoliciesMatchingInstances,2023-05-30T13:15:22Z,2023-11-09T17:35:03Z,2f0728ac677c26b291947354058253d2e8a50bb7,Konrad Lalik +lokiPredefinedOperations,2023-06-02T10:52:36Z,,06003c98c855fcb7c1c718c3f0e5f190b644656a,Ivana Huckova +pluginsFrontendSandbox,2023-06-05T08:51:36Z,,1ed4c0382b3812eecf9722596d8a3ac922f3543d,Esteban Beltran +refactorVariablesTimeRange,2023-06-06T13:12:09Z,,07dd90b5a8866273813c6b4a5e7087cea529f908,Alexa V +sqlDatasourceDatabaseSelection,2023-06-06T16:28:52Z,,c0a1fc2cbdc1ceab4fdcb43270fd3720efe4494a,Jev Forsberg +cloudWatchLogsMonacoEditor,2023-06-12T13:49:52Z,2024-03-18T12:56:57Z,5a831d877ac3305e7dfdb54057069b41b36b40fa,Isabella Siu +recordedQueriesMulti,2023-06-14T12:34:22Z,,db75f20e53e717503fe3bf27217fedc40f4a12e1,Kyle Brandt +exploreScrollableLogsContainer,2023-06-15T11:25:34Z,2024-03-12T14:53:13Z,cda10fae525d5cf784368791e1aaa1de2ce88f4b,Gareth Dawson +alertingLokiRangeToInstant,2023-06-16T17:55:49Z,2023-09-13T11:52:40Z,934ba1aaa1fa4c1b9f1dcc0d6753c67a23c4f466,Jean-Philippe Quéméner +lokiExperimentalStreaming,2023-06-19T10:03:51Z,,271cdb4baa6d5b021e013b463350f227173c4e95,Gábor Farkas +flameGraphV2,2023-06-19T14:34:06Z,2023-07-11T12:58:43Z,5ca03a82f042808efabae84b46991106c23de514,Andrej Ocenas +pluginsDynamicAngularDetectionPatterns,2023-06-26T13:33:21Z,2024-04-15T08:37:28Z,cca9d897339ae2de087bc5b7177849a730d4b38d,Giuseppe Guerra +elasticToggleableFilters,2023-06-27T08:38:20Z,2023-07-28T12:49:02Z,c1ce24c90f75daa133e2cb59288ecc14195e5194,Matias Chomicki +vizAndWidgetSplit,2023-06-27T10:22:13Z,,2785ed80d999b9ee1770e1209284d4fccd985401,Alexa V +nestedFolderPicker,2023-06-28T09:40:29Z,2024-06-04T09:16:12Z,f18a7f7d9696a6e80606dc49d5ac0064384f111d,Josh Hunt +frontendSandboxMonitorOnly,2023-07-05T11:48:25Z,,72f6793344fb3a63f5a8a86570b786b518264366,Esteban Beltran +prometheusIncrementalQueryInstrumentation,2023-07-05T19:39:49Z,,daf9f9cd199e0bbc110222a3f005dc05dab1681a,Galen Kistler +dashboardEmbed,2023-07-06T14:43:20Z,2024-04-19T10:48:08Z,420b19e0e4bbdb97ae707cc1360bef7f79839d02,Alex Khomenko +awsDatasourcesTempCredentials,2023-07-06T15:06:11Z,,d33508453f6f1f7aab262b54c36ef471ed3eab87,Ida Štambuk +logsExploreTableVisualisation,2023-07-12T13:52:42Z,,7e4e743a42052183f549f7fa88cd0cc362844ad1,Sven Grossmann +transformationsRedesign,2023-07-12T16:35:49Z,,5099e882276227f4b4c8e695f35156b7873d81b3,Ludovic Viaud +lokiLogsDataplane,2023-07-13T07:58:00Z,,e045860fbf449feb101d187730a46bb1cdab787a,Gábor Farkas +mlExpressions,2023-07-13T17:37:50Z,,541bfe636daa55705a130df4004aa417c99adc09,Yuri Tseretyan +disableTraceQLStreaming,2023-07-14T14:10:46Z,2023-07-26T13:33:16Z,c1709c93013a3fa5797898e81c046a2a53ae7ed4,Andre Pereira +grafanaAPIServer,2023-07-14T19:22:10Z,2024-01-23T16:27:28Z,52121b7165b6ffcdf10dd7a62fdf04043ef0c00a,Todd Treece +featureToggleAdminPage,2023-07-18T20:43:32Z,,600f623610b00547400e00fd0b2ee7dc4bab5849,Ibrahim +lokiFormatQuery,2023-07-21T12:03:56Z,2024-06-05T09:46:28Z,4e42f9b6198d39ffca82c3c3d32a16d54c66090b,Gareth Dawson +splitScopes,2023-07-21T14:23:01Z,2024-02-23T15:53:37Z,cfa1a2c55feea58bd7f82e4e03df2ea712ef34a7,Ieva +awsAsyncQueryCaching,2023-07-21T15:34:07Z,,56913fbd9578c4666599b2ab46423361d6c9261f,Isabella Siu +toggleLabelsInLogsUI,2023-07-24T08:22:47Z,2023-10-27T11:00:49Z,84f94cdc24c3b911ae4f8eabfe875b3e4a9c1f1b,Matias Chomicki +azureMonitorDataplane,2023-07-24T13:50:49Z,2023-11-08T15:02:46Z,ee60d8c82dbcc8496d52d673bfc000901d72f232,Kyle Brandt +gcomOnlyExternalOrgRoleSync,2023-07-25T12:27:02Z,2023-11-13T09:56:02Z,f7c6491f7317f3fbaf4f21b8beec967d76778a87,Ieva +traceQLStreaming,2023-07-26T13:33:16Z,,89092a1e697788428f76fead65371b7c904cd13f,Andre Pereira +prometheusConfigOverhaulAuth,2023-07-26T16:09:53Z,,98cb3ce3b63ee4d1b82e6b04d11f8a6a065de7b4,Brendan O'Handley +configurableSchedulerTick,2023-07-26T16:44:12Z,,c7598cc6fb492cb7cc34eec90fbd2a08d859cffd,Yuri Tseretyan +permissionsFilterRemoveSubquery,2023-08-02T07:39:25Z,,2c26a02b82238e0f89ca08844914cff22a84255a,Sofia Papagiannaki +influxdbSqlSupport,2023-08-02T16:27:43Z,2024-04-18T14:29:27Z,77e7ae2a1b253276ceae4ef5cda1dffc0e674b4d,ismail simsek +noBasicRole,2023-08-04T09:08:14Z,2023-10-12T11:00:26Z,64ed77ddce79009ce05f43801cc578c135f05b6b,linoman +alertingNoDataErrorExecution,2023-08-15T14:27:15Z,,0717ec11d6042bbc5b4cb8d908a396ecec78cdd5,Yuri Tseretyan +metricsSummary,2023-08-28T14:02:12Z,,59e4c257bba00968741ffc235e2dbe9d4c5b90ac,Joey +angularDeprecationUI,2023-08-29T14:05:47Z,,bf4d025012af42ce023b1166e95626f86e819855,Giuseppe Guerra +dashgpt,2023-08-30T20:22:05Z,,2f4fbf89cac96b74c2375b9ec9710a2c890dcf8f,Nathan Marrs +reportingRetries,2023-08-31T07:47:47Z,,f919c55bbb417f3b1023d72bbfce6617acfd1ab9,Agnès Toulet +newBrowseDashboards,2023-09-07T10:41:00Z,2023-11-22T15:22:00Z,ebe13a53f729c265c1f161be4e6f2c29ba074127,Ashley Harrison +sseGroupByDatasource,2023-09-07T20:02:07Z,,5cc737bb24f80514d35b1f02c7113e106a277dc6,Kyle Brandt +requestInstrumentationStatusSource,2023-09-11T10:13:13Z,2024-02-06T08:29:41Z,8ee43f37051e6b6a016e1c38c3d585cc0df47419,Marcus Efraimsson +wargamesTesting,2023-09-13T18:32:01Z,,2d8f5c1488142288d73a4db892e2108b7c7db03b,Carl Bergquist +alertingInsights,2023-09-14T12:58:04Z,,5d88b8a4f58abeafc11553865f99df69b760fcd3,Virginia Cepeda +dockedMegaMenu,2023-09-18T10:57:11Z,2024-02-06T13:43:11Z,0ceeb18269765b40eb5cf7bf2536894152d5e5e8,Laura Fernández +lokiRunQueriesInParallel,2023-09-19T09:34:01Z,,98aa7db64ab4c4d0f3699a9bcbde9894429daa62,Travis Patterson +pluginsAPIMetrics,2023-09-21T11:36:32Z,,8e8bd2760b8c05df9015900ebdf865c1629881de,Esteban Beltran +externalCorePlugins,2023-09-22T08:50:13Z,,61cdfba87a36bd4b8e1bb227a14c5d88fc943aff,Andres Martinez Gotor +httpSLOLevels,2023-09-22T08:52:28Z,2024-02-06T08:29:41Z,e5fbc4a4cd587e4b342afd0ab0136754cf8ebf76,Carl Bergquist +idForwarding,2023-09-25T15:21:28Z,,d15661c726b582212e1329f0ebe938664445e44a,Karl Persson +cloudwatchNewRegionsHandler,2023-09-25T18:19:12Z,2024-01-30T12:11:52Z,ef441f02d09730449f3f7bbdcf3b31dc00ed797a,Sarah Zinger +cloudWatchWildCardDimensionValues,2023-09-27T14:41:48Z,2024-03-15T13:49:53Z,06a35f55ac56ca5699031b2af9202ac7a26918fc,Isabella Siu +externalServiceAccounts,2023-09-28T07:26:37Z,,4563fc48afe81ff1af54b6a5bf6d65355f961155,Gabriel MABILLE +alertingModifiedExport,2023-09-28T14:07:45Z,2023-10-12T17:17:32Z,169c5262a5e7d04234119a3733b00bb787b8bf90,Sonia Aguilar +enableNativeHTTPHistogram,2023-10-03T18:23:55Z,,0fc403d116b9cbd0b93e497cff0ddded55f9fe60,Carl Bergquist +transformationsVariableSupport,2023-10-04T14:28:46Z,,40cdb3033697e8d8e43d0ee0342f0d16fac7d633,Oscar Kilhed +kubernetesPlaylists,2023-10-05T19:00:36Z,,664ebf771e2a1d2f94bc14d3dababb634c4ac40f,Todd Treece +grafanaAPIServerWithExperimentalAPIs,2023-10-06T18:55:22Z,,717a9dd6160e352ff7c184bfcddf2410eed9d908,Ryan McKinley +panelMonitoring,2023-10-09T05:19:08Z,,ef82767dabea7d19cd8efac0c36ed590d4d767f4,Victor Marin +navAdminSubsections,2023-10-10T10:50:44Z,2023-11-17T10:04:34Z,f56cc6fdc01dbf2efb802f22650cb2bef0c40179,Ashley Harrison +recoveryThreshold,2023-10-10T14:51:50Z,,810fbc3327841da6d21f945e75cad9daf040e625,Yuri Tseretyan +libraryPanelRBAC,2023-10-11T23:30:50Z,,a12cb8cbf3a9b33841b2f2cb1522be11de78c86a,kay delaney +awsDatasourcesNewFormStyling,2023-10-12T08:59:10Z,,2771fb940342aa152377b26b9554eb15082f90ac,Ida Štambuk +cachingOptimizeSerializationMemoryUsage,2023-10-12T16:56:49Z,,94ce87571ddfcede0fb7a229a65502b385d5bca3,Michael Mandrus +panelTitleSearchInV1,2023-10-13T12:04:24Z,,bf2f2540da7a4e4b8d80e1fa4ae3d05868cf7b69,Arati R +exploreContentOutline,2023-10-13T16:57:13Z,,4ec54bc2c39ba43843c693fdb2a4529b6a4703f2,Haris Rozajac +formatString,2023-10-13T18:17:12Z,,889576ac1d9278b1c6e3e278e8195968646a2db0,Sol +pluginsInstrumentationStatusSource,2023-10-17T08:27:45Z,2024-02-21T11:57:40Z,f5076d1868caa14ce44a70e812315541b4199d9f,Giuseppe Guerra +teamHttpHeaders,2023-10-17T10:23:54Z,,be5ba6813209b5b24e955e0f761032cb5826b578,Eric Leijonmarck +costManagementUi,2023-10-17T16:15:51Z,2024-01-08T14:25:11Z,de1ed216f4bbf6f341aa22b144fa66d583a63981,Adam Bannach +managedPluginsInstall,2023-10-18T13:17:03Z,,43add83d1a84fe0945860a9cc700df92c7e8341f,Hugo Kiyodi Oshiro +prometheusPromQAIL,2023-10-19T15:45:32Z,,5580d061019bee46ea2e69c94041f3da14585ceb,Brendan O'Handley +cloudWatchBatchQueries,2023-10-20T19:09:41Z,,ecbc52f51529e1f35e26895db1a10f8a1c2f4244,Isabella Siu +alertingContactPointsV2,2023-10-25T13:57:53Z,2023-11-30T12:37:14Z,e12e40fc2493160338237b0b94e72fa530a78ef4,Gilles De Mey +alertmanagerRemoteOnly,2023-10-30T16:27:08Z,,363830883cb1f5de30f7015df5cba419df47468e,Santiago +alertmanagerRemoteSecondary,2023-10-30T16:27:08Z,,363830883cb1f5de30f7015df5cba419df47468e,Santiago +alertmanagerRemotePrimary,2023-10-30T16:27:08Z,,363830883cb1f5de30f7015df5cba419df47468e,Santiago +annotationPermissionUpdate,2023-10-31T13:30:13Z,,c51c51458e4dd103aa0c099aa48b0d41f9375f86,Ieva +kubernetesPlaylistsAPI,2023-10-31T17:26:39Z,2023-11-08T19:14:05Z,dd773e74f120ba908cafd596a6875c2d7fa199bf,Ryan McKinley +traceToProfiles,2023-11-01T10:14:24Z,2024-01-22T14:21:14Z,c39e9a8f527b79881b95f64fd1c413b4bff42983,Joey +extractFieldsNameDeduplication,2023-11-02T15:47:42Z,,0eda368d32dbd7f57635cabd6b7d6870ca7b58ee,Oscar Kilhed +dashboardSceneForViewers,2023-11-02T19:02:25Z,,6e80a3d59b14d9af235431d6acdce95835e2a792,Dominik Prokop +panelFilterVariable,2023-11-03T12:15:54Z,,6bf4d0cbc6756ec8e8fb64b3a9d7b76f4c4194f5,Jacob Zelek +addFieldFromCalculationStatFunctions,2023-11-03T14:39:58Z,,61d63d3034d7eed4837c63e1f5cb2f6b7114758d,Victor Marin +pdfTables,2023-11-06T13:39:22Z,,95b48339f89c7d267bce7d894404d26ccd75d0e3,Agnès Toulet +newVizTooltips,2023-11-06T16:35:59Z,2024-04-03T00:32:01Z,6b4b7127544865b78f712d907e6f1719595f4232,Adela Almasan +ssoSettingsApi,2023-11-08T09:50:01Z,,5285e9503be5702680acb2b52a6bda0632f4603d,Misi +logsInfiniteScrolling,2023-11-09T10:54:03Z,,174c2ab45a2af912519153c5c3e671f04396d7d7,Matias Chomicki +flameGraphItemCollapsing,2023-11-09T14:31:07Z,,494a07b522df4e3ff9512b47767745a94c15f080,Andrej Ocenas +alertingDetailsViewV2,2023-11-09T17:35:03Z,2024-03-14T14:18:01Z,323ee7c38ceb18b8e71c780d797fabac7041a673,Gilles De Mey +alertingSimplifiedRouting,2023-11-10T13:14:39Z,,68e37c3925080cf64a5e7570eabb042f19ca2dbf,Sonia Aguilar +dashboardScene,2023-11-13T08:51:21Z,,4bc322ca1d6ed63d7e79eecb1ed09f3043f9aedb,Torkel Ödegaard +datatrails,2023-11-15T11:28:29Z,2024-04-09T18:15:18Z,1f1d348e1700735683dc9b80fce106b8a5bc4cee,Torkel Ödegaard +pluginsSkipHostEnvVars,2023-11-15T17:09:14Z,,cb0a88a02770eaee2561fd434d8339cab268f02e,Giuseppe Guerra +logRowsPopoverMenu,2023-11-16T09:48:10Z,,9cb303c3f701169e8651267744a4b5fe1695a066,Matias Chomicki +lokiStructuredMetadata,2023-11-16T16:06:14Z,,a01f8c5b42bb7937139b8d52e9723cc37e5f7ae6,Sven Grossmann +tracesEmbeddedFlameGraph,2023-11-23T13:36:53Z,2024-01-22T14:21:14Z,4f46fb412ca4e96b0b0ef3f462aead3fc146389e,Joey +regressionTransformation,2023-11-24T14:49:16Z,,ab982e7bd36b86c94093bddcc31008f5dc49660e,Oscar Kilhed +displayAnonymousStats,2023-11-29T16:58:41Z,2024-02-23T15:53:37Z,59bdff0280d52ca5d8918157d7697b9279b25501,Eric Leijonmarck +influxqlStreamingParser,2023-11-29T17:29:35Z,,5845f140758473ab5ffe789bec4077032fd22839,ismail simsek +kubernetesSnapshots,2023-12-05T22:31:49Z,,439edebcd605a1b63bf3a9b0ab5c2b83341cd5cd,Ryan McKinley +grafanaAPIServerEnsureKubectlAccess,2023-12-06T20:21:21Z,,c4c9bfaf2e7fa12a8e453df0f089c8b4f914a3d3,Dan Cech +unifiedStorage,2023-12-06T20:21:21Z,,c4c9bfaf2e7fa12a8e453df0f089c8b4f914a3d3,Dan Cech +alertStateHistoryAnnotationsFromLoki,2023-12-11T19:17:01Z,2024-01-25T17:56:09Z,4c1bf86ae11696277025296b86e8514386b4bb31,William Wernert +tableSharedCrosshair,2023-12-13T09:33:14Z,,5aff3389f4633d6970eb2b3629ac015e564c626c,Victor Marin +lokiQueryHints,2023-12-18T20:43:16Z,,2165c9b3f000f59c9fbda80d2bfe3bc74cd6d9dc,Sven Grossmann +canvasPanelPanZoom,2024-01-02T19:52:21Z,,2502fe4d19faa993da8fd4d21b9802d59f4b02af,Drew Slobodnjak +alertingPreviewUpgrade,2024-01-05T18:31:05Z,2024-03-14T14:36:35Z,49891d6a72537c869628a13ab75c8d616fbac590,Matthew Jacobson +enablePluginsTracingByDefault,2024-01-10T11:25:54Z,2024-04-11T16:40:47Z,b40d3e748717961ef68457442d71c1284189c577,Giuseppe Guerra +cloudRBACRoles,2024-01-10T13:19:01Z,,5b3cd9f55b292f0b693f3bb5171da7b69dac4a95,Karl Persson +alertingQueryOptimization,2024-01-10T20:52:58Z,,afa33f12b2cf50d2c7e438f498d27b0684797778,Matthew Jacobson +newFolderPicker,2024-01-15T11:43:19Z,,ec53487c995777b314f566f5a1054e3f8e29ec05,Ashley Harrison +kubernetesFeatureToggles,2024-01-18T05:32:44Z,,41e523bde7db5706f339d418c68d019039a8062e,Ryan McKinley +returnToPrevious,2024-01-18T17:12:14Z,2024-05-27T15:47:57Z,5800e40fba2accf96d81328f000e35bbb7c7acf1,Laura Fernández +jitterAlertRulesWithinGroups,2024-01-18T18:48:11Z,,00a260effab802edc8f72df50bfb6447aac343f0,Alexander Weaver +jitterAlertRules,2024-01-18T18:48:11Z,2024-02-09T21:53:58Z,00a260effab802edc8f72df50bfb6447aac343f0,Alexander Weaver +onPremToCloudMigrations,2024-01-22T16:09:08Z,,cf13cb9f70c2230f17450667ce59440304fb023c,Michael Mandrus +alertingSaveStatePeriodic,2024-01-23T16:03:30Z,,aa25776f813926cb4f1947d4ae5a014f4e7728ff,Jean-Philippe Quéméner +promQLScope,2024-01-29T20:22:17Z,,43d0664340f3e3af219d7b5c747f486a073f5ce3,Kyle Brandt +slateAutocomplete,2024-01-31T10:01:20Z,2024-02-01T13:03:11Z,39057552dc2ddfe8ee770c67a71cf55159aca741,Ashley Harrison +nodeGraphDotLayout,2024-01-31T16:26:12Z,,cb945aa5df3453ab2a851ef7ec8bee244e3eeac4,Andrej Ocenas +kubernetesQueryServiceRewrite,2024-01-31T18:36:51Z,2024-04-19T09:26:21Z,e013cd427cb0457177e11f19ebd30bc523b36c76,Ryan McKinley +influxdbRunQueriesInParallel,2024-02-01T10:58:24Z,,536153c3362782ae7f79e06755e8c7e0cd3b3acd,ismail simsek +groupToNestedTableTransformation,2024-02-07T14:28:26Z,,756cd3c28b0648f55a6c0158f5faf63b632ebe70,Kyle Cunningham +newPDFRendering,2024-02-08T12:09:34Z,,28e66b4ad82ecebb551374325f0be4332412341c,Agnès Toulet +autoMigrateGraphPanel,2024-02-08T22:00:48Z,,829672759c12b27f849c92c3a2aee4a6b0037920,Nathan Marrs +dashboardSceneSolo,2024-02-11T08:08:47Z,,fe6d1460b09b403fc74fd20e95f61513eede2555,Torkel Ödegaard +kubernetesAggregator,2024-02-12T20:59:35Z,,d6e6298103d5d6a4efd1c21a3d74f429b506f3a7,Todd Treece +autoMigrateStatPanel,2024-02-14T16:06:25Z,,ce750e06187599da6b9c0a91ef95c7a62fe0d069,Nathan Marrs +autoMigrateWorldmapPanel,2024-02-14T16:06:25Z,,ce750e06187599da6b9c0a91ef95c7a62fe0d069,Nathan Marrs +autoMigratePiechartPanel,2024-02-14T16:06:25Z,,ce750e06187599da6b9c0a91ef95c7a62fe0d069,Nathan Marrs +autoMigrateTablePanel,2024-02-14T16:06:25Z,,ce750e06187599da6b9c0a91ef95c7a62fe0d069,Nathan Marrs +groupByVariable,2024-02-14T17:18:04Z,,f016f95298fe490a865612864520f3622f8e804a,Dominik Prokop +alertingUpgradeDryrunOnStart,2024-02-16T16:29:54Z,2024-03-14T14:36:35Z,dfaf6d1e2e13b2bd11dc8f0cd4432bfcad819aa9,Matthew Jacobson +expressionParser,2024-02-17T00:59:11Z,,f23f50f58d7ab5cb1fd88b42b6c58ec09c1a159d,Ryan McKinley +sqlExpressions,2024-02-27T21:16:00Z,,70009201d44c2d0ab39cc77081808a69a6c4fd63,Scott Lepper +aiGeneratedDashboardChanges,2024-03-05T12:01:31Z,,a7c06d26f14b2a9fa8faa929a6c9a0c355018429,Ivan Ortega Alba +scopeFilters,2024-03-05T15:41:19Z,,b3efb4217e48656f24aacdffd5737595d7361afe,Carl Bergquist +betterPageScrolling,2024-03-06T15:06:47Z,,6a4e0c692ab26f4d4cb99ae615013e0b6e23f90b,Josh Hunt +emailVerificationEnforcement,2024-03-11T14:09:44Z,2024-03-22T13:30:58Z,0b55d72fb5698e1ea2cf73eaceae166cd5619daa,Karl Persson +ssoSettingsSAML,2024-03-14T11:04:45Z,,831ee9ee1696c0aa7a6e4ca022ddfc0ab28b86dc,linoman +publicDashboardsScene,2024-03-22T14:48:21Z,,8d4ca72f2a0e66c446d58d8bf13fadbc988fce11,Juan Cabanas +autoMigrateXYChartPanel,2024-03-22T15:44:37Z,,d7fa99e2df8269425eefb6373a665d8ec0b219d9,Leon Sorokin +usePrometheusFrontendPackage,2024-03-23T00:47:53Z,2024-04-15T21:45:23Z,d0845952117152e7707f964cc343790497220990,Brendan O'Handley +oauthRequireSubClaim,2024-03-25T13:22:24Z,,2f3a01f79fda7f6c465dd64adf5cf5c7227f9d19,Karl Persson +authAPIAccessTokenAuth,2024-04-02T15:45:15Z,,5340a6e548b1e5fcd3af66695ca34d7e943fd068,Jo +newDashboardWithFiltersAndGroupBy,2024-04-04T11:25:21Z,,32b6ef9d153dc7def2c17b81b02cfb1c412eb315,Dominik Prokop +prometheusCodeModeMetricNamesSearch,2024-04-04T20:38:23Z,,559fab9dc6a6c882e7d9621ba013c3a53347176a,Nick Richmond +cloudWatchNewLabelParsing,2024-04-05T15:57:56Z,,58f32150c262605d188874b88f44a7dece4b9388,Isabella Siu +exploreMetrics,2024-04-09T18:15:18Z,,66c0fd4dcc3202e11f41b302d27894dc162fb288,Darren Janeczek +accessActionSets,2024-04-12T16:19:25Z,,56f4664875047d6861ea3facbc94cd921e263950,Ieva +disableNumericMetricsSortingInExpressions,2024-04-16T14:52:47Z,,d3fee607e2818747ad02daf6b8c58cc801075076,Nick Richmond +queryServiceRewrite,2024-04-19T09:26:21Z,,5a8384a2455bbd3c0ba5ec67e5f5e3cc4a836904,Ryan McKinley +queryServiceFromUI,2024-04-19T09:26:21Z,,5a8384a2455bbd3c0ba5ec67e5f5e3cc4a836904,Ryan McKinley +queryService,2024-04-19T09:26:21Z,,5a8384a2455bbd3c0ba5ec67e5f5e3cc4a836904,Ryan McKinley +grafanaManagedRecordingRules,2024-04-22T17:53:16Z,,c32953e52cf9fd46a462711bc23fde15a5c3b6bf,Alexander Weaver +logsExploreTableDefaultVisualization,2024-05-02T15:28:15Z,,840aeddbd1957117d9b62074c155e87bb4afd4b4,Galen Kistler +autofixDSUID,2024-05-03T11:32:07Z,,b6f899d953a0924760dc5fd5a3d40669c86d475d,Andres Martinez Gotor +newDashboardSharingComponent,2024-05-03T15:02:18Z,,d1434fad3a68bc1d2b49725be31e45526e6ad349,Juan Cabanas +tlsMemcached,2024-05-09T19:12:08Z,,b009536329d110afd807ef2f27f2b7dcc7d310ba,lean.dev +notificationBanner,2024-05-13T09:32:34Z,,f3953b4955c218cc4678e842faa3d3380fd0f8f7,Alex Khomenko +dualWritePlaylistsMode3,2024-05-14T12:11:56Z,2024-05-31T18:18:09Z,6836bfe1ea1bf62f4eb66dc1328a456183874f81,Arati R +dualWritePlaylistsMode2,2024-05-14T12:11:56Z,2024-05-31T18:18:09Z,6836bfe1ea1bf62f4eb66dc1328a456183874f81,Arati R +dashboardRestore,2024-05-16T17:36:26Z,,42d75ac737d7ac001a6d53376e25512408a01db5,Ezequiel Victorero +datasourceProxyDisableRBAC,2024-05-21T13:05:16Z,,0072e4a92d896df343d9586522d6f7533773da78,Aaron Godin +alertingDisableSendAlertsExternal,2024-05-23T12:29:19Z,,8421919cb552b9e8dbd4ebba20bcc67bbc5e6b4f,Steve Simpson +datasourceQueryTypes,2024-05-23T16:46:28Z,,42b0f802de31f3d42ece68544c193a69ea381bc0,Ryan McKinley +alertingListViewV2,2024-05-24T14:40:49Z,,99b5259cc1efa3f5c9dbb9b0153be14437818f38,Gilles De Mey +preserveDashboardStateWhenNavigating,2024-05-27T12:28:06Z,,6e9543e0addab825f6755d4d02c5a15a1598a483,Dominik Prokop +alertingCentralAlertHistory,2024-05-29T15:01:38Z,,289ce6185574df99acea37b86c2e0866ea5940fd,Sonia Aguilar +pluginProxyPreserveTrailingSlash,2024-06-05T11:36:14Z,,fe3e5917f1bc83ab29b6a57578316e054718aa4a,Marcus Efraimsson +kubernetesDashboards,2024-06-05T14:34:23Z,,41e0430f83bf7db50c4caaa1472afa3bf3d5c2dc,Ryan McKinley +azureMonitorPrometheusExemplars,2024-06-06T16:53:17Z,,c9778c3332aa93e5dd8bc3b894264e1955f0d593,Andreas Christou diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 1479474d031..922a10279ed 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -3,111 +3,11 @@ "apiVersion": "featuretoggle.grafana.app/v0alpha1", "metadata": {}, "items": [ - { - "metadata": { - "name": "notificationBanner", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the notification banner UI and API", - "stage": "experimental", - "codeowner": "@grafana/grafana-frontend-platform" - } - }, - { - "metadata": { - "name": "datasourceProxyDisableRBAC", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Disables applying a plugin route's ReqAction field to authorization", - "stage": "GA", - "codeowner": "@grafana/identity-access-team", - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "recordedQueriesMulti", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables writing multiple items from a single query within Recorded Queries", - "stage": "GA", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "traceQLStreaming", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables response streaming of TraceQL queries of the Tempo data source", - "stage": "GA", - "codeowner": "@grafana/observability-traces-and-profiling", - "frontend": true - } - }, - { - "metadata": { - "name": "sseGroupByDatasource", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Send query to the same datasource in a single request when using server side expressions. The `cloudWatchBatchQueries` feature toggle should be enabled if this used with CloudWatch.", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "newPDFRendering", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "New implementation for the dashboard-to-PDF rendering", - "stage": "preview", - "codeowner": "@grafana/sharing-squad" - } - }, - { - "metadata": { - "name": "autoMigrateOldPanels", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Migrate old angular panels to supported versions (graph, table-old, worldmap, etc)", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "datasourceQueryTypes", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Show query type endpoints in datasource API servers (currently hardcoded for testdata, expressions, and prometheus)", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, { "metadata": { "name": "accessActionSets", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-04-12T16:19:25Z" }, "spec": { "description": "Introduces action sets for resource permissions", @@ -115,744 +15,11 @@ "codeowner": "@grafana/identity-access-team" } }, - { - "metadata": { - "name": "metricsSummary", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables metrics summary queries in the Tempo data source", - "stage": "experimental", - "codeowner": "@grafana/observability-traces-and-profiling", - "frontend": true - } - }, - { - "metadata": { - "name": "configurableSchedulerTick", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable changing the scheduler base interval via configuration option unified_alerting.scheduler_tick_interval", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad", - "requiresRestart": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "wargamesTesting", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Placeholder feature flag for internal testing", - "stage": "experimental", - "codeowner": "@grafana/hosted-grafana-team" - } - }, - { - "metadata": { - "name": "alertingSaveStatePeriodic", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Writes the state periodically to the database, asynchronous to rule evaluation", - "stage": "privatePreview", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "live-service-web-worker", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "This will use a webworker thread to processes events rather than the main thread", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "lokiExperimentalStreaming", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Support new streaming approach for loki (prototype, needs special loki build)", - "stage": "experimental", - "codeowner": "@grafana/observability-logs" - } - }, - { - "metadata": { - "name": "exploreContentOutline", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Content outline sidebar", - "stage": "GA", - "codeowner": "@grafana/explore-squad", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "lokiLogsDataplane", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Changes logs responses from Loki to be compliant with the dataplane specification.", - "stage": "experimental", - "codeowner": "@grafana/observability-logs" - } - }, - { - "metadata": { - "name": "newDashboardWithFiltersAndGroupBy", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables filters and group by variables on all new dashboards. Variables are added only if default data source supports filtering.", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "transformationsRedesign", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the transformations redesign", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "autoMigratePiechartPanel", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Migrate old piechart panel to supported piechart panel - broken out from autoMigrateOldPanels to enable granular tracking", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "dashboardSceneForViewers", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables dashboard rendering using Scenes for viewer roles", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "panelFilterVariable", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables use of the `systemPanelFilterVar` variable to filter panels in a dashboard", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "cloudRBACRoles", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enabled grafana cloud specific RBAC roles", - "stage": "experimental", - "codeowner": "@grafana/identity-access-team", - "requiresRestart": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "promQLScope", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "In-development feature that will allow injection of labels into prometheus queries.", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "kubernetesAggregator", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable grafana aggregator", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "extraThemes", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables extra themes", - "stage": "experimental", - "codeowner": "@grafana/grafana-frontend-platform", - "frontend": true - } - }, - { - "metadata": { - "name": "permissionsFilterRemoveSubquery", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder", - "stage": "experimental", - "codeowner": "@grafana/grafana-backend-group" - } - }, - { - "metadata": { - "name": "externalCorePlugins", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Allow core plugins to be loaded as external", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend" - } - }, - { - "metadata": { - "name": "alertmanagerRemoteOnly", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Disable the internal Alertmanager and only use the external one defined.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "scopeFilters", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the use of scope filters in Grafana", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "queryLibrary", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables Query Library feature in Explore", - "stage": "experimental", - "codeowner": "@grafana/explore-squad" - } - }, - { - "metadata": { - "name": "alertingCentralAlertHistory", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the new central alert history.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "lokiPredefinedOperations", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Adds predefined query operations to Loki query editor", - "stage": "experimental", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "kubernetesPlaylists", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s", - "stage": "GA", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "queryServiceRewrite", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Rewrite requests targeting /ds/query to the query service", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "tlsMemcached", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Use TLS-enabled memcached in the enterprise caching feature", - "stage": "experimental", - "codeowner": "@grafana/grafana-operator-experience-squad", - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "annotationPermissionUpdate", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Change the way annotation permissions work by scoping them to folders and dashboards.", - "stage": "GA", - "codeowner": "@grafana/identity-access-team" - } - }, - { - "metadata": { - "name": "onPremToCloudMigrations", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "In-development feature that will allow users to easily migrate their on-prem Grafana instances to Grafana Cloud.", - "stage": "experimental", - "codeowner": "@grafana/grafana-operator-experience-squad" - } - }, - { - "metadata": { - "name": "alertingListViewV2", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the new alert list view design", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "dashboardRestore", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables deleted dashboard restore feature", - "stage": "experimental", - "codeowner": "@grafana/grafana-frontend-platform", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "unifiedRequestLog", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Writes error logs to the request logger", - "stage": "experimental", - "codeowner": "@grafana/grafana-backend-group" - } - }, - { - "metadata": { - "name": "logsExploreTableVisualisation", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "A table visualisation for logs in Explore", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "lokiRunQueriesInParallel", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables running Loki queries in parallel", - "stage": "privatePreview", - "codeowner": "@grafana/observability-logs" - } - }, - { - "metadata": { - "name": "prometheusPromQAIL", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Prometheus and AI/ML to assist users in creating a query", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics", - "frontend": true - } - }, - { - "metadata": { - "name": "alertingSimplifiedRouting", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables users to easily configure alert notifications by specifying a contact point directly when editing or creating an alert rule", - "stage": "GA", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "cloudWatchNewLabelParsing", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Updates CloudWatch label parsing to be more accurate", - "stage": "GA", - "codeowner": "@grafana/aws-datasources" - } - }, - { - "metadata": { - "name": "publicDashboardsScene", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables public dashboard rendering using scenes", - "stage": "experimental", - "codeowner": "@grafana/sharing-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "editPanelCSVDragAndDrop", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables drag and drop for CSV and Excel files", - "stage": "experimental", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "individualCookiePreferences", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Support overriding cookie preferences per user", - "stage": "experimental", - "codeowner": "@grafana/grafana-backend-group" - } - }, - { - "metadata": { - "name": "alertStateHistoryLokiSecondary", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable Grafana to write alert state history to an external Loki instance in addition to Grafana annotations.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "correlations", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Correlations page", - "stage": "GA", - "codeowner": "@grafana/explore-squad", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "awsDatasourcesTempCredentials", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Support temporary security credentials in AWS plugins for Grafana Cloud customers", - "stage": "experimental", - "codeowner": "@grafana/aws-datasources" - } - }, - { - "metadata": { - "name": "angularDeprecationUI", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Display Angular warnings in dashboards and panels", - "stage": "GA", - "codeowner": "@grafana/plugins-platform-backend", - "frontend": true - } - }, - { - "metadata": { - "name": "ssoSettingsApi", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the SSO settings API and the OAuth configuration UIs in Grafana", - "stage": "GA", - "codeowner": "@grafana/identity-access-team", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "jitterAlertRulesWithinGroups", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Distributes alert rule evaluations more evenly over time, including spreading out rules within the same group", - "stage": "preview", - "codeowner": "@grafana/alerting-squad", - "requiresRestart": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "grafanaManagedRecordingRules", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables Grafana-managed recording rules.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "queryOverLive", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Use Grafana Live WebSocket to execute backend queries", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "autoMigrateStatPanel", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "grafanaAPIServerEnsureKubectlAccess", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Start an additional https handler and write kubectl options", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresDevMode": true, - "requiresRestart": true - } - }, - { - "metadata": { - "name": "dashboardScene", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables dashboard rendering using scenes for all roles", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "managedPluginsInstall", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Install managed plugins directly from plugins catalog", - "stage": "GA", - "codeowner": "@grafana/plugins-platform-backend" - } - }, - { - "metadata": { - "name": "nodeGraphDotLayout", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Changed the layout algorithm for the node graph", - "stage": "experimental", - "codeowner": "@grafana/observability-traces-and-profiling", - "frontend": true - } - }, - { - "metadata": { - "name": "expressionParser", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable new expression parser", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "logsExploreTableDefaultVisualization", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Sets the logs table as default visualisation in logs explore", - "stage": "experimental", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "autoMigrateXYChartPanel", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Migrate old XYChart panel to new XYChart2 model", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "canvasPanelNesting", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Allow elements nesting", - "stage": "experimental", - "codeowner": "@grafana/dataviz-squad", - "frontend": true, - "hideFromAdminPage": true - } - }, { "metadata": { "name": "accessControlOnCall", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2022-10-19T16:10:09Z" }, "spec": { "description": "Access control primitives for OnCall", @@ -863,129 +30,25 @@ }, { "metadata": { - "name": "sqlDatasourceDatabaseSelection", + "name": "addFieldFromCalculationStatFunctions", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-11-03T14:39:58Z" }, "spec": { - "description": "Enables previous SQL data source dataset dropdown behavior", + "description": "Add cumulative and window functions to the add field from calculation transformation", "stage": "preview", "codeowner": "@grafana/dataviz-squad", - "frontend": true, - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "newDashboardSharingComponent", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the new sharing drawer design", - "stage": "experimental", - "codeowner": "@grafana/sharing-squad", "frontend": true } }, { "metadata": { - "name": "cloudWatchBatchQueries", + "name": "aiGeneratedDashboardChanges", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-03-05T12:01:31Z" }, "spec": { - "description": "Runs CloudWatch metrics queries as separate batches", - "stage": "preview", - "codeowner": "@grafana/aws-datasources" - } - }, - { - "metadata": { - "name": "awsDatasourcesNewFormStyling", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Applies new form styling for configuration and query editors in AWS plugins", - "stage": "GA", - "codeowner": "@grafana/aws-datasources", - "frontend": true - } - }, - { - "metadata": { - "name": "logRowsPopoverMenu", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable filtering menu displayed when text of a log line is selected", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "lokiQueryHints", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables query hints for Loki", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "publicDashboards", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "[Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version.", - "stage": "GA", - "codeowner": "@grafana/sharing-squad", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "nestedFolders", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable folder nesting", - "stage": "GA", - "codeowner": "@grafana/search-and-storage" - } - }, - { - "metadata": { - "name": "prometheusMetricEncyclopedia", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Adds the metrics explorer component to the Prometheus query builder as an option in metric select", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "vizAndWidgetSplit", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Split panels between visualizations and widgets", + "description": "Enable AI powered features for dashboards to auto-summary changes when saving", "stage": "experimental", "codeowner": "@grafana/dashboards-squad", "frontend": true @@ -993,176 +56,70 @@ }, { "metadata": { - "name": "ssoSettingsSAML", + "name": "alertStateHistoryLokiOnly", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-03-30T18:53:21Z" }, "spec": { - "description": "Use the new SSO Settings API to configure the SAML connector", - "stage": "preview", - "codeowner": "@grafana/identity-access-team", - "allowSelfServe": true + "description": "Disable Grafana alerts from emitting annotations when a remote Loki instance is available.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" } }, { "metadata": { - "name": "pluginsFrontendSandbox", + "name": "alertStateHistoryLokiPrimary", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-03-30T18:53:21Z" }, "spec": { - "description": "Enables the plugins frontend sandbox", + "description": "Enable a remote Loki instance as the primary source for state history reads.", "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "alertStateHistoryLokiSecondary", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-03-30T18:53:21Z" + }, + "spec": { + "description": "Enable Grafana to write alert state history to an external Loki instance in addition to Grafana annotations.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "alertingBacktesting", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-12-14T14:44:14Z" + }, + "spec": { + "description": "Rule backtesting API for alerting", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "alertingCentralAlertHistory", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-29T15:01:38Z" + }, + "spec": { + "description": "Enables the new central alert history.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad", "frontend": true } }, - { - "metadata": { - "name": "prometheusIncrementalQueryInstrumentation", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Adds RudderStack events to incremental queries", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics", - "frontend": true - } - }, - { - "metadata": { - "name": "lokiMetricDataplane", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Changes metric responses from Loki to be compliant with the dataplane specification.", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "kubernetesSnapshots", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Routes snapshot requests from /api to the /apis endpoint", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "sqlExpressions", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables using SQL and DuckDB functions as Expressions.", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad" - } - }, - { - "metadata": { - "name": "prometheusConfigOverhaulAuth", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Update the Prometheus configuration page with the new auth component", - "stage": "GA", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "formatString", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable format string transformer", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "queryServiceFromUI", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Routes requests to the new query service", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "tableSharedCrosshair", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables shared crosshair in table panel", - "stage": "experimental", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "dataplaneFrontendFallback", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Support dataplane contract field name change for transformations and field name matchers where the name is different", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "pluginsAPIMetrics", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Sends metrics of public grafana packages usage by plugins", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend", - "frontend": true - } - }, - { - "metadata": { - "name": "cachingOptimizeSerializationMemoryUsage", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses.", - "stage": "experimental", - "codeowner": "@grafana/grafana-operator-experience-squad" - } - }, { "metadata": { "name": "alertingDisableSendAlertsExternal", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-05-23T12:29:19Z" }, "spec": { "description": "Disables the ability to send alerts to an external Alertmanager datasource.", @@ -1172,270 +129,11 @@ "hideFromDocs": true } }, - { - "metadata": { - "name": "publicDashboardsEmailSharing", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables public dashboard sharing to be restricted to only allowed emails", - "stage": "preview", - "codeowner": "@grafana/sharing-squad", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "disableSSEDataplane", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Disables dataplane specific processing in server side expressions.", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "dashboardSceneSolo", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables rendering dashboards using scenes for solo panels", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "reportingRetries", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables rendering retries for the reporting feature", - "stage": "preview", - "codeowner": "@grafana/sharing-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "canvasPanelPanZoom", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Allow pan and zoom in canvas panel", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "oauthRequireSubClaim", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Require that sub claims is present in oauth tokens.", - "stage": "experimental", - "codeowner": "@grafana/identity-access-team", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "datasourceQueryMultiStatus", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Introduce HTTP 207 Multi Status for api/ds/query", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend" - } - }, - { - "metadata": { - "name": "alertingNoNormalState", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Stop maintaining state of alerts that are not firing", - "stage": "preview", - "codeowner": "@grafana/alerting-squad", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "mlExpressions", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable support for Machine Learning in server-side expressions", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "grafanaAPIServerWithExperimentalAPIs", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Register experimental APIs with the k8s API server", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresDevMode": true, - "requiresRestart": true - } - }, - { - "metadata": { - "name": "betterPageScrolling", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Removes CustomScrollbar from the UI, relying on native browser scrollbars", - "stage": "GA", - "codeowner": "@grafana/grafana-frontend-platform", - "frontend": true - } - }, - { - "metadata": { - "name": "authAPIAccessTokenAuth", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the use of Auth API access tokens for authentication", - "stage": "experimental", - "codeowner": "@grafana/identity-access-team", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "influxqlStreamingParser", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable streaming JSON parser for InfluxDB datasource InfluxQL query language", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "alertStateHistoryLokiPrimary", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable a remote Loki instance as the primary source for state history reads.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "frontendSandboxMonitorOnly", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables monitor only in the plugin frontend sandbox (if enabled)", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend", - "frontend": true - } - }, - { - "metadata": { - "name": "alertingNoDataErrorExecution", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Changes how Alerting state manager handles execution of NoData/Error", - "stage": "GA", - "codeowner": "@grafana/alerting-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "flameGraphItemCollapsing", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Allow collapsing of flame graph items", - "stage": "experimental", - "codeowner": "@grafana/observability-traces-and-profiling", - "frontend": true - } - }, - { - "metadata": { - "name": "kubernetesFeatureToggles", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Use the kubernetes API for feature toggle management in the frontend", - "stage": "experimental", - "codeowner": "@grafana/grafana-operator-experience-squad", - "frontend": true, - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "autofixDSUID", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Automatically migrates invalid datasource UIDs", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend" - } - }, - { - "metadata": { - "name": "enableDatagridEditing", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the edit functionality in the datagrid panel", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, { "metadata": { "name": "alertingInsights", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-09-14T12:58:04Z" }, "spec": { "description": "Show the new alerting insights landing page", @@ -1447,254 +145,159 @@ }, { "metadata": { - "name": "enableNativeHTTPHistogram", + "name": "alertingListViewV2", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-05-24T14:40:49Z" }, "spec": { - "description": "Enables native HTTP Histograms", + "description": "Enables the new alert list view design", "stage": "experimental", - "codeowner": "@grafana/hosted-grafana-team" - } - }, - { - "metadata": { - "name": "addFieldFromCalculationStatFunctions", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Add cumulative and window functions to the add field from calculation transformation", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", + "codeowner": "@grafana/alerting-squad", "frontend": true } }, { "metadata": { - "name": "renderAuthJWT", + "name": "alertingNoDataErrorExecution", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-08-15T14:27:15Z" }, "spec": { - "description": "Uses JWT-based auth for rendering instead of relying on remote cache", + "description": "Changes how Alerting state manager handles execution of NoData/Error", + "stage": "GA", + "codeowner": "@grafana/alerting-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "alertingNoNormalState", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-01-13T23:29:29Z" + }, + "spec": { + "description": "Stop maintaining state of alerts that are not firing", "stage": "preview", - "codeowner": "@grafana/grafana-as-code", + "codeowner": "@grafana/alerting-squad", "hideFromAdminPage": true } }, { "metadata": { - "name": "refactorVariablesTimeRange", + "name": "alertingQueryOptimization", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-01-10T20:52:58Z" }, "spec": { - "description": "Refactor time range variables flow to reduce number of API calls made when query variables are chained", - "stage": "preview", - "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "awsAsyncQueryCaching", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled", + "description": "Optimizes eligible queries in order to reduce load on datasources", "stage": "GA", - "codeowner": "@grafana/aws-datasources" + "codeowner": "@grafana/alerting-squad" } }, { "metadata": { - "name": "aiGeneratedDashboardChanges", + "name": "alertingSaveStatePeriodic", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-01-23T16:03:30Z" }, "spec": { - "description": "Enable AI powered features for dashboards to auto-summary changes when saving", + "description": "Writes the state periodically to the database, asynchronous to rule evaluation", + "stage": "privatePreview", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "alertingSimplifiedRouting", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-10T13:14:39Z" + }, + "spec": { + "description": "Enables users to easily configure alert notifications by specifying a contact point directly when editing or creating an alert rule", + "stage": "GA", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "alertmanagerRemoteOnly", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-30T16:27:08Z" + }, + "spec": { + "description": "Disable the internal Alertmanager and only use the external one defined.", "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "alertmanagerRemotePrimary", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-30T16:27:08Z" + }, + "spec": { + "description": "Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "alertmanagerRemoteSecondary", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-30T16:27:08Z" + }, + "spec": { + "description": "Enable Grafana to sync configuration and state with a remote Alertmanager.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "angularDeprecationUI", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-08-29T14:05:47Z" + }, + "spec": { + "description": "Display Angular warnings in dashboards and panels", + "stage": "GA", + "codeowner": "@grafana/plugins-platform-backend", "frontend": true } }, { "metadata": { - "name": "cloudWatchCrossAccountQuerying", + "name": "annotationPermissionUpdate", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-10-31T13:30:13Z" }, "spec": { - "description": "Enables cross-account querying in CloudWatch datasources", + "description": "Change the way annotation permissions work by scoping them to folders and dashboards.", "stage": "GA", - "codeowner": "@grafana/aws-datasources", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "mysqlAnsiQuotes", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Use double quotes to escape keyword in a MySQL query", - "stage": "experimental", - "codeowner": "@grafana/search-and-storage" - } - }, - { - "metadata": { - "name": "lokiQuerySplitting", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Split large interval queries into subqueries with smaller time intervals", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "prometheusDataplane", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Changes responses to from Prometheus to be compliant with the dataplane specification. In particular, when this feature toggle is active, the numeric `Field.Name` is set from 'Value' to the value of the `__name__` label.", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "panelMonitoring", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables panel monitoring through logs and measurements", - "stage": "GA", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "teamHttpHeaders", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables Team LBAC for datasources to apply team headers to the client requests", - "stage": "preview", "codeowner": "@grafana/identity-access-team" } }, { "metadata": { - "name": "extractFieldsNameDeduplication", + "name": "authAPIAccessTokenAuth", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-04-02T15:45:15Z" }, "spec": { - "description": "Make sure extracted field names are unique in the dataframe", + "description": "Enables the use of Auth API access tokens for authentication", "stage": "experimental", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "newFolderPicker", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the nested folder picker without having nested folders enabled", - "stage": "experimental", - "codeowner": "@grafana/grafana-frontend-platform", - "frontend": true - } - }, - { - "metadata": { - "name": "preserveDashboardStateWhenNavigating", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables possibility to preserve dashboard variables and time range when navigating between dashboards", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", + "codeowner": "@grafana/identity-access-team", "hideFromAdminPage": true, "hideFromDocs": true } }, - { - "metadata": { - "name": "disableEnvelopeEncryption", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Disable envelope encryption (emergency only)", - "stage": "GA", - "codeowner": "@grafana/grafana-as-code", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "unifiedStorage", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "SQL-based k8s storage", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "logsInfiniteScrolling", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables infinite scrolling for the Logs panel in Explore and Dashboards", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "disableNumericMetricsSortingInExpressions", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "In server-side expressions, disable the sorting of numeric-kind metrics by their metric name or labels.", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics", - "requiresRestart": true - } - }, { "metadata": { "name": "autoMigrateGraphPanel", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-02-08T22:00:48Z" }, "spec": { "description": "Migrate old graph panel to supported time series panel - broken out from autoMigrateOldPanels to enable granular tracking", @@ -1705,25 +308,12 @@ }, { "metadata": { - "name": "dashgpt", + "name": "autoMigrateOldPanels", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-03-23T04:02:36Z" }, "spec": { - "description": "Enable AI powered features in dashboards", - "stage": "GA", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "groupToNestedTableTransformation", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the group to nested table transformation", + "description": "Migrate old angular panels to supported versions (graph, table-old, worldmap, etc)", "stage": "preview", "codeowner": "@grafana/dataviz-squad", "frontend": true @@ -1731,40 +321,27 @@ }, { "metadata": { - "name": "groupByVariable", + "name": "autoMigratePiechartPanel", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-02-14T16:06:25Z" }, "spec": { - "description": "Enable groupBy variable support in scenes dashboards", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true, - "hideFromDocs": true + "description": "Migrate old piechart panel to supported piechart panel - broken out from autoMigrateOldPanels to enable granular tracking", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true } }, { "metadata": { - "name": "alertmanagerRemotePrimary", + "name": "autoMigrateStatPanel", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-02-14T16:06:25Z" }, "spec": { - "description": "Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "exploreMetrics", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables the new Explore Metrics core app", - "stage": "GA", - "codeowner": "@grafana/dashboards-squad", + "description": "Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", "frontend": true } }, @@ -1772,7 +349,7 @@ "metadata": { "name": "autoMigrateTablePanel", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-02-14T16:06:25Z" }, "spec": { "description": "Migrate old table panel to supported table panel - broken out from autoMigrateOldPanels to enable granular tracking", @@ -1781,11 +358,345 @@ "frontend": true } }, + { + "metadata": { + "name": "autoMigrateWorldmapPanel", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-02-14T16:06:25Z" + }, + "spec": { + "description": "Migrate old worldmap panel to supported geomap panel - broken out from autoMigrateOldPanels to enable granular tracking", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "autoMigrateXYChartPanel", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-03-22T15:44:37Z" + }, + "spec": { + "description": "Migrate old XYChart panel to new XYChart2 model", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "autofixDSUID", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-03T11:32:07Z" + }, + "spec": { + "description": "Automatically migrates invalid datasource UIDs", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "awsAsyncQueryCaching", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-21T15:34:07Z" + }, + "spec": { + "description": "Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled", + "stage": "GA", + "codeowner": "@grafana/aws-datasources" + } + }, + { + "metadata": { + "name": "awsDatasourcesNewFormStyling", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-12T08:59:10Z" + }, + "spec": { + "description": "Applies new form styling for configuration and query editors in AWS plugins", + "stage": "GA", + "codeowner": "@grafana/aws-datasources", + "frontend": true + } + }, + { + "metadata": { + "name": "awsDatasourcesTempCredentials", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-06T15:06:11Z" + }, + "spec": { + "description": "Support temporary security credentials in AWS plugins for Grafana Cloud customers", + "stage": "experimental", + "codeowner": "@grafana/aws-datasources" + } + }, + { + "metadata": { + "name": "azureMonitorPrometheusExemplars", + "resourceVersion": "1717667267324", + "creationTimestamp": "2024-06-06T16:53:17Z" + }, + "spec": { + "description": "Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars", + "stage": "experimental", + "codeowner": "@grafana/partner-datasources" + } + }, + { + "metadata": { + "name": "betterPageScrolling", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-03-06T15:06:47Z" + }, + "spec": { + "description": "Removes CustomScrollbar from the UI, relying on native browser scrollbars", + "stage": "GA", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true + } + }, + { + "metadata": { + "name": "cachingOptimizeSerializationMemoryUsage", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-12T16:56:49Z" + }, + "spec": { + "description": "If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses.", + "stage": "experimental", + "codeowner": "@grafana/grafana-operator-experience-squad" + } + }, + { + "metadata": { + "name": "canvasPanelNesting", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-05-31T19:03:34Z" + }, + "spec": { + "description": "Allow elements nesting", + "stage": "experimental", + "codeowner": "@grafana/dataviz-squad", + "frontend": true, + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "canvasPanelPanZoom", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-01-02T19:52:21Z" + }, + "spec": { + "description": "Allow pan and zoom in canvas panel", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "cloudRBACRoles", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-01-10T13:19:01Z" + }, + "spec": { + "description": "Enabled grafana cloud specific RBAC roles", + "stage": "experimental", + "codeowner": "@grafana/identity-access-team", + "requiresRestart": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "cloudWatchBatchQueries", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-20T19:09:41Z" + }, + "spec": { + "description": "Runs CloudWatch metrics queries as separate batches", + "stage": "preview", + "codeowner": "@grafana/aws-datasources" + } + }, + { + "metadata": { + "name": "cloudWatchCrossAccountQuerying", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-11-28T11:39:12Z" + }, + "spec": { + "description": "Enables cross-account querying in CloudWatch datasources", + "stage": "GA", + "codeowner": "@grafana/aws-datasources", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "cloudWatchNewLabelParsing", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-05T15:57:56Z" + }, + "spec": { + "description": "Updates CloudWatch label parsing to be more accurate", + "stage": "GA", + "codeowner": "@grafana/aws-datasources" + } + }, + { + "metadata": { + "name": "configurableSchedulerTick", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-26T16:44:12Z" + }, + "spec": { + "description": "Enable changing the scheduler base interval via configuration option unified_alerting.scheduler_tick_interval", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad", + "requiresRestart": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "correlations", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-09-16T13:14:27Z" + }, + "spec": { + "description": "Correlations page", + "stage": "GA", + "codeowner": "@grafana/explore-squad", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "dashboardRestore", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-16T17:36:26Z" + }, + "spec": { + "description": "Enables deleted dashboard restore feature", + "stage": "experimental", + "codeowner": "@grafana/grafana-frontend-platform", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "dashboardScene", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-13T08:51:21Z" + }, + "spec": { + "description": "Enables dashboard rendering using scenes for all roles", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "dashboardSceneForViewers", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-02T19:02:25Z" + }, + "spec": { + "description": "Enables dashboard rendering using Scenes for viewer roles", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "dashboardSceneSolo", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-02-11T08:08:47Z" + }, + "spec": { + "description": "Enables rendering dashboards using scenes for solo panels", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "dashgpt", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-08-30T20:22:05Z" + }, + "spec": { + "description": "Enable AI powered features in dashboards", + "stage": "GA", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "dataplaneFrontendFallback", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-04-07T21:13:19Z" + }, + "spec": { + "description": "Support dataplane contract field name change for transformations and field name matchers where the name is different", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "datasourceProxyDisableRBAC", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-21T13:05:16Z" + }, + "spec": { + "description": "Disables applying a plugin route's ReqAction field to authorization", + "stage": "GA", + "codeowner": "@grafana/identity-access-team", + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "datasourceQueryMultiStatus", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-05-03T16:02:20Z" + }, + "spec": { + "description": "Introduce HTTP 207 Multi Status for api/ds/query", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "datasourceQueryTypes", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-23T16:46:28Z" + }, + "spec": { + "description": "Show query type endpoints in datasource API servers (currently hardcoded for testdata, expressions, and prometheus)", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, { "metadata": { "name": "disableAngular", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-03-23T15:43:45Z" }, "spec": { "description": "Dynamic flag to disable angular at runtime. The preferred method is to set `angular_support_enabled` to `false` in the [security] settings, which allows you to change the state at runtime.", @@ -1795,11 +706,231 @@ "hideFromAdminPage": true } }, + { + "metadata": { + "name": "disableEnvelopeEncryption", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-05-24T08:34:47Z" + }, + "spec": { + "description": "Disable envelope encryption (emergency only)", + "stage": "GA", + "codeowner": "@grafana/grafana-as-code", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "disableNumericMetricsSortingInExpressions", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-16T14:52:47Z" + }, + "spec": { + "description": "In server-side expressions, disable the sorting of numeric-kind metrics by their metric name or labels.", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "disableSSEDataplane", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-04-12T16:24:34Z" + }, + "spec": { + "description": "Disables dataplane specific processing in server side expressions.", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "disableSecretsCompatibility", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-07-12T20:27:37Z" + }, + "spec": { + "description": "Disable duplicated secret storage in legacy tables", + "stage": "experimental", + "codeowner": "@grafana/hosted-grafana-team", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "editPanelCSVDragAndDrop", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-01-24T09:43:44Z" + }, + "spec": { + "description": "Enables drag and drop for CSV and Excel files", + "stage": "experimental", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "enableDatagridEditing", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-04-24T14:46:31Z" + }, + "spec": { + "description": "Enables the edit functionality in the datagrid panel", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "enableElasticsearchBackendQuerying", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-04-14T09:24:35Z", + "deletionTimestamp": "2024-06-05T15:03:29Z" + }, + "spec": { + "description": "Enable the processing of queries and responses in the Elasticsearch data source through backend", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "enableNativeHTTPHistogram", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-03T18:23:55Z" + }, + "spec": { + "description": "Enables native HTTP Histograms", + "stage": "experimental", + "codeowner": "@grafana/hosted-grafana-team" + } + }, + { + "metadata": { + "name": "exploreContentOutline", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-13T16:57:13Z" + }, + "spec": { + "description": "Content outline sidebar", + "stage": "GA", + "codeowner": "@grafana/explore-squad", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "exploreMetrics", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-09T18:15:18Z" + }, + "spec": { + "description": "Enables the new Explore Metrics core app", + "stage": "GA", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "expressionParser", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-02-17T00:59:11Z" + }, + "spec": { + "description": "Enable new expression parser", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "externalCorePlugins", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-09-22T08:50:13Z" + }, + "spec": { + "description": "Allow core plugins to be loaded as external", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "externalServiceAccounts", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-09-28T07:26:37Z" + }, + "spec": { + "description": "Automatic service account and token setup for plugins", + "stage": "preview", + "codeowner": "@grafana/identity-access-team", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "extraThemes", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-05-10T13:37:04Z" + }, + "spec": { + "description": "Enables extra themes", + "stage": "experimental", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true + } + }, + { + "metadata": { + "name": "extractFieldsNameDeduplication", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-02T15:47:42Z" + }, + "spec": { + "description": "Make sure extracted field names are unique in the dataframe", + "stage": "experimental", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "faroDatasourceSelector", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-05-05T00:35:10Z" + }, + "spec": { + "description": "Enable the data source selector within the Frontend Apps section of the Frontend Observability", + "stage": "preview", + "codeowner": "@grafana/app-o11y", + "frontend": true + } + }, + { + "metadata": { + "name": "featureHighlights", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-02-03T11:53:23Z" + }, + "spec": { + "description": "Highlight Grafana Enterprise features", + "stage": "GA", + "codeowner": "@grafana/grafana-as-code", + "allowSelfServe": true + } + }, { "metadata": { "name": "featureToggleAdminPage", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-07-18T20:43:32Z" }, "spec": { "description": "Enable admin page for managing feature toggles from the Grafana front-end. Grafana Cloud only.", @@ -1811,139 +942,25 @@ }, { "metadata": { - "name": "idForwarding", + "name": "flameGraphItemCollapsing", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-11-09T14:31:07Z" }, "spec": { - "description": "Generate signed id token for identity that can be forwarded to plugins and external services", + "description": "Allow collapsing of flame graph items", "stage": "experimental", - "codeowner": "@grafana/identity-access-team" - } - }, - { - "metadata": { - "name": "lokiQuerySplittingConfig", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Give users the option to configure split durations for Loki queries", - "stage": "experimental", - "codeowner": "@grafana/observability-logs", + "codeowner": "@grafana/observability-traces-and-profiling", "frontend": true } }, { "metadata": { - "name": "alertingQueryOptimization", + "name": "formatString", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-10-13T18:17:12Z" }, "spec": { - "description": "Optimizes eligible queries in order to reduce load on datasources", - "stage": "GA", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "panelTitleSearch", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Search for dashboards using panel title", - "stage": "preview", - "codeowner": "@grafana/grafana-app-platform-squad", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "storage", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Configurable storage for dashboards, datasources, and resources", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad" - } - }, - { - "metadata": { - "name": "disableSecretsCompatibility", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Disable duplicated secret storage in legacy tables", - "stage": "experimental", - "codeowner": "@grafana/hosted-grafana-team", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "logsContextDatasourceUi", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Allow datasource to provide custom UI for context view", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "showDashboardValidationWarnings", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Show warnings when dashboards do not validate against the schema", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad" - } - }, - { - "metadata": { - "name": "recoveryThreshold", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables feature recovery threshold (aka hysteresis) for threshold server-side expression", - "stage": "GA", - "codeowner": "@grafana/alerting-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "prometheusCodeModeMetricNamesSearch", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables search for metric names in Code Mode, to improve performance when working with an enormous number of metric names", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics", - "frontend": true - } - }, - { - "metadata": { - "name": "regressionTransformation", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables regression analysis transformation", + "description": "Enable format string transformer", "stage": "preview", "codeowner": "@grafana/dataviz-squad", "frontend": true @@ -1951,107 +968,91 @@ }, { "metadata": { - "name": "logRequestsInstrumentedAsUnknown", + "name": "frontendSandboxMonitorOnly", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-07-05T11:48:25Z" }, "spec": { - "description": "Logs the path for requests that are instrumented as unknown", + "description": "Enables monitor only in the plugin frontend sandbox (if enabled)", "stage": "experimental", - "codeowner": "@grafana/hosted-grafana-team" + "codeowner": "@grafana/plugins-platform-backend", + "frontend": true } }, { "metadata": { - "name": "alertStateHistoryLokiOnly", + "name": "grafanaAPIServerEnsureKubectlAccess", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-12-06T20:21:21Z" }, "spec": { - "description": "Disable Grafana alerts from emitting annotations when a remote Loki instance is available.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "libraryPanelRBAC", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables RBAC support for library panels", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "pdfTables", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables generating table data as PDF in reporting", - "stage": "preview", - "codeowner": "@grafana/sharing-squad" - } - }, - { - "metadata": { - "name": "topnav", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enables topnav support in external plugins. The new Grafana navigation cannot be disabled.", - "stage": "deprecated", - "codeowner": "@grafana/grafana-frontend-platform" - } - }, - { - "metadata": { - "name": "alertingBacktesting", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Rule backtesting API for alerting", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "queryService", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Register /apis/query.grafana.app/ -- will eventually replace /api/ds/query", + "description": "Start an additional https handler and write kubectl options", "stage": "experimental", "codeowner": "@grafana/grafana-app-platform-squad", + "requiresDevMode": true, "requiresRestart": true } }, { "metadata": { - "name": "alertmanagerRemoteSecondary", + "name": "grafanaAPIServerWithExperimentalAPIs", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-10-06T18:55:22Z" }, "spec": { - "description": "Enable Grafana to sync configuration and state with a remote Alertmanager.", + "description": "Register experimental APIs with the k8s API server", "stage": "experimental", - "codeowner": "@grafana/alerting-squad" + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresDevMode": true, + "requiresRestart": true + } + }, + { + "metadata": { + "name": "grafanaManagedRecordingRules", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-22T17:53:16Z" + }, + "spec": { + "description": "Enables Grafana-managed recording rules.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "groupByVariable", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-02-14T17:18:04Z" + }, + "spec": { + "description": "Enable groupBy variable support in scenes dashboards", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "groupToNestedTableTransformation", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-02-07T14:28:26Z" + }, + "spec": { + "description": "Enables the group to nested table transformation", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true } }, { "metadata": { "name": "grpcServer", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2022-09-26T20:25:34Z" }, "spec": { "description": "Run the GRPC server", @@ -2062,86 +1063,34 @@ }, { "metadata": { - "name": "faroDatasourceSelector", + "name": "idForwarding", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-09-25T15:21:28Z" }, "spec": { - "description": "Enable the data source selector within the Frontend Apps section of the Frontend Observability", - "stage": "preview", - "codeowner": "@grafana/app-o11y", - "frontend": true - } - }, - { - "metadata": { - "name": "pluginsSkipHostEnvVars", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Disables passing host environment variable to plugin processes", + "description": "Generate signed id token for identity that can be forwarded to plugins and external services", "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend" + "codeowner": "@grafana/identity-access-team" } }, { "metadata": { - "name": "transformationsVariableSupport", + "name": "individualCookiePreferences", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-02-21T10:19:07Z" }, "spec": { - "description": "Allows using variables in transformations", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "panelTitleSearchInV1", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Enable searching for dashboards using panel title in search v1", + "description": "Support overriding cookie preferences per user", "stage": "experimental", - "codeowner": "@grafana/search-and-storage", - "requiresDevMode": true - } - }, - { - "metadata": { - "name": "featureHighlights", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Highlight Grafana Enterprise features", - "stage": "GA", - "codeowner": "@grafana/grafana-as-code", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "autoMigrateWorldmapPanel", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Migrate old worldmap panel to supported geomap panel - broken out from autoMigrateOldPanels to enable granular tracking", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true + "codeowner": "@grafana/grafana-backend-group" } }, { "metadata": { "name": "influxdbBackendMigration", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2022-02-09T18:26:16Z", + "deletionTimestamp": "2023-01-17T14:11:26Z" }, "spec": { "description": "Query InfluxDB InfluxQL without the proxy", @@ -2154,7 +1103,7 @@ "metadata": { "name": "influxdbRunQueriesInParallel", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-02-01T10:58:24Z" }, "spec": { "description": "Enables running InfluxDB Influxql queries in parallel", @@ -2164,73 +1113,48 @@ }, { "metadata": { - "name": "scenes", + "name": "influxqlStreamingParser", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2023-11-29T17:29:35Z" }, "spec": { - "description": "Experimental framework to build interactive dashboards", + "description": "Enable streaming JSON parser for InfluxDB datasource InfluxQL query language", "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true + "codeowner": "@grafana/observability-metrics" } }, { "metadata": { - "name": "enableElasticsearchBackendQuerying", + "name": "jitterAlertRulesWithinGroups", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z", - "deletionTimestamp": "2024-06-05T14:07:54Z" + "creationTimestamp": "2024-01-18T18:48:11Z" }, "spec": { - "description": "Enable the processing of queries and responses in the Elasticsearch data source through backend", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "externalServiceAccounts", - "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" - }, - "spec": { - "description": "Automatic service account and token setup for plugins", + "description": "Distributes alert rule evaluations more evenly over time, including spreading out rules within the same group", "stage": "preview", - "codeowner": "@grafana/identity-access-team", - "hideFromAdminPage": true + "codeowner": "@grafana/alerting-squad", + "requiresRestart": true, + "hideFromDocs": true } }, { "metadata": { - "name": "lokiStructuredMetadata", + "name": "kubernetesAggregator", "resourceVersion": "1717578796182", - "creationTimestamp": "2024-06-05T09:13:16Z" + "creationTimestamp": "2024-02-12T20:59:35Z" }, "spec": { - "description": "Enables the loki data source to request structured metadata from the Loki server", - "stage": "GA", - "codeowner": "@grafana/observability-logs" - } - }, - { - "metadata": { - "name": "pluginProxyPreserveTrailingSlash", - "resourceVersion": "1717581171624", - "creationTimestamp": "2024-06-05T09:52:51Z" - }, - "spec": { - "description": "Preserve plugin proxy trailing slash.", - "stage": "GA", - "codeowner": "@grafana/plugins-platform-backend" + "description": "Enable grafana aggregator", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true } }, { "metadata": { "name": "kubernetesDashboards", "resourceVersion": "1717593661635", - "creationTimestamp": "2024-06-05T13:21:01Z" + "creationTimestamp": "2024-06-05T14:34:23Z" }, "spec": { "description": "Use the kubernetes API in the frontend for dashboards", @@ -2241,14 +1165,1092 @@ }, { "metadata": { - "name": "azureMonitorPrometheusExemplars", - "resourceVersion": "1717667267324", - "creationTimestamp": "2024-06-06T09:47:47Z" + "name": "kubernetesFeatureToggles", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-01-18T05:32:44Z" }, "spec": { - "description": "Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars", + "description": "Use the kubernetes API for feature toggle management in the frontend", "stage": "experimental", - "codeowner": "@grafana/partner-datasources" + "codeowner": "@grafana/grafana-operator-experience-squad", + "frontend": true, + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "kubernetesPlaylists", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-05T19:00:36Z" + }, + "spec": { + "description": "Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s", + "stage": "GA", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "kubernetesSnapshots", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-12-05T22:31:49Z" + }, + "spec": { + "description": "Routes snapshot requests from /api to the /apis endpoint", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "libraryPanelRBAC", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-11T23:30:50Z" + }, + "spec": { + "description": "Enables RBAC support for library panels", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "live-service-web-worker", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-01-26T17:44:20Z" + }, + "spec": { + "description": "This will use a webworker thread to processes events rather than the main thread", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "logRequestsInstrumentedAsUnknown", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-06-10T08:56:55Z" + }, + "spec": { + "description": "Logs the path for requests that are instrumented as unknown", + "stage": "experimental", + "codeowner": "@grafana/hosted-grafana-team" + } + }, + { + "metadata": { + "name": "logRowsPopoverMenu", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-16T09:48:10Z" + }, + "spec": { + "description": "Enable filtering menu displayed when text of a log line is selected", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "logsContextDatasourceUi", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-01-27T14:12:01Z" + }, + "spec": { + "description": "Allow datasource to provide custom UI for context view", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "logsExploreTableDefaultVisualization", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-02T15:28:15Z" + }, + "spec": { + "description": "Sets the logs table as default visualisation in logs explore", + "stage": "experimental", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "logsExploreTableVisualisation", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-12T13:52:42Z" + }, + "spec": { + "description": "A table visualisation for logs in Explore", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "logsInfiniteScrolling", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-09T10:54:03Z" + }, + "spec": { + "description": "Enables infinite scrolling for the Logs panel in Explore and Dashboards", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "lokiExperimentalStreaming", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-06-19T10:03:51Z" + }, + "spec": { + "description": "Support new streaming approach for loki (prototype, needs special loki build)", + "stage": "experimental", + "codeowner": "@grafana/observability-logs" + } + }, + { + "metadata": { + "name": "lokiLogsDataplane", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-13T07:58:00Z" + }, + "spec": { + "description": "Changes logs responses from Loki to be compliant with the dataplane specification.", + "stage": "experimental", + "codeowner": "@grafana/observability-logs" + } + }, + { + "metadata": { + "name": "lokiMetricDataplane", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-04-13T13:07:08Z" + }, + "spec": { + "description": "Changes metric responses from Loki to be compliant with the dataplane specification.", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "lokiPredefinedOperations", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-06-02T10:52:36Z" + }, + "spec": { + "description": "Adds predefined query operations to Loki query editor", + "stage": "experimental", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "lokiQueryHints", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-12-18T20:43:16Z" + }, + "spec": { + "description": "Enables query hints for Loki", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "lokiQuerySplitting", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-02-09T17:27:02Z" + }, + "spec": { + "description": "Split large interval queries into subqueries with smaller time intervals", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "lokiQuerySplittingConfig", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-03-20T15:51:36Z" + }, + "spec": { + "description": "Give users the option to configure split durations for Loki queries", + "stage": "experimental", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "lokiRunQueriesInParallel", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-09-19T09:34:01Z" + }, + "spec": { + "description": "Enables running Loki queries in parallel", + "stage": "privatePreview", + "codeowner": "@grafana/observability-logs" + } + }, + { + "metadata": { + "name": "lokiStructuredMetadata", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-16T16:06:14Z" + }, + "spec": { + "description": "Enables the loki data source to request structured metadata from the Loki server", + "stage": "GA", + "codeowner": "@grafana/observability-logs" + } + }, + { + "metadata": { + "name": "managedPluginsInstall", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-18T13:17:03Z" + }, + "spec": { + "description": "Install managed plugins directly from plugins catalog", + "stage": "GA", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "metricsSummary", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-08-28T14:02:12Z" + }, + "spec": { + "description": "Enables metrics summary queries in the Tempo data source", + "stage": "experimental", + "codeowner": "@grafana/observability-traces-and-profiling", + "frontend": true + } + }, + { + "metadata": { + "name": "mlExpressions", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-13T17:37:50Z" + }, + "spec": { + "description": "Enable support for Machine Learning in server-side expressions", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "mysqlAnsiQuotes", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-10-12T11:43:35Z" + }, + "spec": { + "description": "Use double quotes to escape keyword in a MySQL query", + "stage": "experimental", + "codeowner": "@grafana/search-and-storage" + } + }, + { + "metadata": { + "name": "nestedFolders", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-10-26T14:15:14Z" + }, + "spec": { + "description": "Enable folder nesting", + "stage": "GA", + "codeowner": "@grafana/search-and-storage" + } + }, + { + "metadata": { + "name": "newDashboardSharingComponent", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-03T15:02:18Z" + }, + "spec": { + "description": "Enables the new sharing drawer design", + "stage": "experimental", + "codeowner": "@grafana/sharing-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "newDashboardWithFiltersAndGroupBy", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-04T11:25:21Z" + }, + "spec": { + "description": "Enables filters and group by variables on all new dashboards. Variables are added only if default data source supports filtering.", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "newFolderPicker", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-01-15T11:43:19Z" + }, + "spec": { + "description": "Enables the nested folder picker without having nested folders enabled", + "stage": "experimental", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true + } + }, + { + "metadata": { + "name": "newPDFRendering", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-02-08T12:09:34Z" + }, + "spec": { + "description": "New implementation for the dashboard-to-PDF rendering", + "stage": "preview", + "codeowner": "@grafana/sharing-squad" + } + }, + { + "metadata": { + "name": "nodeGraphDotLayout", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-01-31T16:26:12Z" + }, + "spec": { + "description": "Changed the layout algorithm for the node graph", + "stage": "experimental", + "codeowner": "@grafana/observability-traces-and-profiling", + "frontend": true + } + }, + { + "metadata": { + "name": "notificationBanner", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-13T09:32:34Z" + }, + "spec": { + "description": "Enables the notification banner UI and API", + "stage": "experimental", + "codeowner": "@grafana/grafana-frontend-platform" + } + }, + { + "metadata": { + "name": "oauthRequireSubClaim", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-03-25T13:22:24Z" + }, + "spec": { + "description": "Require that sub claims is present in oauth tokens.", + "stage": "experimental", + "codeowner": "@grafana/identity-access-team", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "onPremToCloudMigrations", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-01-22T16:09:08Z" + }, + "spec": { + "description": "In-development feature that will allow users to easily migrate their on-prem Grafana instances to Grafana Cloud.", + "stage": "experimental", + "codeowner": "@grafana/grafana-operator-experience-squad" + } + }, + { + "metadata": { + "name": "panelFilterVariable", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-03T12:15:54Z" + }, + "spec": { + "description": "Enables use of the `systemPanelFilterVar` variable to filter panels in a dashboard", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "panelMonitoring", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-09T05:19:08Z" + }, + "spec": { + "description": "Enables panel monitoring through logs and measurements", + "stage": "GA", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "panelTitleSearch", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-02-15T18:26:03Z" + }, + "spec": { + "description": "Search for dashboards using panel title", + "stage": "preview", + "codeowner": "@grafana/grafana-app-platform-squad", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "panelTitleSearchInV1", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-13T12:04:24Z" + }, + "spec": { + "description": "Enable searching for dashboards using panel title in search v1", + "stage": "experimental", + "codeowner": "@grafana/search-and-storage", + "requiresDevMode": true + } + }, + { + "metadata": { + "name": "pdfTables", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-06T13:39:22Z" + }, + "spec": { + "description": "Enables generating table data as PDF in reporting", + "stage": "preview", + "codeowner": "@grafana/sharing-squad" + } + }, + { + "metadata": { + "name": "permissionsFilterRemoveSubquery", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-08-02T07:39:25Z" + }, + "spec": { + "description": "Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder", + "stage": "experimental", + "codeowner": "@grafana/grafana-backend-group" + } + }, + { + "metadata": { + "name": "pluginProxyPreserveTrailingSlash", + "resourceVersion": "1717581171624", + "creationTimestamp": "2024-06-05T11:36:14Z" + }, + "spec": { + "description": "Preserve plugin proxy trailing slash.", + "stage": "GA", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "pluginsAPIMetrics", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-09-21T11:36:32Z" + }, + "spec": { + "description": "Sends metrics of public grafana packages usage by plugins", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend", + "frontend": true + } + }, + { + "metadata": { + "name": "pluginsFrontendSandbox", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-06-05T08:51:36Z" + }, + "spec": { + "description": "Enables the plugins frontend sandbox", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend", + "frontend": true + } + }, + { + "metadata": { + "name": "pluginsSkipHostEnvVars", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-15T17:09:14Z" + }, + "spec": { + "description": "Disables passing host environment variable to plugin processes", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "preserveDashboardStateWhenNavigating", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-27T12:28:06Z" + }, + "spec": { + "description": "Enables possibility to preserve dashboard variables and time range when navigating between dashboards", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "promQLScope", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-01-29T20:22:17Z" + }, + "spec": { + "description": "In-development feature that will allow injection of labels into prometheus queries.", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "prometheusCodeModeMetricNamesSearch", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-04T20:38:23Z" + }, + "spec": { + "description": "Enables search for metric names in Code Mode, to improve performance when working with an enormous number of metric names", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics", + "frontend": true + } + }, + { + "metadata": { + "name": "prometheusConfigOverhaulAuth", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-26T16:09:53Z" + }, + "spec": { + "description": "Update the Prometheus configuration page with the new auth component", + "stage": "GA", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "prometheusDataplane", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-03-29T15:26:32Z" + }, + "spec": { + "description": "Changes responses to from Prometheus to be compliant with the dataplane specification. In particular, when this feature toggle is active, the numeric `Field.Name` is set from 'Value' to the value of the `__name__` label.", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "prometheusIncrementalQueryInstrumentation", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-05T19:39:49Z" + }, + "spec": { + "description": "Adds RudderStack events to incremental queries", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics", + "frontend": true + } + }, + { + "metadata": { + "name": "prometheusMetricEncyclopedia", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-03-07T18:41:05Z" + }, + "spec": { + "description": "Adds the metrics explorer component to the Prometheus query builder as an option in metric select", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "prometheusPromQAIL", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-19T15:45:32Z" + }, + "spec": { + "description": "Prometheus and AI/ML to assist users in creating a query", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics", + "frontend": true + } + }, + { + "metadata": { + "name": "publicDashboards", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-04-07T18:30:19Z" + }, + "spec": { + "description": "[Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version.", + "stage": "GA", + "codeowner": "@grafana/sharing-squad", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "publicDashboardsEmailSharing", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-01-03T19:45:15Z" + }, + "spec": { + "description": "Enables public dashboard sharing to be restricted to only allowed emails", + "stage": "preview", + "codeowner": "@grafana/sharing-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "publicDashboardsScene", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-03-22T14:48:21Z" + }, + "spec": { + "description": "Enables public dashboard rendering using scenes", + "stage": "experimental", + "codeowner": "@grafana/sharing-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "queryLibrary", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-10-07T18:31:45Z", + "deletionTimestamp": "2023-03-20T16:00:14Z" + }, + "spec": { + "description": "Enables Query Library feature in Explore", + "stage": "experimental", + "codeowner": "@grafana/explore-squad" + } + }, + { + "metadata": { + "name": "queryOverLive", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-01-26T17:44:20Z" + }, + "spec": { + "description": "Use Grafana Live WebSocket to execute backend queries", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "queryService", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-19T09:26:21Z" + }, + "spec": { + "description": "Register /apis/query.grafana.app/ -- will eventually replace /api/ds/query", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "queryServiceFromUI", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-19T09:26:21Z" + }, + "spec": { + "description": "Routes requests to the new query service", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "queryServiceRewrite", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-04-19T09:26:21Z" + }, + "spec": { + "description": "Rewrite requests targeting /ds/query to the query service", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "recordedQueriesMulti", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-06-14T12:34:22Z" + }, + "spec": { + "description": "Enables writing multiple items from a single query within Recorded Queries", + "stage": "GA", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "recoveryThreshold", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-10T14:51:50Z" + }, + "spec": { + "description": "Enables feature recovery threshold (aka hysteresis) for threshold server-side expression", + "stage": "GA", + "codeowner": "@grafana/alerting-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "refactorVariablesTimeRange", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-06-06T13:12:09Z" + }, + "spec": { + "description": "Refactor time range variables flow to reduce number of API calls made when query variables are chained", + "stage": "preview", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "regressionTransformation", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-24T14:49:16Z" + }, + "spec": { + "description": "Enables regression analysis transformation", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "renderAuthJWT", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-04-03T16:53:38Z" + }, + "spec": { + "description": "Uses JWT-based auth for rendering instead of relying on remote cache", + "stage": "preview", + "codeowner": "@grafana/grafana-as-code", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "reportingRetries", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-08-31T07:47:47Z" + }, + "spec": { + "description": "Enables rendering retries for the reporting feature", + "stage": "preview", + "codeowner": "@grafana/sharing-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "scenes", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-07-07T06:53:02Z" + }, + "spec": { + "description": "Experimental framework to build interactive dashboards", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "scopeFilters", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-03-05T15:41:19Z" + }, + "spec": { + "description": "Enables the use of scope filters in Grafana", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "showDashboardValidationWarnings", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-10-14T13:51:05Z" + }, + "spec": { + "description": "Show warnings when dashboards do not validate against the schema", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad" + } + }, + { + "metadata": { + "name": "sqlDatasourceDatabaseSelection", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-06-06T16:28:52Z" + }, + "spec": { + "description": "Enables previous SQL data source dataset dropdown behavior", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true, + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "sqlExpressions", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-02-27T21:16:00Z" + }, + "spec": { + "description": "Enables using SQL and DuckDB functions as Expressions.", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad" + } + }, + { + "metadata": { + "name": "sseGroupByDatasource", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-09-07T20:02:07Z" + }, + "spec": { + "description": "Send query to the same datasource in a single request when using server side expressions. The `cloudWatchBatchQueries` feature toggle should be enabled if this used with CloudWatch.", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "ssoSettingsApi", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-11-08T09:50:01Z" + }, + "spec": { + "description": "Enables the SSO settings API and the OAuth configuration UIs in Grafana", + "stage": "GA", + "codeowner": "@grafana/identity-access-team", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "ssoSettingsSAML", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-03-14T11:04:45Z" + }, + "spec": { + "description": "Use the new SSO Settings API to configure the SAML connector", + "stage": "preview", + "codeowner": "@grafana/identity-access-team", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "storage", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-03-17T17:19:23Z" + }, + "spec": { + "description": "Configurable storage for dashboards, datasources, and resources", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad" + } + }, + { + "metadata": { + "name": "tableSharedCrosshair", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-12-13T09:33:14Z" + }, + "spec": { + "description": "Enables shared crosshair in table panel", + "stage": "experimental", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "teamHttpHeaders", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-17T10:23:54Z" + }, + "spec": { + "description": "Enables Team LBAC for datasources to apply team headers to the client requests", + "stage": "preview", + "codeowner": "@grafana/identity-access-team" + } + }, + { + "metadata": { + "name": "tlsMemcached", + "resourceVersion": "1717578796182", + "creationTimestamp": "2024-05-09T19:12:08Z" + }, + "spec": { + "description": "Use TLS-enabled memcached in the enterprise caching feature", + "stage": "experimental", + "codeowner": "@grafana/grafana-operator-experience-squad", + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "topnav", + "resourceVersion": "1717578796182", + "creationTimestamp": "2022-06-20T14:25:43Z" + }, + "spec": { + "description": "Enables topnav support in external plugins. The new Grafana navigation cannot be disabled.", + "stage": "deprecated", + "codeowner": "@grafana/grafana-frontend-platform" + } + }, + { + "metadata": { + "name": "traceQLStreaming", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-26T13:33:16Z" + }, + "spec": { + "description": "Enables response streaming of TraceQL queries of the Tempo data source", + "stage": "GA", + "codeowner": "@grafana/observability-traces-and-profiling", + "frontend": true + } + }, + { + "metadata": { + "name": "transformationsRedesign", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-07-12T16:35:49Z" + }, + "spec": { + "description": "Enables the transformations redesign", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "transformationsVariableSupport", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-10-04T14:28:46Z" + }, + "spec": { + "description": "Allows using variables in transformations", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "unifiedRequestLog", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-03-31T13:38:09Z" + }, + "spec": { + "description": "Writes error logs to the request logger", + "stage": "experimental", + "codeowner": "@grafana/grafana-backend-group" + } + }, + { + "metadata": { + "name": "unifiedStorage", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-12-06T20:21:21Z" + }, + "spec": { + "description": "SQL-based k8s storage", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "vizAndWidgetSplit", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-06-27T10:22:13Z" + }, + "spec": { + "description": "Split panels between visualizations and widgets", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "wargamesTesting", + "resourceVersion": "1717578796182", + "creationTimestamp": "2023-09-13T18:32:01Z" + }, + "spec": { + "description": "Placeholder feature flag for internal testing", + "stage": "experimental", + "codeowner": "@grafana/hosted-grafana-team" } }, { diff --git a/pkg/services/featuremgmt/toggles_gen_test.go b/pkg/services/featuremgmt/toggles_gen_test.go index ccadd1a46b3..b7ea5df8b7d 100644 --- a/pkg/services/featuremgmt/toggles_gen_test.go +++ b/pkg/services/featuremgmt/toggles_gen_test.go @@ -9,6 +9,7 @@ import ( "log" "os" "path/filepath" + "sort" "strconv" "strings" "testing" @@ -107,6 +108,25 @@ func TestFeatureToggleFiles(t *testing.T) { }) } + // Set the dates from git history + dates := readFlagDateInfo(t) + for idx, item := range current.Items { + found, ok := dates[item.Name] + if ok { + // current.Items[idx].ResourceVersion = fmt.Sprintf("%d", found.created.UnixMilli()+int64(idx)) + current.Items[idx].CreationTimestamp = v1.NewTime(found.created) + if found.deleted != nil { + tmp := v1.NewTime(*found.deleted) + current.Items[idx].DeletionTimestamp = &tmp + } + } + } + + // Sort by name -- will avoid more git conflicts + sort.Slice(current.Items, func(i, j int) bool { + return current.Items[i].Name < current.Items[j].Name + }) + out, err := json.MarshalIndent(current, "", " ") require.NoError(t, err) @@ -189,6 +209,38 @@ func verifyFlagsConfiguration(t *testing.T) { // acronyms can be configured as needed via `ConfigureAcronym` function from `./strcase/camel.go` } +type flagDateInfo struct { + created time.Time + deleted *time.Time +} + +// Load a cached copy of the feature toggle dates +func readFlagDateInfo(t *testing.T) map[string]flagDateInfo { + info := make(map[string]flagDateInfo, 300) + // This file is created by running the script in: + // https://github.com/grafana/grafana-enterprise/blob/ff-git-log-history/scripts/sidecar/main.go#L9 + body, err := os.ReadFile("toggles-gitlog.csv") + require.NoError(t, err) + reader := csv.NewReader(bytes.NewBuffer(body)) + rows, err := reader.ReadAll() + require.NoError(t, err) + for _, row := range rows { + if strings.HasPrefix(row[0], "#") { + continue + } + d := flagDateInfo{} + d.created, err = time.Parse(time.RFC3339, row[1]) + require.NoError(t, err) + if row[2] != "" { + tmp, err := time.Parse(time.RFC3339, row[2]) + require.NoError(t, err) + d.deleted = &tmp + } + info[row[0]] = d + } + return info +} + func verifyAndGenerateFile(t *testing.T, fpath string, gen string) { // nolint:gosec // We can ignore the gosec G304 warning since this is a test and the function is only called explicitly above From 404617b33d6ac7f46af64131962e954aa6e2fc98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=B1or=20Performo=20-=20Leandro=20Melendez?= <54183040+srperf@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:57:03 -0600 Subject: [PATCH 13/15] Added Grafana Play Links to Panel visualization docs (#88995) * Update index.md on Node panels to Grafana Play link * Update index.md add Traces docs link to Grafana Play * Update index.md News documentation link to Grafana Play * Update index.md Alert list documentation link to Grafana Play * Update index.md Alert list fixed missing link * Update index.md Fixed title * Update index.md Trying code fix * Ran prettier command * Matched link text to dashboard titles * Replaced URL with ref URI key --------- Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com> Co-authored-by: Isabel Matwawana --- .../panels-visualizations/visualizations/alert-list/index.md | 4 +++- .../panels-visualizations/visualizations/news/index.md | 2 ++ .../panels-visualizations/visualizations/node-graph/index.md | 2 ++ .../panels-visualizations/visualizations/traces/index.md | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/sources/panels-visualizations/visualizations/alert-list/index.md b/docs/sources/panels-visualizations/visualizations/alert-list/index.md index 0c71dc3e928..d40d3acf325 100644 --- a/docs/sources/panels-visualizations/visualizations/alert-list/index.md +++ b/docs/sources/panels-visualizations/visualizations/alert-list/index.md @@ -28,12 +28,14 @@ refs: # Alert list -Alert lists allow you to display a list of important alerts that you want to track. You can configure the alert list to show the current state of your alert, such as firing, pending, or normal. Learn more about alerts in [Grafana Alerting overview][]. +Alert lists allow you to display a list of important alerts that you want to track. You can configure the alert list to show the current state of your alert, such as firing, pending, or normal. Learn more about alerts in [Grafana Alerting overview](ref:grafana-alerting-overview). {{< figure src="/static/img/docs/alert-list-panel/alert-list-panel.png" max-width="850px" alt="An alert list visualization" >}} On each dashboard load, this visualization queries the alert list, always providing the most up-to-date results. +{{< docs/play title="Alert List" url="https://play.grafana.org/d/bdodlcyou483ke/" >}} + ## Configure an alert list Once you’ve created a [dashboard](https://grafana.com/docs/grafana//dashboards/build-dashboards/create-dashboard/), the following video shows you how to configure an alert list visualization: diff --git a/docs/sources/panels-visualizations/visualizations/news/index.md b/docs/sources/panels-visualizations/visualizations/news/index.md index 2a1e5675c3d..8cb516606e6 100644 --- a/docs/sources/panels-visualizations/visualizations/news/index.md +++ b/docs/sources/panels-visualizations/visualizations/news/index.md @@ -30,6 +30,8 @@ In version 8.5, we discontinued the "Use Proxy" option for Grafana news visualiz You can use the news visualization to provide regular news and updates to your users. +{{< docs/play title="News Panel" url="https://play.grafana.org/d/cdodkwspaaa68b/" >}} + ## Configure a news visualization Once you’ve created a [dashboard](https://grafana.com/docs/grafana//dashboards/build-dashboards/create-dashboard/), enter the URL of an RSS in the [URL](#url) field in the **News** section. This visualization type doesn't accept any other queries, and you shouldn't expect to be able to filter or query the RSS feed data in any way using this visualization. diff --git a/docs/sources/panels-visualizations/visualizations/node-graph/index.md b/docs/sources/panels-visualizations/visualizations/node-graph/index.md index c8667a88d41..eba00952189 100644 --- a/docs/sources/panels-visualizations/visualizations/node-graph/index.md +++ b/docs/sources/panels-visualizations/visualizations/node-graph/index.md @@ -29,6 +29,8 @@ The following video provides beginner steps for creating node panel visualizatio {{< youtube id="VrvsMkRwoKw" >}} +{{< docs/play title="Node graph panel" url="https://play.grafana.org/d/bdodfbi3d57uoe/" >}} + ## Data requirements A node graph requires a specific shape of the data to be able to display its nodes and edges. This means not every data source or query can be visualized with this graph. If you want to use this as a data source developer see the section about data API. diff --git a/docs/sources/panels-visualizations/visualizations/traces/index.md b/docs/sources/panels-visualizations/visualizations/traces/index.md index b68688630d9..46a309fccd1 100644 --- a/docs/sources/panels-visualizations/visualizations/traces/index.md +++ b/docs/sources/panels-visualizations/visualizations/traces/index.md @@ -65,6 +65,8 @@ For more information about traces and how to use them, refer to the following do {{< figure src="/static/img/docs/explore/trace-view-9-4.png" class="docs-image--no-shadow" max-width= "900px" caption="Screenshot of the trace view" >}} +{{< docs/play title="Traces Panel" url="https://play.grafana.org/d/edodkfmj0tce8f/" >}} + ## Add a panel with tracing visualizations Once you have tracing data available in your Grafana stack, you can add tracing panels to your Grafana dashboards. From 59fbc0d9e3de38ac83289fb4bab16e78dd15e398 Mon Sep 17 00:00:00 2001 From: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:10:03 -0500 Subject: [PATCH 14/15] Docs: Updates to the Grafana enterprise data source intro page (#89065) removed a dupe, deprecated data source and cleaned up a page name --- docs/sources/introduction/grafana-enterprise.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/sources/introduction/grafana-enterprise.md b/docs/sources/introduction/grafana-enterprise.md index 03f20e0233d..ab145ce8468 100644 --- a/docs/sources/introduction/grafana-enterprise.md +++ b/docs/sources/introduction/grafana-enterprise.md @@ -19,7 +19,7 @@ To learn more about Grafana Enterprise, refer to [our product page](/enterprise) ## Enterprise features in Grafana Cloud -Many Grafana Enterprise features are also available in [Grafana Cloud](/docs/grafana-cloud) Free, Pro, and Advanced accounts. For details, refer to [the Grafana Cloud features table](/pricing/#featuresTable). +Many Grafana Enterprise features are also available in [Grafana Cloud](/docs/grafana-cloud) Free, Pro, and Advanced accounts. For details, refer to [Grafana Cloud pricing](/pricing/#featuresTable). To migrate to Grafana Cloud, refer to [Migrate from Grafana Enterprise to Grafana Cloud](/docs/grafana-cloud/account-management/e2c-guide/). @@ -79,12 +79,10 @@ With a Grafana Enterprise license, you also get access to premium data sources, - [Azure Devops](/grafana/plugins/grafana-azuredevops-datasource) - [Databricks](/grafana/plugins/grafana-databricks-datasource) - [DataDog](/grafana/plugins/grafana-datadog-datasource) -- [Databricks](/grafana/plugins/grafana-databricks-datasource/) - [Dynatrace](/grafana/plugins/grafana-dynatrace-datasource) - [Gitlab](/grafana/plugins/grafana-gitlab-datasource) - [Honeycomb](/grafana/plugins/grafana-honeycomb-datasource) - [Jira](/grafana/plugins/grafana-jira-datasource) -- [k6 Cloud App](/grafana/plugins/grafana-k6-app) - [MongoDB](/grafana/plugins/grafana-mongodb-datasource) - [New Relic](/grafana/plugins/grafana-newrelic-datasource) - [Oracle Database](/grafana/plugins/grafana-oracle-datasource) From 465efbf8020fb210f723ae9048e80cec03caa33c Mon Sep 17 00:00:00 2001 From: Jay Clifford <45856600+Jayclifford345@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:56:20 -0400 Subject: [PATCH 15/15] feat: Updated whats-new-in-v11-0.md with release video (#87953) Update whats-new-in-v11-0.md Grafana 11 release video added. --- docs/sources/whatsnew/whats-new-in-v11-0.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sources/whatsnew/whats-new-in-v11-0.md b/docs/sources/whatsnew/whats-new-in-v11-0.md index 6d1c37adf2f..024e7a44c69 100644 --- a/docs/sources/whatsnew/whats-new-in-v11-0.md +++ b/docs/sources/whatsnew/whats-new-in-v11-0.md @@ -23,6 +23,8 @@ weight: -42 Welcome to Grafana 11.0! This release contains some major improvements: most notably, the ability to explore your Prometheus metrics and Loki logs without writing any PromQL or LogQL, using Explore Metrics and Explore Logs. The dashboard experience is better than ever with edit mode for dashboards, AI-generated dashboard names and descriptions, and general availability for subfolders. You can also take advantage of improvements to the canvas and table visualizations, new transformations, a revamp of the Alert Rule page, and more. +{{< youtube id="96ZXShRyMB8" >}} + For even more detail about all the changes in this release, refer to the [changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md). For the specific steps we recommend when you upgrade to v11.0, check out our [Upgrade Guide](https://grafana.com/docs/grafana//upgrade-guide/upgrade-v11.0/). ## Breaking changes