blob: 4db47a264ff1a91228a4aa3631091a3bfabcc691 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import { Component, OnInit, ElementRef, ViewChild, Input, OnDestroy } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import { LogsContainerService } from '@app/services/logs-container.service';
import { ServiceLogsHistogramDataService } from '@app/services/storage/service-logs-histogram-data.service';
import { AuditLogsGraphDataService } from '@app/services/storage/audit-logs-graph-data.service';
import { AppStateService } from '@app/services/storage/app-state.service';
import { TabsService } from '@app/services/storage/tabs.service';
import { AuditLog } from '@app/classes/models/audit-log';
import { ServiceLog } from '@app/classes/models/service-log';
import { LogTypeTab } from '@app/classes/models/log-type-tab';
import { BarGraph } from '@app/classes/models/bar-graph';
import { ActiveServiceLogEntry } from '@app/classes/active-service-log-entry';
import { ListItem } from '@app/classes/list-item';
import { HomogeneousObject, LogLevelObject } from '@app/classes/object';
import { LogsType, LogLevel } from '@app/classes/string';
import { FiltersPanelComponent } from '@app/components/filters-panel/filters-panel.component';
import { Subscription } from 'rxjs/Subscription';
import { LogsFilteringUtilsService } from '@app/services/logs-filtering-utils.service';
import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { commonFieldNames as auditLogsCommonFieldNames } from '@app/classes/models/audit-log';
@Component({
selector: 'logs-container',
templateUrl: './logs-container.component.html',
styleUrls: ['./logs-container.component.less']
})
export class LogsContainerComponent implements OnInit, OnDestroy {
private isFilterPanelFixedPostioned = false;
tabs: Observable<LogTypeTab[]> = this.tabsStorage.getAll().map((tabs: LogTypeTab[]) => {
return tabs.map((tab: LogTypeTab) => {
const params = this.logsFilteringUtilsService.getParamsFromActiveFilter(
tab.activeFilters, tab.appState.activeLogsType
);
return Object.assign({}, tab, {params});
});
});
logsType: LogsType;
serviceLogsHistogramData: HomogeneousObject<HomogeneousObject<number>>;
auditLogsGraphData: HomogeneousObject<HomogeneousObject<number>>;
serviceLogsHistogramColors: HomogeneousObject<string> = this.logsContainerService.logLevels.reduce((
currentObject: HomogeneousObject<string>, level: LogLevelObject
): HomogeneousObject<string> => {
return Object.assign({}, currentObject, {
[level.name]: level.color
});
}, {});
isServiceLogContextView = false;
private activeTabId$: BehaviorSubject<string> = new BehaviorSubject(
this.router.routerState.snapshot.root.firstChild && this.router.routerState.snapshot.root.firstChild.params.activeTab
);
@ViewChild('container') containerRef: ElementRef;
@ViewChild('filtersPanel') filtersPanelRef: FiltersPanelComponent;
@Input()
routerPath: string[] = ['/logs'];
private subscriptions: Subscription[] = [];
private paramsSyncInProgress: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
isServiceLogsFileView$: Observable<boolean> = this.appState.getParameter('isServiceLogsFileView');
get auditLogsCommonFieldNames() {
return auditLogsCommonFieldNames;
}
constructor(
private appState: AppStateService,
private tabsStorage: TabsService,
public logsContainerService: LogsContainerService,
private logsFilteringUtilsService: LogsFilteringUtilsService,
private serviceLogsHistogramStorage: ServiceLogsHistogramDataService,
private auditLogsGraphStorage: AuditLogsGraphDataService,
private router: Router,
private activatedRoute: ActivatedRoute
) {}
ngOnInit() {
// set te logsType when the activeLogsType state has changed
this.subscriptions.push(
this.appState.getParameter('activeLogsType').subscribe((value: LogsType) => this.logsType = value)
);
// set the hhistogramm data
this.subscriptions.push(
this.serviceLogsHistogramStorage.getAll().subscribe((data: BarGraph[]): void => {
this.serviceLogsHistogramData = this.logsContainerService.getGraphData(data, this.logsContainerService.logLevels.map((
level: LogLevelObject
): LogLevel => {
return level.name;
}));
})
);
// audit graph data set
this.subscriptions.push(
this.auditLogsGraphStorage.getAll().subscribe((data: BarGraph[]): void => {
this.auditLogsGraphData = this.logsContainerService.getGraphData(data);
})
);
// service log context flag subscription
this.subscriptions.push(
this.appState.getParameter('isServiceLogContextView').subscribe((value: boolean): void => {
this.isServiceLogContextView = value;
})
);
this.activatedRoute.params.first().map(params => params.activeTab).subscribe((tabId) => {
this.logsContainerService.setActiveTabById(tabId);
});
//// SYNC BETWEEN PARAMS AND FORM
// sync to filters form when the query params changed (only when there is no other way sync)
this.subscriptions.push(
this.activatedRoute.params.filter(() => !this.paramsSyncInProgress.getValue())
.subscribe(this.onParamsChange)
);
// Sync from form to params on form values change
this.subscriptions.push(
this.filtersForm.valueChanges
.filter(() => !this.logsContainerService.filtersFormSyncInProgress$.getValue())
.subscribe(this.onFiltersFormChange)
);
//// SYNC BETWEEN PARAMS AND FORM END
//// TAB CHANGE
// when the activeTabId$ behaviour subject change, this depends on the params' changes
this.subscriptions.push(
this.activeTabId$.distinctUntilChanged().subscribe(this.onActiveTabIdChange)
);
}
ngOnDestroy() {
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
}
get filtersForm(): FormGroup {
return this.logsContainerService.filtersForm;
};
get totalCount(): number {
return this.logsContainerService.totalCount;
}
get autoRefreshRemainingSeconds(): number {
return this.logsContainerService.autoRefreshRemainingSeconds;
}
get autoRefreshInterval(): number {
return this.logsContainerService.autoRefreshInterval;
}
get captureTimeRangeCache(): ListItem {
return this.logsContainerService.captureTimeRangeCache;
}
get autoRefreshMessageParams(): object {
return {
remainingSeconds: this.autoRefreshRemainingSeconds
};
}
/**
* The goal is to provide the single source for the parameters of 'xyz events found' message.
* @returns {Object}
*/
get totalEventsFoundMessageParams(): {totalCount: number} {
return {
totalCount: this.totalCount
};
}
get isServiceLogsFileView(): boolean {
return this.logsContainerService.isServiceLogsFileView;
}
get activeLog(): ActiveServiceLogEntry | null {
return this.logsContainerService.activeLog;
}
get auditLogs(): Observable<AuditLog[]> {
return this.logsContainerService.auditLogs;
}
get auditLogsColumns(): Observable<ListItem[]> {
return this.logsContainerService.auditLogsColumns;
}
get serviceLogs(): Observable<ServiceLog[]> {
return this.logsContainerService.serviceLogs;
}
get serviceLogsColumns(): Observable<ListItem[]> {
return this.logsContainerService.serviceLogsColumns;
}
//
// SECTION: TABS
//
/**
* Set the active params in the store corresponding to the URL param (activeTab)
* @param {string} tabId The 'activeTab' segment of the URL (eg.: #/logs/serviceLogs where the serviceLogs is the activeTab parameter)
*/
private onActiveTabIdChange = (tabId: string): void => {
this.logsContainerService.setActiveTabById(tabId);
}
//
// SECTION END: TABS
//
//
// SECTION: FILTER SYNCHRONIZATION
//
/**
* Turn on the 'query params in sync' flag, so that the query to form sync don't run.
* So when we actualize the query params to reflect the filters form values we have to turn of the back sync (query params change to form)
*/
private paramsSyncStart = (): void => {
this.paramsSyncInProgress.next(true);
}
/**
* Turn off the 'query params in sync' flag
*/
private paramsSyncStop = (): void => {
this.paramsSyncInProgress.next(false);
}
/**
* The goal is to make the app always bookmarkable.
* @param filters
*/
private syncFiltersToParams(filters): void {
this.activatedRoute.params.first().subscribe((routeParams) => {
const params = this.logsFilteringUtilsService.getParamsFromActiveFilter(
filters, this.logsContainerService.activeLogsType
);
this.paramsSyncStart(); // turn on the 'sync in progress' flag
this.router.navigate([params], { relativeTo: this.activatedRoute })
.then(this.paramsSyncStop, this.paramsSyncStop) // turn off the 'sync in progress' flag
.catch(this.paramsSyncStop); // turn off the 'sync in progress' flag
});
}
/**
* This will call the LogsContainerService to reset the filter form with the given values.
* It will add default values where it is missing from the object.
* @param values {[key: string]: any} The new values for the filter form
*/
private resetFiltersForm(values: {[key: string]: any}): void {
this.logsContainerService.resetFiltersForms({
...values
});
}
/**
* It will request the LogsContainerService to store the given filters to the given tab
* in order to apply these filters when there is no filter params in the URL.
* @param filters {[key: string]: any} The values for the filters form
* @param tabId string The tab where it should be stored (in activeFilters property)
*/
private syncFilterToTabStore(filters: {[key: string]: any}, tabId: string): void {
this.logsContainerService.syncFiltersToTabFilters(filters, tabId);
}
/**
* Handle the filters' form changes and sync it to the query parameters
* @param values The new filter values. This is the raw value of the form group
*/
private onFiltersFormChange = (filters): void => {
this.syncFiltersToParams(filters);
}
private onParamsChange = (params: {[key: string]: any}) => {
const {activeTab, ...filtersParams} = params;
if (activeTab !== this.activeTabId$.getValue()) { // tab change
this.tabsStorage.findInCollection((tab: LogTypeTab) => tab.id === params.activeTab)
.first()
.subscribe((tab) => {
if (tab) {
this.activeTabId$.next(activeTab);
}
});
} else { // filter change
const filtersFromParams: {[key: string]: any} = this.logsFilteringUtilsService.getFilterFromParams(
filtersParams,
this.logsContainerService.activeLogsType
);
const currentFormParams = this.logsFilteringUtilsService.getParamsFromActiveFilter(
this.filtersForm.value, this.logsContainerService.activeLogsType
);
const filtersFormControlNames = Object.keys(this.filtersForm.controls);
const hasChange = filtersFormControlNames.reduce(
(changed, key) => {
if (currentFormParams[key] === undefined && filtersParams[key] === undefined) {
return changed;
}
return (
changed
|| (currentFormParams[key] === undefined && filtersParams[key] !== undefined)
|| (currentFormParams[key] !== undefined && filtersParams[key] === undefined)
|| currentFormParams[key].toString() !== filtersParams[key].toString()
);
},
false
);
if (hasChange) {
// we don't have to reset the form with the new values when there is tab changes
// because the onActiveTabIdChange will call the setActiveTabById on LogsContainerService
// which will reset the form to the tab's activeFilters prop.
// If we do reset wvery time then the form will be reseted twice with every tab changes... not a big deal anyway
if (this.activeTabId$.getValue() === activeTab) {
this.resetFiltersForm(filtersFromParams);
}
this.syncFilterToTabStore(filtersFromParams, activeTab);
}
}
}
//
// SECTION END: FILTER SYNCHRONIZATION
//
setCustomTimeRange(startTime: number, endTime: number): void {
this.logsContainerService.setCustomTimeRange(startTime, endTime);
}
onSwitchTab(activeTab: LogTypeTab): void {
this.logsContainerService.switchTab(activeTab);
}
onCloseTab(activeTab: LogTypeTab, newActiveTab: LogTypeTab): void {
const activateNewTab: boolean = activeTab.isActive;
this.tabsStorage.deleteObjectInstance(activeTab);
if (activateNewTab && newActiveTab) {
this.router.navigate(['/logs', ...this.logsFilteringUtilsService.getNavigationForTab(newActiveTab)]);
}
}
//
// CAPTURE FEATURES
//
cancelCapture(): void {
this.logsContainerService.cancelCapture();
}
clearCaptureTimeRangeCache(): void {
if (this.captureTimeRangeCache) {
this.filtersForm.controls.timeRange.setValue(this.captureTimeRangeCache);
this.logsContainerService.captureTimeRangeCache = null;
}
}
onFilterPanelClear() {
this.syncFiltersToParams(this.filtersForm.getRawValue());
}
}