Explore: Add hide_logs_download and hide button to download logs (#99512)

* Explore: Add `disableLogsDownload` and hide button to download logs

* change copy

* Explore: Change `disableLogsDownload` to `hide_logs_download`

* change casing in frontend

* also hide from inspector

* add test

* lint
This commit is contained in:
Sven Grossmann 2025-01-29 11:53:52 +01:00 committed by GitHub
parent 6ea87802ed
commit 336449c169
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 55 additions and 8 deletions

View File

@ -1574,6 +1574,9 @@ enabled = true
# set the default offset for the time picker
defaultTimeOffset = 1h
# hides the download logs button in Explore
hide_logs_download = false
#################################### Help #############################
[help]
# Enable the Help section

View File

@ -1934,6 +1934,10 @@ Enable or disable the Explore section. Default is `enabled`.
Set a default time offset from now on the time picker. Default is 1 hour.
This setting should be expressed as a duration. Examples: 1h (hour), 1d (day), 1w (week), 1M (month).
#### `hide_logs_download`
Show or hide the button to download logs in Explore. Default is `false`, so that the button will be visible.
### `[help]`
Configures the help section.

View File

@ -238,6 +238,7 @@ export interface GrafanaConfig {
listScopesEndpoint?: string;
reportingStaticContext?: Record<string, string>;
exploreDefaultTimeOffset?: string;
exploreHideLogsDownload?: boolean;
// The namespace to use for kubernetes apiserver requests
namespace: string;

View File

@ -203,6 +203,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
cloudMigrationPollIntervalMs = 2000;
reportingStaticContext?: Record<string, string>;
exploreDefaultTimeOffset = '1h';
exploreHideLogsDownload: boolean | undefined;
/**
* Language used in Grafana's UI. This is after the user's preference (or deteceted locale) is resolved to one of

View File

@ -212,6 +212,7 @@ type FrontendSettingsDTO struct {
CSPReportOnlyEnabled bool `json:"cspReportOnlyEnabled"`
EnableFrontendSandboxForPlugins []string `json:"enableFrontendSandboxForPlugins"`
ExploreDefaultTimeOffset string `json:"exploreDefaultTimeOffset"`
ExploreHideLogsDownload bool `json:"ExploreHideLogsDownload"`
Auth FrontendSettingsAuthDTO `json:"auth"`

View File

@ -247,6 +247,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
LocalFileSystemAvailable: hs.Cfg.LocalFileSystemAvailable,
ReportingStaticContext: hs.Cfg.ReportingStaticContext,
ExploreDefaultTimeOffset: hs.Cfg.ExploreDefaultTimeOffset,
ExploreHideLogsDownload: hs.Cfg.ExploreHideLogsDownload,
DefaultDatasourceManageAlertsUIToggle: hs.Cfg.DefaultDatasourceManageAlertsUIToggle,

View File

@ -511,6 +511,7 @@ type Cfg struct {
// Explore UI
ExploreEnabled bool
ExploreDefaultTimeOffset string
ExploreHideLogsDownload bool
// Help UI
HelpEnabled bool
@ -1214,6 +1215,7 @@ func (cfg *Cfg) parseINIFile(iniFile *ini.File) error {
} else {
cfg.ExploreDefaultTimeOffset = exploreDefaultTimeOffset
}
cfg.ExploreHideLogsDownload = explore.Key("hide_logs_download").MustBool(false)
help := iniFile.Section("help")
cfg.HelpEnabled = help.Key("enabled").MustBool(true)

View File

@ -5,6 +5,7 @@ import { ComponentProps } from 'react';
import { FieldType, LogLevel, LogsDedupStrategy, standardTransformersRegistry, toDataFrame } from '@grafana/data';
import { organizeFieldsTransformer } from '@grafana/data/src/transformations/transformers/organize';
import { config } from '@grafana/runtime';
import { MAX_CHARACTERS } from '../../logs/components/LogRowMessage';
import { logRowsToReadableJson } from '../../logs/utils';
@ -32,11 +33,12 @@ const defaultProps: LogsMetaRowProps = {
clearDetectedFields: jest.fn(),
};
const setup = (propOverrides?: object) => {
const setup = (propOverrides?: object, disableDownload = false) => {
const props = {
...defaultProps,
...propOverrides,
};
config.exploreHideLogsDownload = disableDownload;
return render(<LogsMetaRow {...props} />);
};
@ -121,6 +123,11 @@ describe('LogsMetaRow', () => {
expect(screen.getByText('Download').closest('button')).toBeInTheDocument();
});
it('does not render a button to show the download menu if disabled', async () => {
setup({}, true);
expect(screen.queryByText('Download')).toBeNull();
});
it('renders a button to show the download menu', async () => {
setup();

View File

@ -16,7 +16,7 @@ import {
Labels,
} from '@grafana/data';
import { DataFrame } from '@grafana/data/';
import { reportInteraction } from '@grafana/runtime';
import { config, reportInteraction } from '@grafana/runtime';
import { Button, Dropdown, Menu, ToolbarButton, Tooltip, useStyles2 } from '@grafana/ui';
import { downloadDataFrameAsCsv, downloadLogsModelAsTxt } from '../../inspector/utils/download';
@ -182,11 +182,13 @@ export const LogsMetaRow = memo(
};
})}
/>
<Dropdown overlay={downloadMenu}>
<ToolbarButton isOpen={false} variant="canvas" icon="download-alt">
Download
</ToolbarButton>
</Dropdown>
{!config.exploreHideLogsDownload && (
<Dropdown overlay={downloadMenu}>
<ToolbarButton isOpen={false} variant="canvas" icon="download-alt">
Download
</ToolbarButton>
</Dropdown>
)}
</div>
)}
</>

View File

@ -4,6 +4,7 @@ import { ComponentProps } from 'react';
import { Props } from 'react-virtualized-auto-sizer';
import { DataFrame, FieldType } from '@grafana/data';
import { config } from '@grafana/runtime';
import { InspectDataTab } from './InspectDataTab';
@ -75,6 +76,8 @@ describe('InspectDataTab', () => {
expect(screen.getByText(/Second data frame/i)).toBeInTheDocument();
});
it('should show download logs button if logs data', () => {
const oldConfig = config.exploreHideLogsDownload;
config.exploreHideLogsDownload = false;
const dataWithLogs = [
{
name: 'Data frame with logs',
@ -91,6 +94,28 @@ describe('InspectDataTab', () => {
] as unknown as DataFrame[];
render(<InspectDataTab {...createProps({ data: dataWithLogs })} />);
expect(screen.getByText(/Download logs/i)).toBeInTheDocument();
config.exploreHideLogsDownload = oldConfig;
});
it('should not show download logs button if logs data but config disabled', () => {
const oldConfig = config.exploreHideLogsDownload;
config.exploreHideLogsDownload = true;
const dataWithLogs = [
{
name: 'Data frame with logs',
fields: [
{ name: 'time', type: FieldType.time, values: [100, 200, 300], config: {} },
{ name: 'name', type: FieldType.string, values: ['uniqueA', 'b', 'c'], config: {} },
{ name: 'value', type: FieldType.number, values: [1, 2, 3], config: {} },
],
length: 3,
meta: {
preferredVisualisationType: 'logs',
},
},
] as unknown as DataFrame[];
render(<InspectDataTab {...createProps({ data: dataWithLogs })} />);
expect(screen.queryByText(/Download logs/i)).not.toBeInTheDocument();
config.exploreHideLogsDownload = oldConfig;
});
it('should not show download logs button if no logs data', () => {
render(<InspectDataTab {...createProps()} />);

View File

@ -223,7 +223,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
<Button variant="primary" onClick={() => this.exportCsv(dataFrames, hasLogs)} size="sm">
<Trans i18nKey="dashboard.inspect-data.download-csv">Download CSV</Trans>
</Button>
{hasLogs && (
{hasLogs && !config.exploreHideLogsDownload && (
<Button variant="primary" onClick={this.onExportLogsAsTxt} size="sm">
<Trans i18nKey="dashboard.inspect-data.download-logs">Download logs</Trans>
</Button>