New Logs Panel: remove sibling visualization (#110444)

* New Logs Panel: remove sibling visualization

* More removals

* Update provisioned dashboard

* Update translations
This commit is contained in:
Matias Chomicki 2025-09-04 11:27:50 +02:00 committed by GitHub
parent 600137919e
commit df685757ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 6 additions and 656 deletions

1
.github/CODEOWNERS vendored
View File

@ -949,7 +949,6 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/plugins/panel/heatmap/ @grafana/dataviz-squad
/public/app/plugins/panel/histogram/ @grafana/dataviz-squad
/public/app/plugins/panel/logs/ @grafana/observability-logs
/public/app/plugins/panel/logs-new/ @grafana/observability-logs
/public/app/plugins/panel/nodeGraph/ @grafana/observability-traces-and-profiling @grafana/app-o11y-visualizations
/public/app/plugins/panel/traces/ @grafana/observability-traces-and-profiling
/public/app/plugins/panel/flamegraph/ @grafana/observability-traces-and-profiling

View File

@ -919,7 +919,7 @@
}
],
"title": "Requests",
"type": "logs-new"
"type": "logs"
},
{
"datasource": {
@ -962,7 +962,7 @@
}
],
"title": "grafana-api requests",
"type": "logs-new"
"type": "logs"
},
{
"datasource": {
@ -1317,7 +1317,7 @@
}
],
"title": "frontend-service errors",
"type": "logs-new"
"type": "logs"
},
{
"datasource": {
@ -1360,7 +1360,7 @@
}
],
"title": "grafana-api errors",
"type": "logs-new"
"type": "logs"
},
{
"datasource": {
@ -1403,7 +1403,7 @@
}
],
"title": "All frontend-service logs",
"type": "logs-new"
"type": "logs"
},
{
"datasource": {
@ -1446,7 +1446,7 @@
}
],
"title": "All grafana-api logs",
"type": "logs-new"
"type": "logs"
}
],
"preload": false,

View File

@ -248,16 +248,6 @@ func GetComposableKinds() ([]ComposableKind, error) {
CueFile: logsCue,
})
logsnewCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/logs-new/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "logsnew",
Filename: "panelcfg.cue",
CueFile: logsnewCue,
})
newsCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/news/panelcfg.cue"))
if err != nil {
return nil, err

View File

@ -227,7 +227,6 @@ func verifyCorePluginCatalogue(t *testing.T, ctx context.Context, ps *pluginstor
"histogram": {},
"live": {},
"logs": {},
"logs-new": {},
"candlestick": {},
"news": {},
"nodeGraph": {},

View File

@ -44,8 +44,6 @@ const histogramPanel = async () =>
await import(/* webpackChunkName: "histogramPanel" */ 'app/plugins/panel/histogram/module');
const livePanel = async () => await import(/* webpackChunkName: "livePanel" */ 'app/plugins/panel/live/module');
const logsPanel = async () => await import(/* webpackChunkName: "logsPanel" */ 'app/plugins/panel/logs/module');
const newLogsPanel = async () =>
await import(/* webpackChunkName: "newLogsPanel" */ 'app/plugins/panel/logs-new/module');
const newsPanel = async () => await import(/* webpackChunkName: "newsPanel" */ 'app/plugins/panel/news/module');
const pieChartPanel = async () =>
await import(/* webpackChunkName: "pieChartPanel" */ 'app/plugins/panel/piechart/module');
@ -111,7 +109,6 @@ const builtInPlugins: Record<string, System.Module | (() => Promise<System.Modul
'core:plugin/bargauge': barGaugePanel,
'core:plugin/barchart': barChartPanel,
'core:plugin/logs': logsPanel,
'core:plugin/logs-new': newLogsPanel,
'core:plugin/traces': tracesPanel,
'core:plugin/welcome': welcomeBanner,
'core:plugin/nodeGraph': nodeGraph,

View File

@ -1,164 +0,0 @@
import { render, screen } from '@testing-library/react';
import { ComponentProps } from 'react';
import {
LoadingState,
createDataFrame,
FieldType,
LogsSortOrder,
getDefaultTimeRange,
LogsDedupStrategy,
EventBusSrv,
DataFrameType,
LogSortOrderChangeEvent,
} from '@grafana/data';
import { getAppEvents } from '@grafana/runtime';
import { LogsPanel } from './LogsPanel';
type LogsPanelProps = ComponentProps<typeof LogsPanel>;
jest.mock('@grafana/assistant', () => ({
...jest.requireActual('@grafana/assistant'),
useAssistant: jest.fn(() => [true, jest.fn()]),
}));
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getAppEvents: jest.fn(),
}));
const defaultProps = {
data: {
error: undefined,
request: {
panelId: 4,
app: 'dashboard',
requestId: 'A',
timezone: 'browser',
interval: '30s',
intervalMs: 30000,
maxDataPoints: 823,
targets: [],
range: getDefaultTimeRange(),
scopedVars: {},
startTime: 1,
},
series: [
createDataFrame({
refId: 'A',
fields: [
{
name: 'timestamp',
type: FieldType.time,
values: ['2019-04-26T09:28:11.352440161Z'],
},
{
name: 'body',
type: FieldType.string,
values: ['logline text'],
},
{
name: 'labels',
type: FieldType.other,
values: [
{
app: 'common_app',
},
],
},
],
meta: {
type: DataFrameType.LogLines,
},
}),
],
state: LoadingState.Done,
timeRange: getDefaultTimeRange(),
},
timeZone: 'utc',
timeRange: getDefaultTimeRange(),
options: {
showLabels: false,
showTime: false,
wrapLogMessage: false,
sortOrder: LogsSortOrder.Descending,
dedupStrategy: LogsDedupStrategy.none,
enableLogDetails: false,
enableInfiniteScrolling: false,
showControls: false,
syntaxHighlighting: false,
},
title: 'Logs panel',
id: 1,
transparent: false,
width: 400,
height: 100,
renderCounter: 0,
fieldConfig: {
defaults: {},
overrides: [],
},
eventBus: new EventBusSrv(),
onOptionsChange: jest.fn(),
onFieldConfigChange: jest.fn(),
replaceVariables: jest.fn(),
onChangeTimeRange: jest.fn(),
};
const publishMock = jest.fn();
beforeAll(() => {
jest.mocked(getAppEvents).mockReturnValue({
publish: publishMock,
getStream: jest.fn(),
subscribe: jest.fn(),
removeAllListeners: jest.fn(),
newScopedBus: jest.fn(),
});
});
describe('LogsPanel', () => {
test('Renders a list of logs without controls ', async () => {
setup();
await screen.findByText('logline text');
expect(screen.queryByLabelText('Scroll to bottom')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Display levels')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Scroll to top')).not.toBeInTheDocument();
});
test('Renders a list of logs with controls', async () => {
setup({ options: { ...defaultProps.options, showControls: true } });
await screen.findByText('logline text');
expect(screen.getByLabelText('Scroll to bottom')).toBeInTheDocument();
expect(screen.getByLabelText('Display levels')).toBeInTheDocument();
expect(screen.getByLabelText('Scroll to top')).toBeInTheDocument();
});
test('Publishes an event with the current sort order', async () => {
publishMock.mockClear();
setup();
await screen.findByText('logline text');
expect(publishMock).toHaveBeenCalledTimes(1);
expect(publishMock).toHaveBeenCalledWith(
new LogSortOrderChangeEvent({
order: LogsSortOrder.Descending,
})
);
});
});
const setup = (propsOverrides?: Partial<LogsPanelProps>) => {
const props: LogsPanelProps = {
...defaultProps,
data: {
...(propsOverrides?.data || defaultProps.data),
},
options: {
...(propsOverrides?.options || defaultProps.options),
},
};
return { ...render(<LogsPanel {...props} />), props };
};

View File

@ -1,185 +0,0 @@
import { css } from '@emotion/css';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
AbsoluteTimeRange,
CoreApp,
DataFrame,
DataHoverEvent,
GrafanaTheme2,
LoadingState,
LogRowModel,
LogSortOrderChangeEvent,
LogsSortOrder,
PanelProps,
} from '@grafana/data';
import { config, getAppEvents } from '@grafana/runtime';
import { usePanelContext, useStyles2 } from '@grafana/ui';
import { LogList } from 'app/features/logs/components/panel/LogList';
import { PanelDataErrorView } from 'app/features/panel/components/PanelDataErrorView';
import { dataFrameToLogsModel, dedupLogRows } from '../../../features/logs/logsModel';
import { requestMoreLogs } from '../logs/LogsPanel';
import { useDatasourcesFromTargets } from '../logs/useDatasourcesFromTargets';
import { Options } from './panelcfg.gen';
import { isCoreApp, isLogsGrammar, isOnLogOptionsChange, isOnNewLogsReceivedType } from './types';
interface LogsPanelProps extends PanelProps<Options> {}
export const LogsPanel = ({
data,
timeZone,
fieldConfig,
options: {
controlsStorageKey,
dedupStrategy,
enableInfiniteScrolling,
grammar,
onLogOptionsChange,
onNewLogsReceived,
showControls,
showTime,
sortOrder,
syntaxHighlighting,
wrapLogMessage,
},
id,
}: LogsPanelProps) => {
const style = useStyles2(getStyles);
const [logsContainer, setLogsContainer] = useState<HTMLDivElement | null>(null);
const [panelData, setPanelData] = useState(data);
const dataSourcesMap = useDatasourcesFromTargets(data.request?.targets);
// Prevents the scroll position to change when new data from infinite scrolling is received
const keepScrollPositionRef = useRef(false);
// Loading ref to prevent firing multiple requests
const loadingRef = useRef(false);
const { app, eventBus } = usePanelContext();
const logs = useMemo(() => {
const logsModel = panelData
? dataFrameToLogsModel(panelData.series, panelData.request?.intervalMs, undefined, panelData.request?.targets)
: null;
return logsModel ? dedupLogRows(logsModel.rows, dedupStrategy) : [];
}, [dedupStrategy, panelData]);
useEffect(() => {
getAppEvents().publish(
new LogSortOrderChangeEvent({
order: sortOrder,
})
);
}, [sortOrder]);
useEffect(() => {
if (data.state !== LoadingState.Loading) {
setPanelData(data);
}
}, [data]);
const loadMoreLogs = useCallback(
async (scrollRange: AbsoluteTimeRange) => {
if (!data.request || !config.featureToggles.logsInfiniteScrolling || loadingRef.current) {
return;
}
loadingRef.current = true;
const onNewLogsReceivedCallback = isOnNewLogsReceivedType(onNewLogsReceived) ? onNewLogsReceived : undefined;
let newSeries: DataFrame[] = [];
try {
newSeries = await requestMoreLogs(dataSourcesMap, panelData, scrollRange, timeZone, onNewLogsReceivedCallback);
} catch (e) {
console.error(e);
} finally {
loadingRef.current = false;
}
keepScrollPositionRef.current = true;
setPanelData({
...panelData,
series: newSeries,
});
},
[data.request, dataSourcesMap, onNewLogsReceived, panelData, timeZone]
);
const onLogRowHover = useCallback(
(row?: LogRowModel) => {
if (row) {
eventBus.publish(
new DataHoverEvent({
point: {
time: row.timeEpochMs,
},
})
);
}
},
[eventBus]
);
const initialScrollPosition = useMemo(() => {
/**
* In dashboards, users with newest logs at the bottom have the expectation of keeping the scroll at the bottom
* when new data is received. See https://github.com/grafana/grafana/pull/37634
*/
if (data.request?.app === CoreApp.Dashboard || data.request?.app === CoreApp.PanelEditor) {
return sortOrder === LogsSortOrder.Ascending ? 'bottom' : 'top';
}
return 'top';
}, [data.request?.app, sortOrder]);
const storageKey = useMemo(() => {
if (controlsStorageKey) {
return controlsStorageKey;
}
if (!data.request) {
return undefined;
}
return `${data.request?.dashboardUID}.${id}`;
}, [controlsStorageKey, data.request, id]);
if (!logs.length) {
return <PanelDataErrorView fieldConfig={fieldConfig} panelId={id} data={data} needsStringField />;
}
return (
<div className={style.container} ref={(element: HTMLDivElement) => setLogsContainer(element)}>
{logs.length > 0 && logsContainer && (
<LogList
app={isCoreApp(app) ? app : CoreApp.Dashboard}
containerElement={logsContainer}
dedupStrategy={dedupStrategy}
displayedFields={[]}
enableLogDetails
grammar={isLogsGrammar(grammar) ? grammar : undefined}
initialScrollPosition={initialScrollPosition}
logs={logs}
loadMore={enableInfiniteScrolling ? loadMoreLogs : undefined}
onLogOptionsChange={isOnLogOptionsChange(onLogOptionsChange) ? onLogOptionsChange : undefined}
onLogLineHover={onLogRowHover}
showControls={showControls}
showTime={showTime}
sortOrder={sortOrder}
logOptionsStorageKey={storageKey}
syntaxHighlighting={syntaxHighlighting}
timeRange={data.timeRange}
timeZone={timeZone}
wrapLogMessage={wrapLogMessage}
/>
)}
</div>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
container: css({
marginBottom: theme.spacing(1.5),
minHeight: '100%',
maxHeight: '100%',
display: 'flex',
flex: 1,
flexDirection: 'column',
}),
});

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 80.49 80.99"><defs><style>.cls-1{fill:#3865ab;}.cls-2{fill:#8ab8ff;}.cls-3{fill:url(#linear-gradient);}.cls-4{fill:url(#linear-gradient-2);}.cls-5{fill:#3a76d0;}</style><linearGradient id="linear-gradient" x1="-3918.19" y1="8047.29" x2="-3910.1" y2="8047.29" gradientTransform="translate(-3910.1 8051.33) rotate(180)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f2cc0c"/><stop offset="1" stop-color="#ff9830"/></linearGradient><linearGradient id="linear-gradient-2" x1="-3918.19" y1="8010.84" x2="-3910.1" y2="8010.84" xlink:href="#linear-gradient"/></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M79.49,8H14.54V0H79.49a1,1,0,0,1,1,1V7A1,1,0,0,1,79.49,8Z"/><path class="cls-1" d="M59.49,26.27H14.54v-8h45a1,1,0,0,1,1,1v6A1,1,0,0,1,59.49,26.27Z"/><path class="cls-2" d="M6.92,19.38a4,4,0,0,0-1.34-.85,4.1,4.1,0,0,0-3.08,0,4,4,0,0,0-1.33.85,4,4,0,0,0-.85,1.34,4,4,0,0,0,.85,4.41A4.44,4.44,0,0,0,2.5,26a3.76,3.76,0,0,0,3.08,0,4.61,4.61,0,0,0,1.34-.85,4.19,4.19,0,0,0,0-5.75Z"/><path class="cls-1" d="M48.49,44.49H14.54v-8h34a1,1,0,0,1,1,1v6A1,1,0,0,1,48.49,44.49Z"/><path class="cls-1" d="M79.49,81H14.54V73H79.49a1,1,0,0,1,1,1v6A1,1,0,0,1,79.49,81Z"/><path class="cls-2" d="M7.77,75.4a4,4,0,0,1-.85,4.41,4.61,4.61,0,0,1-1.34.85,3.76,3.76,0,0,1-3.08,0,4.44,4.44,0,0,1-1.33-.85A4,4,0,0,1,.32,75.4a4,4,0,0,1,.85-1.34,4,4,0,0,1,1.33-.85,4.1,4.1,0,0,1,3.08,0,4,4,0,0,1,1.34.85A4,4,0,0,1,7.77,75.4Z"/><path class="cls-3" d="M6.92,1.15A4.36,4.36,0,0,0,5.58.3,4.1,4.1,0,0,0,2.5.3a4.32,4.32,0,0,0-1.33.85A4,4,0,0,0,.32,2.49a4,4,0,0,0,.85,4.42,4.87,4.87,0,0,0,1.33.85,3.84,3.84,0,0,0,3.08,0,5.07,5.07,0,0,0,1.34-.85,4.07,4.07,0,0,0,.85-4.42A4,4,0,0,0,6.92,1.15Z"/><path class="cls-4" d="M6.92,37.61a4,4,0,0,0-1.34-.85,4,4,0,0,0-3.08,0,4,4,0,0,0-1.33.85,4,4,0,0,0-.85,1.33,4,4,0,0,0,.85,4.42,4.64,4.64,0,0,0,1.33.85,3.84,3.84,0,0,0,3.08,0,4.83,4.83,0,0,0,1.34-.85,4.07,4.07,0,0,0,.85-4.42A4,4,0,0,0,6.92,37.61Z"/><path class="cls-1" d="M70.49,62.72H14.54v-8h56a1,1,0,0,1,1,1v6A1,1,0,0,1,70.49,62.72Z"/><path class="cls-5" d="M6.92,55.83A4.36,4.36,0,0,0,5.58,55,4.1,4.1,0,0,0,2.5,55a4.32,4.32,0,0,0-1.33.85,4.2,4.2,0,0,0,0,5.76,4.87,4.87,0,0,0,1.33.85,3.84,3.84,0,0,0,3.08,0,5.07,5.07,0,0,0,1.34-.85,4.07,4.07,0,0,0,.85-4.42A4,4,0,0,0,6.92,55.83Z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,108 +0,0 @@
import { PanelPlugin, LogsSortOrder, LogsDedupStrategy, LogsDedupDescription } from '@grafana/data';
import { t } from '@grafana/i18n';
import { LogsPanel } from './LogsPanel';
import { Options } from './panelcfg.gen';
import { LogsPanelSuggestionsSupplier } from './suggestions';
export const plugin = new PanelPlugin<Options>(LogsPanel)
.setPanelOptions((builder) => {
const category = [t('logs-new.category-logs', 'Logs')];
builder
.addBooleanSwitch({
path: 'showTime',
name: t('logs-new.name-time', 'Time'),
category,
description: '',
defaultValue: false,
})
.addBooleanSwitch({
path: 'wrapLogMessage',
name: t('logs-new.name-wrap-lines', 'Wrap lines'),
category,
description: '',
defaultValue: false,
})
.addBooleanSwitch({
path: 'syntaxHighlighting',
name: t('logs-new.name-syntax-highlighting', 'Enable syntax highlighting'),
category,
description: t(
'logs-new.description-syntax-highlighting',
'Use a predefined syntax coloring grammar to highlight relevant parts of the log lines'
),
defaultValue: true,
})
.addBooleanSwitch({
path: 'enableLogDetails',
name: t('logs-new.name-enable-log-details', 'Enable log details'),
category,
description: '',
defaultValue: true,
})
.addBooleanSwitch({
path: 'showControls',
name: t('logs-new.name-show-controls', 'Show controls'),
category,
description: t(
'logs-new.description-show-controls',
'Display controls to jump to the last or first log line, and filters by log level'
),
defaultValue: false,
})
.addBooleanSwitch({
path: 'enableInfiniteScrolling',
name: t('logs-new.name-infinite-scrolling', 'Enable infinite scrolling'),
category,
description: t(
'logs-new.description-infinite-scrolling',
'Experimental. Request more results by scrolling to the bottom of the logs list.'
),
defaultValue: false,
})
.addRadio({
path: 'dedupStrategy',
name: t('logs-new.name-deduplication', 'Deduplication'),
category,
description: '',
settings: {
options: [
{
value: LogsDedupStrategy.none,
label: t('logs-new.deduplication-options.label-none', 'None'),
description: LogsDedupDescription[LogsDedupStrategy.none],
},
{
value: LogsDedupStrategy.exact,
label: t('logs-new.deduplication-options.label-exact', 'Exact'),
description: LogsDedupDescription[LogsDedupStrategy.exact],
},
{
value: LogsDedupStrategy.numbers,
label: t('logs-new.deduplication-options.label-numbers', 'Numbers'),
description: LogsDedupDescription[LogsDedupStrategy.numbers],
},
{
value: LogsDedupStrategy.signature,
label: t('logs-new.deduplication-options.label-signature', 'Signature'),
description: LogsDedupDescription[LogsDedupStrategy.signature],
},
],
},
defaultValue: LogsDedupStrategy.none,
})
.addRadio({
path: 'sortOrder',
name: t('logs-new.name-order', 'Order'),
category,
description: '',
settings: {
options: [
{ value: LogsSortOrder.Descending, label: t('logs-new.order-options.label-newest-first', 'Newest first') },
{ value: LogsSortOrder.Ascending, label: t('logs-new.order-options.label-oldest-first', 'Oldest first') },
],
},
defaultValue: LogsSortOrder.Descending,
});
})
.setSuggestionsSupplier(new LogsPanelSuggestionsSupplier());

View File

@ -1,46 +0,0 @@
// Copyright 2023 Grafana Labs
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package grafanaplugin
import (
"github.com/grafana/grafana/packages/grafana-schema/src/common"
)
composableKinds: PanelCfg: {
maturity: "experimental"
lineage: {
schemas: [{
version: [0, 0]
schema: {
Options: {
showControls: bool
showTime: bool
wrapLogMessage: bool
enableLogDetails: bool
syntaxHighlighting: bool
sortOrder: common.LogsSortOrder
dedupStrategy: common.LogsDedupStrategy
grammar?: _
enableInfiniteScrolling?: bool
onLogOptionsChange?: _
onNewLogsReceived?: _
controlsStorageKey?: string
} @cuetsy(kind="interface")
}
}]
lenses: []
}
}

View File

@ -1,26 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// public/app/plugins/gen.go
// Using jennies:
// TSTypesJenny
// PluginTsTypesJenny
//
// Run 'make gen-cue' from repository root to regenerate.
import * as common from '@grafana/schema';
export interface Options {
controlsStorageKey?: string;
dedupStrategy: common.LogsDedupStrategy;
enableInfiniteScrolling?: boolean;
enableLogDetails: boolean;
grammar?: unknown;
onLogOptionsChange?: unknown;
onNewLogsReceived?: unknown;
showControls: boolean;
showTime: boolean;
sortOrder: common.LogsSortOrder;
syntaxHighlighting: boolean;
wrapLogMessage: boolean;
}

View File

@ -1,24 +0,0 @@
{
"type": "panel",
"name": "Logs new",
"id": "logs-new",
"state": "alpha",
"info": {
"author": {
"name": "Grafana Labs",
"url": "https://grafana.com"
},
"logos": {
"small": "img/icn-logs-panel.svg",
"large": "img/icn-logs-panel.svg"
},
"links": [
{ "name": "Raise issue", "url": "https://github.com/grafana/grafana/issues/new" },
{
"name": "Documentation",
"url": "https://grafana.com/docs/grafana/latest/panels-visualizations/visualizations/logs/"
}
]
}
}

View File

@ -1,33 +0,0 @@
import { VisualizationSuggestionsBuilder, VisualizationSuggestionScore } from '@grafana/data';
import { SuggestionName } from 'app/types/suggestions';
import { Options } from './panelcfg.gen';
export class LogsPanelSuggestionsSupplier {
getSuggestionsForData(builder: VisualizationSuggestionsBuilder) {
const list = builder.getListAppender<Options, {}>({
name: '',
pluginId: 'logs-new',
options: {},
fieldConfig: {
defaults: {
custom: {},
},
overrides: [],
},
});
const { dataSummary: ds } = builder;
// Require a string & time field
if (!ds.hasData || !ds.hasTimeField || !ds.hasStringField) {
return;
}
if (ds.preferredVisualisationType === 'logs') {
list.append({ name: SuggestionName.Logs, score: VisualizationSuggestionScore.Best });
} else {
list.append({ name: SuggestionName.Logs });
}
}
}

View File

@ -1,24 +0,0 @@
import { Grammar } from 'prismjs';
import { CoreApp, DataFrame } from '@grafana/data';
import { LogListControlOptions } from 'app/features/logs/components/panel/LogList';
type onNewLogsReceivedType = (allLogs: DataFrame[], newLogs: DataFrame[]) => void;
type onLogOptionsChangeType = (option: LogListControlOptions, value: string | boolean | string[]) => void;
export function isOnNewLogsReceivedType(callback: unknown): callback is onNewLogsReceivedType {
return typeof callback === 'function';
}
export function isOnLogOptionsChange(callback: unknown): callback is onLogOptionsChangeType {
return typeof callback === 'function';
}
export function isLogsGrammar(grammar: unknown): grammar is Grammar {
return grammar !== null && typeof grammar === 'object' && Object.getPrototypeOf(grammar) === Object.prototype;
}
export function isCoreApp(app: unknown): app is CoreApp {
const apps = Object.values(CoreApp).map((coreApp) => coreApp.toString());
return typeof app === 'string' && apps.includes(app);
}

View File

@ -9734,30 +9734,6 @@
"label-log-stats": "{{label}}: {{total}} of {{rowCount}} rows have that label"
}
},
"logs-new": {
"category-logs": "Logs",
"deduplication-options": {
"label-exact": "Exact",
"label-none": "None",
"label-numbers": "Numbers",
"label-signature": "Signature"
},
"description-infinite-scrolling": "Experimental. Request more results by scrolling to the bottom of the logs list.",
"description-show-controls": "Display controls to jump to the last or first log line, and filters by log level",
"description-syntax-highlighting": "Use a predefined syntax coloring grammar to highlight relevant parts of the log lines",
"name-deduplication": "Deduplication",
"name-enable-log-details": "Enable log details",
"name-infinite-scrolling": "Enable infinite scrolling",
"name-order": "Order",
"name-show-controls": "Show controls",
"name-syntax-highlighting": "Enable syntax highlighting",
"name-time": "Time",
"name-wrap-lines": "Wrap lines",
"order-options": {
"label-newest-first": "Newest first",
"label-oldest-first": "Oldest first"
}
},
"manage-dashbaords": {
"import-dashboard-form": {
"description-existing-library-panels": "List of existing library panels. These panels are not affected by the import."