[CB-6221] Add a Clear button to the Output log #4410 (#3547)

* dbeaver/pro#4410 adds clear button for output logs

* pr fixes

* pr fixes

* lint fix

* removes logs only for specific context

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
sergeyteleshev
2025-06-26 19:05:29 +08:00
committed by GitHub
co-authored by Evgenia
parent 9de5f9508f
commit eaeb493d6b
7 changed files with 84 additions and 33 deletions
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -625,7 +625,7 @@ export abstract class CachedResource<
context?: TInclude,
): Promise<void> {}
protected abstract loader(param: ResourceKey<TKey>, include: ReadonlyArray<string> | undefined, refresh: boolean): Promise<TData>;
protected abstract loader(param: ResourceKey<TKey>, include: ReadonlyArray<string> | undefined, refresh: boolean): TData | Promise<TData>;
/**
* Implements same behavior as {@link CachedResource.load} and {@link CachedResource.refresh} for custom loaders.
@@ -784,7 +784,7 @@ export abstract class CachedResource<
this.markOutdatedSync(key);
}
},
success: async () => {
success: () => {
if (loaded) {
this.dataUpdate(key);
}
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createAction } from '@cloudbeaver/core-view';
export const ACTION_SQL_EDITOR_CLEAR_OUTPUT_LOGS = createAction('action_sql_editor_clear_output_logs', {
label: 'ui_clear',
icon: 'erase',
tooltip: 'ui_clear',
});
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -31,17 +31,15 @@ export const OutputLogsPanel = observer<Props>(function SqlOutputLogsPanel({ sql
return (
<Container className="theme-background-secondary" overflow vertical noWrap dense parent gap>
<OutputLogsToolbar state={state} sqlEditorTabState={sqlEditorTabState} />
{data && (
<Group box overflow>
<EditorLoader
value={state.resultValue}
foldGutter={false}
lineWrapping={outputLogsService.settings.wrapMode}
highlightActiveLine={false}
readonly
/>
</Group>
)}
<Group box overflow>
<EditorLoader
value={state.resultValue}
foldGutter={false}
lineWrapping={outputLogsService.settings.wrapMode}
highlightActiveLine={false}
readonly
/>
</Group>
</Container>
);
});
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -58,7 +58,16 @@ export class OutputLogsResource extends CachedDataResource<IOutputLog[]> {
this.setData(updatedData);
}
protected async loader(): Promise<IOutputLog[]> {
override clear(): void {
super.clear();
this.useTracker.use(undefined);
}
deleteLogsByContextId(contextId: string): void {
this.setData(this.data.filter(log => log.contextId !== contextId));
}
protected loader(): IOutputLog[] {
return this.data;
}
}
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@ import type { ISqlEditorTabState } from '../../ISqlEditorTabState.js';
import { SqlDataSourceService } from '../../SqlDataSource/SqlDataSourceService.js';
import { OUTPUT_LOG_TYPES } from './IOutputLogTypes.js';
import { OUTPUT_LOGS_TAB_ID } from './OUTPUT_LOGS_TAB_ID.js';
import type { IOutputLog } from './OutputLogsResource.js';
import { type IOutputLog, OutputLogsResource } from './OutputLogsResource.js';
const OUTPUT_LOGS_KEY = 'output_logs';
@@ -22,13 +22,17 @@ interface ISettings {
@injectable()
export class OutputLogsService {
get settings() {
get settings(): ISettings {
return this.userDataService.getUserData(OUTPUT_LOGS_KEY, getOutputLogsDefaultSettings);
}
constructor(private readonly sqlDataSourceService: SqlDataSourceService, private readonly userDataService: UserDataService) {}
constructor(
private readonly sqlDataSourceService: SqlDataSourceService,
private readonly userDataService: UserDataService,
private readonly outputLogsResource: OutputLogsResource,
) {}
async showOutputLogs(editorState: ISqlEditorTabState): Promise<void> {
showOutputLogs(editorState: ISqlEditorTabState): void {
this.createOutputLogsTab(editorState);
editorState.currentTabId = OUTPUT_LOGS_TAB_ID;
}
@@ -39,7 +43,7 @@ export class OutputLogsService {
}
}
toggleWrapMode() {
toggleWrapMode(): void {
this.settings.wrapMode = !this.settings.wrapMode;
}
@@ -61,11 +65,22 @@ export class OutputLogsService {
state.tabs.push({ ...tab });
}
getOutputLogs(events: IOutputLog[], editorState: ISqlEditorTabState) {
getOutputLogs(events: IOutputLog[], editorState: ISqlEditorTabState): IOutputLog[] {
const dataSource = this.sqlDataSourceService.get(editorState.editorId);
return events.filter(event => event.contextId === dataSource?.executionContext?.id);
}
clearOutputLogs(state: ISqlEditorTabState): void {
const dataSource = this.sqlDataSourceService.get(state.editorId);
const contextId = dataSource?.executionContext?.id;
if (!contextId) {
return;
}
this.outputLogsResource.deleteLogsByContextId(contextId);
}
}
function getOutputLogsDefaultSettings(): ISettings {
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import { OUTPUT_LOGS_FILTER_MENU } from './OUTPUT_LOGS_FILTER_MENU.js';
import { OUTPUT_LOGS_MENU } from './OUTPUT_LOGS_MENU.js';
import { OUTPUT_LOGS_SETTINGS_MENU } from './OUTPUT_LOGS_SETTINGS_MENU.js';
import { OutputLogsService } from './OutputLogsService.js';
import { ACTION_SQL_EDITOR_CLEAR_OUTPUT_LOGS } from './ACTION_SQL_EDITOR_CLEAR_OUTPUT_LOGS.js';
@injectable()
export class OutputMenuBootstrap extends Bootstrap {
@@ -37,7 +38,7 @@ export class OutputMenuBootstrap extends Bootstrap {
this.menuService.addCreator({
menus: [OUTPUT_LOGS_MENU],
getItems(context, items) {
return [...items, OUTPUT_LOGS_FILTER_MENU, OUTPUT_LOGS_SETTINGS_MENU];
return [...items, ACTION_SQL_EDITOR_CLEAR_OUTPUT_LOGS, OUTPUT_LOGS_FILTER_MENU, OUTPUT_LOGS_SETTINGS_MENU];
},
});
@@ -118,7 +119,7 @@ export class OutputMenuBootstrap extends Bootstrap {
private registerOutputLogsAction() {
this.actionService.addHandler({
id: 'output-logs-handler',
actions: [ACTION_SHOW_OUTPUT_LOGS],
actions: [ACTION_SHOW_OUTPUT_LOGS, ACTION_SQL_EDITOR_CLEAR_OUTPUT_LOGS],
contexts: [DATA_CONTEXT_SQL_EDITOR_STATE],
isActionApplicable: (context): boolean => {
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!;
@@ -130,12 +131,16 @@ export class OutputMenuBootstrap extends Bootstrap {
return !!isQuery && !!isExecutable;
},
handler: async (context, action) => {
handler: (context, action) => {
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!;
if (action === ACTION_SHOW_OUTPUT_LOGS) {
this.outputLogsService.showOutputLogs(state);
}
if (action === ACTION_SQL_EDITOR_CLEAR_OUTPUT_LOGS) {
this.outputLogsService.clearOutputLogs(state);
}
},
});
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -15,26 +15,31 @@ import type { IOutputLog } from './OutputLogsResource.js';
export interface SqlOutputLogsPanelState {
searchValue: string;
outputLogs: IOutputLog[];
sqlEditorTabState: ISqlEditorTabState;
setSearchValue: (value: string) => void;
logMessages: WsOutputLogInfo['message'][];
readonly resultValue: string;
readonly filteredLogs: WsOutputLogInfo[];
}
export const useOutputLogsPanelState = (outputLogs: IOutputLog[], sqlEditorTabState: ISqlEditorTabState) =>
export const useOutputLogsPanelState = (outputLogs: IOutputLog[], sqlEditorTabState: ISqlEditorTabState): SqlOutputLogsPanelState =>
useObservableRef<SqlOutputLogsPanelState>(
() => ({
outputLogs,
sqlEditorTabState,
searchValue: '',
setSearchValue(value: string) {
this.searchValue = value;
},
get filteredLogs() {
const selectedLogTypes = sqlEditorTabState.outputLogsTab?.selectedLogTypes;
const selectedLogTypes = this.sqlEditorTabState.outputLogsTab?.selectedLogTypes;
if (!selectedLogTypes?.length) {
return [];
}
return outputLogs.filter(log => {
return this.outputLogs.filter(log => {
if (log.severity && !selectedLogTypes.includes(log.severity)) {
return false;
}
@@ -65,10 +70,15 @@ export const useOutputLogsPanelState = (outputLogs: IOutputLog[], sqlEditorTabSt
},
}),
{
outputLogs: observable.ref,
sqlEditorTabState: observable.ref,
searchValue: observable.ref,
setSearchValue: action.bound,
filteredLogs: computed,
resultValue: computed,
},
false,
{
outputLogs,
sqlEditorTabState,
},
);