Skip to content

Commit ca57966

Browse files
committed
feat: enhance LogExplorer with SQL query support and custom keyword suggestions
1 parent 2f2a547 commit ca57966

8 files changed

Lines changed: 80 additions & 23 deletions

File tree

frontend/src/app/log-analyzer/explorer/log-analyzer-field/log-analyzer-field.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export class LogAnalyzerFieldComponent implements OnInit, OnDestroy {
5050
this.pageStart = 0;
5151
this.pageEnd = 100;
5252
this.pattern = dataChange.pattern.pattern;
53-
this.loadFields()
53+
this.loadFields();
5454
});
5555

5656
this.indexFieldController.$field.subscribe(async (field) => {

frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
<app-index-pattern-select
66
(indexPatternChange)="changeFields($event)"
77
[template]="'log-explorer'"
8-
[pattern]="pattern">
8+
[pattern]="pattern"
9+
(indexPatternInitialized) ="indexPatternLoaded($event)">
910
</app-index-pattern-select>
1011
</div>
1112
<div class="d-flex search-container flex-grow-5 align-items-center gap-1">
@@ -46,7 +47,9 @@
4647
<app-code-editor
4748
(execute)="executeSqlQuery($event)"
4849
(clearData)="onClearData()"
49-
[queryError]="sqlError">
50+
[queryError]="sqlError"
51+
[customKeywords]="indexPatternNames.concat(fieldsNames)"
52+
>
5053
</app-code-editor>
5154
</div>
5255

frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,34 @@ import {ResizeEvent} from 'angular-resizable-element';
66
import * as moment from 'moment';
77
import {Observable, Subject} from 'rxjs';
88
import {filter, takeUntil} from 'rxjs/operators';
9-
import {UtmToastService} from '../../../shared/alert/utm-toast.service';
10-
import {
11-
ElasticFilterDefaultTime
12-
} from '../../../shared/components/utm/filters/elastic-filter-time/elastic-filter-time.component';
13-
import {
14-
UtmFilterBehavior
15-
} from '../../../shared/components/utm/filters/utm-elastic-filter/shared/behavior/utm-filter.behavior';
16-
import {
17-
UtmTableDetailComponent
18-
} from '../../../shared/components/utm/table/utm-table/utm-table-detail/utm-table-detail.component';
9+
1910
import {ADMIN_ROLE} from '../../../shared/constants/global.constant';
20-
import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../shared/constants/log-analyzer.constant';
11+
import {ALERT_INDEX_PATTERN, LOG_INDEX_PATTERN} from '../../../shared/constants/main-index-pattern.constant';
2112
import {ITEMS_PER_PAGE} from '../../../shared/constants/pagination.constants';
13+
import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../shared/constants/log-analyzer.constant';
14+
2215
import {ElasticDataTypesEnum} from '../../../shared/enums/elastic-data-types.enum';
2316
import {ElasticOperatorsEnum} from '../../../shared/enums/elastic-operators.enum';
2417
import {DataNatureTypeEnum, NatureDataPrefixEnum} from '../../../shared/enums/nature-data.enum';
2518
import {ElasticDataExportService} from '../../../shared/services/elasticsearch/elastic-data-export.service';
2619
import {ElasticDataService} from '../../../shared/services/elasticsearch/elastic-data.service';
20+
import {LocalFieldService} from '../../../shared/services/elasticsearch/local-field.service';
2721
import {TimezoneFormatService} from '../../../shared/services/utm-timezone.service';
22+
23+
import {UtmToastService} from '../../../shared/alert/utm-toast.service';
24+
25+
import {ElasticFilterDefaultTime} from '../../../shared/components/utm/filters/elastic-filter-time/elastic-filter-time.component';
26+
import {UtmFilterBehavior} from '../../../shared/components/utm/filters/utm-elastic-filter/shared/behavior/utm-filter.behavior';
27+
import {UtmTableDetailComponent} from '../../../shared/components/utm/table/utm-table/utm-table-detail/utm-table-detail.component';
28+
2829
import {DatePipeDefaultOptions} from '../../../shared/types/date-pipe-default-options';
2930
import {ElasticSearchFieldInfoType} from '../../../shared/types/elasticsearch/elastic-search-field-info.type';
3031
import {ElasticFilterType} from '../../../shared/types/filter/elastic-filter.type';
3132
import {UtmIndexPattern} from '../../../shared/types/index-pattern/utm-index-pattern';
3233
import {UtmFieldType} from '../../../shared/types/table/utm-field.type';
3334
import {parseQueryParamsToFilter} from '../../../shared/util/query-params-to-filter.util';
34-
import {
35-
LogAnalyzerQueryCreateComponent
36-
} from '../../queries/log-analyzer-query-create/log-analyzer-query-create.component';
35+
36+
import {LogAnalyzerQueryCreateComponent} from '../../queries/log-analyzer-query-create/log-analyzer-query-create.component';
3737
import {IndexFieldController} from '../../shared/behaviors/index-field-controller.behavior';
3838
import {IndexPatternBehavior} from '../../shared/behaviors/index-pattern.behavior';
3939
import {LogFilterBehavior} from '../../shared/behaviors/log-filter.behavior';
@@ -62,6 +62,8 @@ export class LogAnalyzerViewComponent implements OnInit, OnDestroy {
6262
value: ['now-24h', 'now']
6363
}];
6464
selectedFields: ElasticSearchFieldInfoType[] = [{name: '@timestamp', type: ElasticDataTypesEnum.DATE}];
65+
fieldsNames: string[] = [];
66+
indexPatternNames: string[] = [];
6567
dataNature: string = DataNatureTypeEnum.EVENT;
6668
queryParams: any;
6769
counter: any;
@@ -99,7 +101,8 @@ export class LogAnalyzerViewComponent implements OnInit, OnDestroy {
99101
private elasticDataExportService: ElasticDataExportService,
100102
private timezoneFormatService: TimezoneFormatService,
101103
private logFilterBehavior: LogFilterBehavior,
102-
private router: Router) {
104+
private router: Router,
105+
private localFieldService: LocalFieldService) {
103106

104107
this.detailWidth = (this.pageWidth - 310);
105108
}
@@ -123,6 +126,7 @@ export class LogAnalyzerViewComponent implements OnInit, OnDestroy {
123126

124127
});
125128
this.dateFormat$ = this.timezoneFormatService.getDateFormatSubject();
129+
this.loadFieldNames();
126130
this.initExplorer();
127131
}
128132

@@ -468,4 +472,15 @@ export class LogAnalyzerViewComponent implements OnInit, OnDestroy {
468472
this.destroy$.next();
469473
this.destroy$.complete();
470474
}
475+
476+
loadFieldNames() {
477+
this.fieldsNames = [
478+
...this.localFieldService.getPatternStoredFields(ALERT_INDEX_PATTERN).map(f => f.name),
479+
...this.localFieldService.getPatternStoredFields(LOG_INDEX_PATTERN).map(f => f.name)
480+
];
481+
}
482+
483+
indexPatternLoaded(indexPatternNames: string[]) {
484+
this.indexPatternNames = indexPatternNames;
485+
}
471486
}

frontend/src/app/shared/components/code-editor/code-editor.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
class="editor-area"
4242
[options]="consoleOptions"
4343
[(ngModel)]="sqlQuery"
44-
(ngModelChange)="resetMessages()">
44+
(ngModelChange)="resetMessages()"
45+
(onInit)="onEditorInit()">>
4546
</ngx-monaco-editor>
4647
</div>
4748

frontend/src/app/shared/components/code-editor/code-editor.component.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,7 @@
6363
::ng-deep .monaco-editor {
6464
padding-top: 10px;
6565
}
66+
67+
.monaco-editor .suggest-widget .codicon {
68+
display: none !important;
69+
}

frontend/src/app/shared/components/code-editor/code-editor.component.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {
22
Component, EventEmitter,
3-
Input, OnChanges, OnInit, Output, SimpleChanges
3+
Input, OnInit, Output
44
} from '@angular/core';
55

66
interface ConsoleOptions {
77
value?: string;
8-
language?: string;
8+
language?: 'sql';
99
theme?: 'vs' | 'vs-dark' | 'hc-black' | string;
1010
minimap?: { enabled: boolean };
1111
renderLineHighlight?: 'none' | 'line' | 'gutter' | 'all';
@@ -20,6 +20,17 @@ interface ConsoleOptions {
2020
automaticLayout: boolean;
2121
}
2222

23+
const SQL_KEYWORDS = ['CREATE', 'DROP', 'ALTER', 'TRUNCATE',
24+
'SELECT', 'INSERT', 'UPDATE', 'DELETE',
25+
'COMMIT', 'ROLLBACK',
26+
'AND', 'OR', 'NOT', 'BETWEEN', 'IN', 'LIKE', 'EXISTS',
27+
'COUNT', 'SUM', 'AVG', 'MIN', 'MAX',
28+
'FROM', 'WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'DISTINCT',
29+
'JOIN', 'INNER', 'LEFT', 'RIGHT', 'FULL', 'UNION', 'INTERSECT',
30+
'NULL', 'TRUE', 'FALSE',
31+
'AS', 'CASE', 'WHEN', 'THEN', 'END'
32+
];
33+
2334
@Component({
2435
selector: 'app-code-editor',
2536
templateUrl: './code-editor.component.html',
@@ -30,6 +41,7 @@ export class CodeEditorComponent implements OnInit {
3041
@Output() execute = new EventEmitter<string>();
3142
@Output() clearData = new EventEmitter<void>();
3243
@Input() queryError: string | null = null;
44+
@Input() customKeywords: string[] = [];
3345
sqlQuery = '';
3446
errorMessage = '';
3547
successMessage = '';
@@ -57,6 +69,25 @@ export class CodeEditorComponent implements OnInit {
5769
this.consoleOptions = { ...this.defaultOptions, ...this.consoleOptions };
5870
}
5971

72+
onEditorInit() {
73+
monaco.languages.registerCompletionItemProvider('sql', {
74+
provideCompletionItems: () => {
75+
const allKeywords = Array.from(new Set([
76+
...SQL_KEYWORDS,
77+
...(this.customKeywords || [])
78+
]));
79+
80+
const suggestions: monaco.languages.CompletionItem[] = allKeywords.map(k => ({
81+
label: k,
82+
kind: monaco.languages.CompletionItemKind.Text,
83+
insertText: k,
84+
}));
85+
86+
return { suggestions };
87+
}
88+
});
89+
}
90+
6091
executeQuery(): void {
6192
this.resetMessages();
6293
const query = this.sqlQuery ? this.sqlQuery.trim() : '';

frontend/src/app/shared/components/utm/index-pattern/index-pattern-select/index-pattern-select.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export class IndexPatternSelectComponent implements OnInit {
1616
@Input() patterRegex;
1717
@Output() indexPatternChange = new EventEmitter<UtmIndexPattern>();
1818
@Input() template: 'default' | 'log-explorer' = 'default';
19+
@Output() indexPatternInitialized = new EventEmitter<string[]>();
1920
order: 'asc' | 'desc' = 'asc';
2021
@ViewChild('popover') popover: NgbPopover;
2122
indexPatternList = [];
@@ -67,6 +68,8 @@ export class IndexPatternSelectComponent implements OnInit {
6768
private onSuccess(data, headers, init) {
6869
this.patterns = data;
6970
this.indexPatternList = this.getListPatterns();
71+
const indexPatternNames = this.indexPatternList.map(f => f.name);
72+
this.indexPatternInitialized.emit(indexPatternNames);
7073
if (init) {
7174
this.pattern = this.patterns[0];
7275
this.indexPatternChange.emit(this.pattern);

frontend/src/environments/environment.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
export const environment = {
66
production: false,
7-
//SERVER_API_URL: 'https://192.168.1.18/',
8-
SERVER_API_URL: 'http://localhost:8080/',
7+
SERVER_API_URL: 'https://192.168.1.18/',
8+
//SERVER_API_URL: 'http://localhost:8080/',
99
SERVER_API_CONTEXT: '',
1010
SESSION_AUTH_TOKEN: window.location.host.split(':')[0].toLocaleUpperCase(),
1111
WEBSOCKET_URL: '//localhost:8080',

0 commit comments

Comments
 (0)