This is an automated email from the ASF dual-hosted git repository. ababiichuk pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push: new 79504f7 AMBARI-22835 Log Search UI: implement log level filter 79504f7 is described below commit 79504f74e83164a9c70713ea54cfc2f257d5be04 Author: aBabiichuk <ababiic...@hortonworks.com> AuthorDate: Sat Feb 3 13:46:48 2018 +0200 AMBARI-22835 Log Search UI: implement log level filter --- .../ambari-logsearch-web/src/app/app.module.ts | 6 +- .../src/app/classes/models/app-settings.ts | 8 +- .../src/app/classes/models/filter.ts | 8 +- .../src/app/classes/models/store.ts | 10 +- .../ambari-logsearch-web/src/app/classes/object.ts | 8 + .../app/classes/{models/filter.ts => settings.ts} | 17 +- .../ambari-logsearch-web/src/app/classes/string.ts | 2 + .../action-menu/action-menu.component.html | 14 +- .../action-menu/action-menu.component.spec.ts | 11 +- .../action-menu/action-menu.component.ts | 30 +- .../date-picker/date-picker.component.ts | 21 +- .../log-index-filter.component.html | 86 ++ .../log-index-filter.component.less} | 40 +- .../log-index-filter.component.spec.ts} | 26 +- .../log-index-filter/log-index-filter.component.ts | 177 +++ .../logs-container/logs-container.component.ts | 20 +- .../src/app/components/modal/modal.component.html | 5 +- .../src/app/components/modal/modal.component.less | 22 + .../src/app/components/modal/modal.component.ts | 9 +- .../service-logs-table.component.less | 2 +- .../timezone-picker.component.spec.ts | 6 +- .../timezone-picker/timezone-picker.component.ts | 8 +- .../src/app/components/variables.less | 5 +- .../ambari-logsearch-web/src/app/mock-data.ts | 1484 +++++++++++++++++++- .../app/services/component-generator.service.ts | 9 +- .../src/app/services/http-client.service.ts | 9 +- .../app/services/logs-container.service.spec.ts | 26 - .../src/app/services/logs-container.service.ts | 116 +- .../src/app/services/storage/reducers.service.ts | 2 - .../user-settings.service.spec.ts} | 36 +- .../src/app/services/user-settings.service.ts | 162 +++ .../src/app/services/utils.service.spec.ts | 24 + .../src/app/services/utils.service.ts | 26 + .../ambari-logsearch-web/src/assets/i18n/en.json | 13 +- 34 files changed, 2202 insertions(+), 246 deletions(-) diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts index 0a42994..5a2b59d 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts @@ -38,6 +38,7 @@ import {HttpClientService} from '@app/services/http-client.service'; import {UtilsService} from '@app/services/utils.service'; import {LogsContainerService} from '@app/services/logs-container.service'; import {ComponentGeneratorService} from '@app/services/component-generator.service'; +import {UserSettingsService} from '@app/services/user-settings.service'; import {AppSettingsService} from '@app/services/storage/app-settings.service'; import {AppStateService} from '@app/services/storage/app-state.service'; @@ -49,7 +50,6 @@ import {ServiceLogsTruncatedService} from '@app/services/storage/service-logs-tr import {GraphsService} from '@app/services/storage/graphs.service'; import {HostsService} from '@app/services/storage/hosts.service'; import {UserConfigsService} from '@app/services/storage/user-configs.service'; -import {FiltersService} from '@app/services/storage/filters.service'; import {ClustersService} from '@app/services/storage/clusters.service'; import {ComponentsService} from '@app/services/storage/components.service'; import {ServiceLogsFieldsService} from '@app/services/storage/service-logs-fields.service'; @@ -97,6 +97,7 @@ import {GraphLegendItemComponent} from '@app/components/graph-legend-item/graph- import {TimeLineGraphComponent} from '@app/components/time-line-graph/time-line-graph.component'; import {ContextMenuComponent} from '@app/components/context-menu/context-menu.component'; import {HistoryItemControlsComponent} from '@app/components/history-item-controls/history-item-controls.component'; +import {LogIndexFilterComponent} from '@app/components/log-index-filter/log-index-filter.component'; import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe'; import {TimerSecondsPipe} from '@app/pipes/timer-seconds.pipe'; @@ -161,6 +162,7 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR TimeLineGraphComponent, ContextMenuComponent, HistoryItemControlsComponent, + LogIndexFilterComponent, TimeZoneAbbrPipe, TimerSecondsPipe ], @@ -188,6 +190,7 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR UtilsService, LogsContainerService, ComponentGeneratorService, + UserSettingsService, AppSettingsService, AppStateService, AuditLogsService, @@ -198,7 +201,6 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR GraphsService, HostsService, UserConfigsService, - FiltersService, ClustersService, ComponentsService, ServiceLogsFieldsService, diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-settings.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-settings.ts index 11821a3..3ba5089 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-settings.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-settings.ts @@ -17,11 +17,15 @@ */ import * as moment from 'moment-timezone'; +import {HomogeneousObject} from '@app/classes/object'; +import {Filter} from '@app/classes/models/filter'; export interface AppSettings { timeZone: string; + logIndexFilters: HomogeneousObject<HomogeneousObject<Filter>>; } export const defaultSettings: AppSettings = { - timeZone: moment.tz.guess() -} + timeZone: moment.tz.guess(), + logIndexFilters: {} +}; diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/filter.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/filter.ts index c7ff662..b3e01aa 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/filter.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/filter.ts @@ -16,10 +16,12 @@ * limitations under the License. */ +import {LogLevel} from '@app/classes/string'; + export interface Filter { label: string; hosts: string[]; - defaultLevels: string[]; - overrideLevels: string[]; - expiryTime: string; + defaultLevels: LogLevel[]; + overrideLevels: LogLevel[]; + expiryTime: string | null; } diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts index f2d0696..1764c93 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts @@ -26,12 +26,11 @@ import {BarGraph} from '@app/classes/models/bar-graph'; import {Graph} from '@app/classes/models/graph'; import {NodeItem} from '@app/classes/models/node-item'; import {UserConfig} from '@app/classes/models/user-config'; -import {Filter} from '@app/classes/models/filter'; import {AuditLogField} from '@app/classes/models/audit-log-field'; import {ServiceLogField} from '@app/classes/models/service-log-field'; import {Tab} from '@app/classes/models/tab'; -export const storeActions = { +const storeActions = { 'ARRAY.ADD': 'ADD', 'ARRAY.ADD.START': 'ADD_TO_START', 'ARRAY.DELETE.PRIMITIVE': 'DELETE_PRIMITIVE', @@ -53,7 +52,6 @@ export interface AppStore { graphs: Graph[]; hosts: NodeItem[]; userConfigs: UserConfig[]; - filters: Filter[]; clusters: string[]; components: NodeItem[]; serviceLogsFields: ServiceLogField[]; @@ -144,9 +142,9 @@ export class ObjectModelService extends ModelService { } setParameter(key: string, value: any): void { - let payload = {}; - payload[key] = value; - this.setParameters(payload); + this.setParameters({ + [key]: value + }); } setParameters(params: any): void { diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/object.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/object.ts index 4d0c7f6..13cc4cc 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/object.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/object.ts @@ -16,4 +16,12 @@ * limitations under the License. */ +import {LogLevel} from '@app/classes/string'; + export type HomogeneousObject<T> = {[key: string]: T}; + +export interface LogLevelObject { + name: LogLevel; + label: string; + color: string; +} diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/filter.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/settings.ts similarity index 72% copy from ambari-logsearch/ambari-logsearch-web/src/app/classes/models/filter.ts copy to ambari-logsearch/ambari-logsearch-web/src/app/classes/settings.ts index c7ff662..dcef457 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/filter.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/settings.ts @@ -16,10 +16,17 @@ * limitations under the License. */ -export interface Filter { +import {HomogeneousObject} from '@app/classes/object'; + +export type LevelOverridesConfig = HomogeneousObject<{ + defaults: boolean; + overrides: boolean; +}> + +export type LogIndexFilterComponentConfig = LevelOverridesConfig & { + name: string; label: string; - hosts: string[]; - defaultLevels: string[]; - overrideLevels: string[]; - expiryTime: string; + hosts: string; + expiryTime: string | null; + hasOverrides?: boolean; } diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/string.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/string.ts index 21ff4ca..a8156e6 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/string.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/string.ts @@ -23,3 +23,5 @@ export type TimeRangeType = 'CURRENT' | 'LAST' | 'PAST'; export type SortingType = 'asc' | 'desc'; export type ScrollType = 'before' | 'after' | ''; + +export type LogLevel = 'FATAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE' | 'UNKNOWN'; diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html index 2e332d4..78fb277 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html @@ -19,9 +19,21 @@ <menu-button label="{{'topMenu.undo' | translate}}" [subItems]="undoItems" iconClass="fa fa-arrow-left" listClass="history-dropdown" (buttonClick)="undoLatest()" (selectItem)="undo($event)"></menu-button> <menu-button label="{{'topMenu.redo' | translate}}" [subItems]="redoItems" iconClass="fa fa-arrow-right" - listClass="history-dropdown" (buttonClick)="redoLatest()" (selectItem)="redo($event)">></menu-button> + listClass="history-dropdown" (buttonClick)="redoLatest()" (selectItem)="redo($event)"></menu-button> <menu-button label="{{'topMenu.history' | translate}}" [subItems]="historyItems" iconClass="fa fa-history" listClass="history-dropdown" [isRightAlign]="true" additionalLabelComponentSetter="getHistoryItemIcons"></menu-button> +<menu-button label="{{'topMenu.filter' | translate}}" iconClass="fa fa-filter" + (buttonClick)="openLogIndexFilter()"></menu-button> <menu-button label="{{'topMenu.refresh' | translate}}" iconClass="fa fa-refresh" (buttonClick)="refresh()"></menu-button> +<modal *ngIf="isLogIndexFilterDisplayed" (submit)="saveLogIndexFilter()" (cancel)="closeLogIndexFilter()" + (close)="closeLogIndexFilter()" [isExtraLargeModal]="true" [isSubmitDisabled]="isModalSubmitDisabled" + title="{{'logIndexFilter.title' | translate}}" submitButtonLabel="{{'modal.save' | translate}}"> + <ng-template> + <form [formGroup]="settingsForm"> + <log-index-filter formControlName="logIndexFilter" + (changeIsSubmitDisabled)="setModalSubmitDisabled($event)"></log-index-filter> + </form> + </ng-template> +</modal> diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts index ba53ee1..71676ac 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts @@ -18,6 +18,7 @@ import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {TranslationModules} from '@app/test-config.spec'; import {StoreModule} from '@ngrx/store'; import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service'; @@ -38,7 +39,9 @@ import {TabsService, tabs} from '@app/services/storage/tabs.service'; import {HistoryManagerService} from '@app/services/history-manager.service'; import {HttpClientService} from '@app/services/http-client.service'; import {LogsContainerService} from '@app/services/logs-container.service'; +import {UserSettingsService} from '@app/services/user-settings.service'; import {UtilsService} from '@app/services/utils.service'; +import {ModalComponent} from '@app/components/modal/modal.component'; import {ActionMenuComponent} from './action-menu.component'; @@ -57,6 +60,8 @@ describe('ActionMenuComponent', () => { }; TestBed.configureTestingModule({ imports: [ + FormsModule, + ReactiveFormsModule, ...TranslationModules, StoreModule.provideStore({ auditLogs, @@ -74,7 +79,10 @@ describe('ActionMenuComponent', () => { tabs }) ], - declarations: [ActionMenuComponent], + declarations: [ + ActionMenuComponent, + ModalComponent + ], providers: [ { provide: HttpClientService, @@ -82,6 +90,7 @@ describe('ActionMenuComponent', () => { }, HistoryManagerService, LogsContainerService, + UserSettingsService, UtilsService, AuditLogsService, ServiceLogsService, diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts index 351268c..21ce307 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts @@ -17,8 +17,10 @@ */ import {Component} from '@angular/core'; +import {FormGroup} from '@angular/forms'; import {LogsContainerService} from '@app/services/logs-container.service'; import {HistoryManagerService} from '@app/services/history-manager.service'; +import {UserSettingsService} from '@app/services/user-settings.service'; import {ListItem} from '@app/classes/list-item'; @Component({ @@ -28,7 +30,10 @@ import {ListItem} from '@app/classes/list-item'; }) export class ActionMenuComponent { - constructor(private logsContainer: LogsContainerService, private historyManager: HistoryManagerService) { + constructor( + private logsContainer: LogsContainerService, private historyManager: HistoryManagerService, + private settings: UserSettingsService + ) { } get undoItems(): ListItem[] { @@ -43,6 +48,16 @@ export class ActionMenuComponent { return this.historyManager.activeHistory; } + isLogIndexFilterDisplayed: boolean = false; + + settingsForm: FormGroup = this.settings.settingsFormGroup; + + isModalSubmitDisabled: boolean = true; + + setModalSubmitDisabled(isDisabled: boolean): void { + this.isModalSubmitDisabled = isDisabled; + } + undoLatest(): void { this.historyManager.undo(this.undoItems[0]); } @@ -63,4 +78,17 @@ export class ActionMenuComponent { this.logsContainer.loadLogs(); } + openLogIndexFilter(): void { + this.isLogIndexFilterDisplayed = true; + } + + closeLogIndexFilter(): void { + this.isLogIndexFilterDisplayed = false; + } + + saveLogIndexFilter(): void { + this.isLogIndexFilterDisplayed = false; + this.settings.saveIndexFilterConfig(); + } + } diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/date-picker/date-picker.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/date-picker/date-picker.component.ts index e33d71e..93ebe37 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/date-picker/date-picker.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/date-picker/date-picker.component.ts @@ -20,7 +20,7 @@ import { Component, OnInit, OnChanges, OnDestroy, SimpleChanges, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core'; import * as $ from 'jquery'; -import {Moment} from 'moment-timezone'; +import * as moment from 'moment'; import '@vendor/js/bootstrap-datetimepicker.min'; import {AppSettingsService} from '@app/services/storage/app-settings.service'; @@ -31,16 +31,16 @@ import {AppSettingsService} from '@app/services/storage/app-settings.service'; export class DatePickerComponent implements OnInit, OnChanges, OnDestroy { constructor(private appSettings: AppSettingsService) { - appSettings.getParameter('timeZone').subscribe((value: string): void => { + } + + ngOnInit(): void { + this.appSettings.getParameter('timeZone').subscribe((value: string): void => { this.destroyDatePicker(); this.timeZone = value; if (this.datePickerElement) { this.createDatePicker(); } }); - } - - ngOnInit(): void { this.createDatePicker(); } @@ -56,10 +56,10 @@ export class DatePickerComponent implements OnInit, OnChanges, OnDestroy { /** * Value of time input field passed from parent component - * @type {Moment} + * @type {Moment|Date|string} */ @Input() - time: Moment; + time: moment.Moment | Date | string; @Output() timeChange: EventEmitter<number> = new EventEmitter(); @@ -89,10 +89,11 @@ export class DatePickerComponent implements OnInit, OnChanges, OnDestroy { /** * Set value to time input field - * @param {Moment} time + * @param {Moment|Date|string} time */ - private setTime(time: Moment): void { - this.datePickerElement.data('DateTimePicker').date(time); + private setTime(time: moment.Moment | Date | string): void { + const timeMoment = moment.isMoment(time) ? time : moment(time); + this.datePickerElement.data('DateTimePicker').date(timeMoment); } } diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.html new file mode 100644 index 0000000..50d7742 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.html @@ -0,0 +1,86 @@ +<!-- + 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. +--> + +<div>{{'logIndexFilter.caption' | translate}}</div> +<h5>{{'logIndexFilter.selectCluster' | translate}}</h5> +<dropdown-button [options]="clustersListItems | async" (selectItem)="setActiveCluster($event)" + label="{{'logIndexFilter.select' | translate}}" buttonClass="btn-default"></dropdown-button> +<table *ngIf="activeClusterName" class="table table-hover"> + <thead> + <tr> + <th class="component-column">{{'filter.components' | translate}}</th> + <th *ngFor="let column of columns" class="checkbox-column"> + <input type="checkbox" attr.id="{{column.name}}" + [attr.checked]="isAllComponentsCheckedForLevel(column.name) ? 'checked' : null" + (change)="processAllComponentsForLevel(column.name, $event.target.checked)"> + <label attr.for="{{column.name}}"> + <graph-legend-item label="{{column.label | translate}}" color="{{column.color}}"></graph-legend-item> + </label> + </th> + <th class="override-column">{{'logIndexFilter.override' | translate}}</th> + </tr> + </thead> + <tbody> + <ng-container *ngFor="let component of activeClusterConfigs"> + <tr> + <td class="component-column"> + <input type="checkbox" attr.id="{{component.name}}" + [attr.checked]="isAllLevelsCheckedForComponent(component.name) ? 'checked' : null" + (change)="processAllLevelsForComponent(component.name, $event.target.checked)"> + <label attr.for="{{component.name}}">{{component.label}}</label> + </td> + <td *ngFor="let levelName of levelNames" class="checkbox-column"> + <input type="checkbox" attr.id="{{getCheckBoxId(component.name, levelName)}}" + [(ngModel)]="component[levelName].defaults" (change)="updateValue()"> + <label attr.for="{{getCheckBoxId(component.name, levelName)}}"> </label> + </td> + <td class="override-column"> + <span class="overrides-toggle">{{'logIndexFilter.addHosts' | translate}}</span> + </td> + </tr> + <tr> <!--overrides row --> + <td class="component-column"> + <input type="checkbox" attr.id="{{component.name}}_overrides" + [attr.checked]="isAllLevelsCheckedForComponent(component.name, true) ? 'checked' : null" + (change)="processAllLevelsForComponent(component.name, $event.target.checked, true)"> + <label attr.for="{{component.name}}_overrides"> </label> + </td> + <td *ngFor="let levelName of levelNames" class="checkbox-column"> + <input type="checkbox" attr.id="{{getCheckBoxId(component.name, levelName, true)}}" + [(ngModel)]="component[levelName].overrides" (change)="updateValue()"> + <label attr.for="{{getCheckBoxId(component.name, levelName, true)}}"> </label> + </td> + <td class="override-column"> + <div class="col-md-12 row"> + <div class="col-md-6"> + <div class="text-uppercase">{{'logIndexFilter.hostname' | translate}}</div> + <input type="text" class="form-control" [(ngModel)]="component.hosts" (change)="updateValue()"> + <!-- TODO implement typeahead similar to search box + (some functionality and appearance most likely will be shared) --> + <!-- <input type="text" class="form-control" [(ngModel)]="component.hosts" (change)="updateValue()" + [typeahead]="hosts | async" typeaheadOptionField="name"> --> + </div> + <div class=" col-md-6"> + <div class="text-uppercase">{{'logIndexFilter.expiryDate' | translate}}</div> + <date-picker [time]="component.expiryTime" (timeChange)="setExpiryTime($event, component)"></date-picker> + </div> + </div> + </td> + </tr> + </ng-container> + </tbody> +</table> diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/filters.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.less similarity index 62% rename from ambari-logsearch/ambari-logsearch-web/src/app/services/storage/filters.service.ts rename to ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.less index 493e2e6..f932258 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/filters.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.less @@ -16,18 +16,38 @@ * limitations under the License. */ +@import '../mixins'; -import {Injectable} from '@angular/core'; -import {Store} from '@ngrx/store'; -import {AppStore, CollectionModelService, getCollectionReducer} from '@app/classes/models/store'; +table { + display: block; + max-height: 70vh; + overflow-y: auto; + table-layout: fixed; -export const modelName = 'filters'; + .component-column { + width: auto; + } + + .checkbox-column { + width: 100px; + padding-left: 0; + padding-right: 0; -@Injectable() -export class FiltersService extends CollectionModelService { - constructor(store: Store<AppStore>) { - super(modelName, store); + /deep/ graph-legend-item { + padding-right: 1px; + } + } + + .override-column { + width: 32%; + padding-right: 0; } -} -export const filters = getCollectionReducer(modelName); + .overrides-toggle { + .clickable-item; + } + + input[type=checkbox] + label { + font-size: @table-font-size; + } +} diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.spec.ts similarity index 81% copy from ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts copy to ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.spec.ts index ba53ee1..a236686 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.spec.ts @@ -18,6 +18,7 @@ import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {FormsModule} from '@angular/forms'; import {TranslationModules} from '@app/test-config.spec'; import {StoreModule} from '@ngrx/store'; import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service'; @@ -35,16 +36,19 @@ import {ComponentsService, components} from '@app/services/storage/components.se import {HostsService, hosts} from '@app/services/storage/hosts.service'; import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service'; import {TabsService, tabs} from '@app/services/storage/tabs.service'; -import {HistoryManagerService} from '@app/services/history-manager.service'; +import {ComponentGeneratorService} from '@app/services/component-generator.service'; import {HttpClientService} from '@app/services/http-client.service'; import {LogsContainerService} from '@app/services/logs-container.service'; +import {UserSettingsService} from '@app/services/user-settings.service'; import {UtilsService} from '@app/services/utils.service'; +import {DropdownButtonComponent} from '@app/components/dropdown-button/dropdown-button.component'; +import {DropdownListComponent} from '@app/components/dropdown-list/dropdown-list.component'; -import {ActionMenuComponent} from './action-menu.component'; +import {LogIndexFilterComponent} from './log-index-filter.component'; -describe('ActionMenuComponent', () => { - let component: ActionMenuComponent; - let fixture: ComponentFixture<ActionMenuComponent>; +describe('LogIndexFilterComponent', () => { + let component: LogIndexFilterComponent; + let fixture: ComponentFixture<LogIndexFilterComponent>; beforeEach(async(() => { const httpClient = { @@ -57,6 +61,7 @@ describe('ActionMenuComponent', () => { }; TestBed.configureTestingModule({ imports: [ + FormsModule, ...TranslationModules, StoreModule.provideStore({ auditLogs, @@ -74,14 +79,19 @@ describe('ActionMenuComponent', () => { tabs }) ], - declarations: [ActionMenuComponent], + declarations: [ + LogIndexFilterComponent, + DropdownButtonComponent, + DropdownListComponent + ], providers: [ + ComponentGeneratorService, { provide: HttpClientService, useValue: httpClient }, - HistoryManagerService, LogsContainerService, + UserSettingsService, UtilsService, AuditLogsService, ServiceLogsService, @@ -103,7 +113,7 @@ describe('ActionMenuComponent', () => { })); beforeEach(() => { - fixture = TestBed.createComponent(ActionMenuComponent); + fixture = TestBed.createComponent(LogIndexFilterComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.ts new file mode 100644 index 0000000..a65555f --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.ts @@ -0,0 +1,177 @@ +/** + * 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, Output, EventEmitter, forwardRef} from '@angular/core'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; +import {Observable} from 'rxjs/Observable'; +import 'rxjs/add/operator/map'; +import {Moment} from 'moment'; +import {ListItem} from '@app/classes/list-item'; +import {HomogeneousObject, LogLevelObject} from '@app/classes/object'; +import {LogIndexFilterComponentConfig} from '@app/classes/settings'; +import {LogLevel} from '@app/classes/string'; +import {LogsContainerService} from '@app/services/logs-container.service'; +import {UserSettingsService} from '@app/services/user-settings.service'; +import {UtilsService} from '@app/services/utils.service'; +import {ClustersService} from '@app/services/storage/clusters.service'; +import {HostsService} from '@app/services/storage/hosts.service'; + +@Component({ + selector: 'log-index-filter', + templateUrl: './log-index-filter.component.html', + styleUrls: ['./log-index-filter.component.less'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => LogIndexFilterComponent), + multi: true + } + ] +}) +export class LogIndexFilterComponent implements OnInit, ControlValueAccessor { + + constructor( + private logsContainer: LogsContainerService, private settingsService: UserSettingsService, + private utils: UtilsService, private clustersStorage: ClustersService, private hostsStorage: HostsService + ) { + } + + ngOnInit() { + this.changeIsSubmitDisabled.emit(true); + this.clusters.subscribe((clusters: string[]) => this.settingsService.loadIndexFilterConfig(clusters)); + } + + @Output() + changeIsSubmitDisabled: EventEmitter<boolean> = new EventEmitter(); + + private onChange: (fn: any) => void; + + readonly columns: LogLevelObject[] = this.logsContainer.logLevels; + + readonly levelNames: LogLevel[] = this.columns.map((level: LogLevelObject): LogLevel => level.name); + + clusters: Observable<string[]> = this.clustersStorage.getAll(); + + hosts: Observable<string[]> = this.hostsStorage.getAll(); + + clustersListItems: Observable<ListItem[]> = this.clusters.map((clusterNames: string[]): ListItem[] => { + return clusterNames.map(this.utils.getListItemFromString); + }); + + activeClusterName: string = ''; + + /** + * Configs for all clusters + */ + private configs: HomogeneousObject<LogIndexFilterComponentConfig[]>; + + /** + * Configs for selected cluster + * @returns {LogIndexFilterComponentConfig[]} + */ + get activeClusterConfigs(): LogIndexFilterComponentConfig[] { + return this.configs[this.activeClusterName]; + } + + /** + * Select or unselect checkboxes for all log levels for given component + * @param {string} componentName + * @param {boolean} isChecked + * @param {boolean} isOverride - indicates whether levels for override are processed + */ + processAllLevelsForComponent(componentName: string, isChecked: boolean, isOverride: boolean = false): void { + const componentConfig = this.getComponentConfigs(componentName), + key = isOverride ? 'overrides' : 'defaults'; + this.levelNames.forEach((levelName: LogLevel) => componentConfig[levelName][key] = isChecked); + this.updateValue(); + } + + /** + * Select or unselect checkboxes for all components for given log level + * @param {LogLevel} levelName + * @param {boolean} isChecked + */ + processAllComponentsForLevel(levelName: LogLevel, isChecked: boolean): void { + this.activeClusterConfigs.forEach((component: LogIndexFilterComponentConfig): void => { + component[levelName].defaults = isChecked; + component[levelName].overrides = isChecked; + }); + this.updateValue(); + } + + /** + * Indicates whether all log levels for given component are checked + * @param {string} componentName + * @param {string} isOverride - indicates whether levels for override are overviewed + * @returns {boolean} + */ + isAllLevelsCheckedForComponent(componentName: string, isOverride: boolean = false): boolean { + const componentConfig = this.getComponentConfigs(componentName), + key = isOverride ? 'overrides' : 'defaults'; + return this.levelNames.every((levelName: LogLevel): boolean => componentConfig[levelName][key]); + } + + /** + * Indicates whether all components for given log level are checked + * @param {LogLevel} levelName + * @returns {boolean} + */ + isAllComponentsCheckedForLevel(levelName: LogLevel): boolean { + return this.activeClusterConfigs.every((component: LogIndexFilterComponentConfig): boolean => { + return component[levelName].defaults; + }); + } + + setActiveCluster(clusterName: string): void { + this.activeClusterName = clusterName; + this.changeIsSubmitDisabled.emit(false); + } + + getCheckBoxId(componentName: string, levelName: string, isOverride: boolean = false): string { + return `component_${componentName}_level_${levelName}${isOverride ? '_override' : ''}`; + } + + setExpiryTime(time: Moment, componentConfig): void { + componentConfig.expiryTime = time.toISOString(); + } + + private getComponentConfigs(componentName: string) { + return this.activeClusterConfigs.find((component: LogIndexFilterComponentConfig): boolean => { + return component.name === componentName; + }); + } + + writeValue(filters: HomogeneousObject<LogIndexFilterComponentConfig[]>): void { + this.configs = filters; + this.updateValue(); + } + + registerOnChange(callback: any): void { + this.onChange = callback; + } + + registerOnTouched(): void { + } + + updateValue(): void { + if(this.onChange) { + this.onChange(this.configs); + } + } + +} diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts index d853bb5..e8ccc08 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts @@ -30,8 +30,8 @@ import {Tab} from '@app/classes/models/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} from '@app/classes/object'; -import {LogsType} from '@app/classes/string'; +import {HomogeneousObject, LogLevelObject} from '@app/classes/object'; +import {LogsType, LogLevel} from '@app/classes/string'; import {FiltersPanelComponent} from "@app/components/filters-panel/filters-panel.component"; @Component({ @@ -52,7 +52,11 @@ export class LogsContainerComponent implements OnInit { this.logsContainer.loadColumnsNames(); this.appState.getParameter('activeLogsType').subscribe((value: LogsType) => this.logsType = value); this.serviceLogsHistogramStorage.getAll().subscribe((data: BarGraph[]): void => { - this.serviceLogsHistogramData = this.logsContainer.getGraphData(data, Object.keys(this.logsContainer.colors)); + this.serviceLogsHistogramData = this.logsContainer.getGraphData(data, this.logsContainer.logLevels.map(( + level: LogLevelObject + ): LogLevel => { + return level.name; + })); }); this.auditLogsGraphStorage.getAll().subscribe((data: BarGraph[]): void => { this.auditLogsGraphData = this.logsContainer.getGraphData(data); @@ -88,9 +92,13 @@ export class LogsContainerComponent implements OnInit { auditLogsGraphData: HomogeneousObject<HomogeneousObject<number>>; - get serviceLogsHistogramColors(): HomogeneousObject<string> { - return this.logsContainer.colors; - } + serviceLogsHistogramColors: HomogeneousObject<string> = this.logsContainer.logLevels.reduce(( + currentObject: HomogeneousObject<string>, level: LogLevelObject + ): HomogeneousObject<string> => { + return Object.assign({}, currentObject, { + [level.name]: level.color + }); + }, {}); get autoRefreshRemainingSeconds(): number { return this.logsContainer.autoRefreshRemainingSeconds; diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html index 398a429..799b840 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html @@ -17,7 +17,9 @@ <div class="modal-backdrop in"></div> <div class="modal in"> - <div [ngClass]="{'modal-dialog': true, 'modal-sm': isSmallModal, 'modal-lg': isLargeModal}"> + <div [ngClass]="{ + 'modal-dialog': true, 'modal-sm': isSmallModal, 'modal-lg': isLargeModal, 'modal-xl': isExtraLargeModal + }"> <div class="modal-content"> <div *ngIf="showHeader" class="modal-header"> <button *ngIf="showCloseButton" type="button" class="close" data-dismiss="modal" (click)="onClose()"> @@ -33,6 +35,7 @@ <button *ngIf="showCancelButton" class="btn {{cancelButtonClassName}}" (click)="onCancel()">{{cancelButtonLabel | translate}}</button> <button *ngIf="showSubmitButton" class="btn {{submitButtonClassName}}" + [attr.disabled]="isSubmitDisabled ? 'disabled' : null" (click)="onSubmit()">{{submitButtonLabel | translate}}</button> </div> </div> diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.less new file mode 100644 index 0000000..9d1d642 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.less @@ -0,0 +1,22 @@ +/** + * 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 '../variables'; + +.modal-xl { + width: @large-modal-width; +} diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts index a5df53f..a724086 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts @@ -21,7 +21,8 @@ import * as $ from 'jquery'; @Component({ selector: 'modal', - templateUrl: './modal.component.html' + templateUrl: './modal.component.html', + styleUrls: ['./modal.component.less'] }) export class ModalComponent implements OnInit, AfterViewInit { @@ -81,6 +82,12 @@ export class ModalComponent implements OnInit, AfterViewInit { @Input() isLargeModal: boolean = false; + @Input() + isExtraLargeModal: boolean = false; + + @Input() + isSubmitDisabled: boolean = false; + @ContentChild(TemplateRef) bodyTemplate; diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.less index 27fafe5..61a479e 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.less +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.less @@ -192,7 +192,7 @@ .log-list { color: @base-font-color; border-bottom: 1px solid @log-list-border-color; - font-size: @log-list-font-size; + font-size: @table-font-size; .log-date-row { background: @list-header-background-color; padding: @log-list-row-data-padding; diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts index 3d79b46..b522562 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts @@ -36,6 +36,8 @@ import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/s import {TabsService, tabs} from '@app/services/storage/tabs.service'; import {HttpClientService} from '@app/services/http-client.service'; import {LogsContainerService} from '@app/services/logs-container.service'; +import {UserSettingsService} from '@app/services/user-settings.service'; +import {UtilsService} from '@app/services/utils.service'; import {AuthService} from '@app/services/auth.service'; import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe'; import {ModalComponent} from '@app/components/modal/modal.component'; @@ -98,7 +100,9 @@ describe('TimeZonePickerComponent', () => { useValue: httpClient }, LogsContainerService, - AuthService + AuthService, + UserSettingsService, + UtilsService ], }) .compileComponents(); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.ts index 98758ab..a8a3480 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.ts @@ -20,6 +20,7 @@ import {Component, OnInit} from '@angular/core'; import * as $ from 'jquery'; import '@vendor/js/WorldMapGenerator.min'; import {AppSettingsService} from '@app/services/storage/app-settings.service'; +import {UserSettingsService} from '@app/services/user-settings.service'; @Component({ selector: 'timezone-picker', @@ -28,7 +29,7 @@ import {AppSettingsService} from '@app/services/storage/app-settings.service'; }) export class TimeZonePickerComponent implements OnInit { - constructor(private appSettings: AppSettingsService) { + constructor(private appSettings: AppSettingsService, private settingsService: UserSettingsService) { } ngOnInit() { @@ -72,10 +73,7 @@ export class TimeZonePickerComponent implements OnInit { setTimeZone(): void { const timeZone = this.timeZoneSelect.val(); - - // TODO replace with setTimeZone() method call from settings service as soon as it's implemented - this.appSettings.setParameter('timeZone', timeZone); - + this.settingsService.setTimeZone(timeZone); this.setTimeZonePickerDisplay(false); } diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less index f26f5ca..6f403a5 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less @@ -65,13 +65,16 @@ // Table @table-border-color: #EEE; +@table-font-size: 13px; // Graph @graph-padding: .5rem; // Log list @log-list-row-data-padding: 8px; -@log-list-font-size: 13px; @log-list-row-hover-background-color: #E7F6FC; @log-list-row-hover-border-color: #A7DFF2; @log-list-border-color: rgb(238, 238, 238); + +// Modals +@large-modal-width: 1200px; diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts b/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts index 1e1246b..fd4270b 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts @@ -1068,63 +1068,1437 @@ export const mockData = { configurationUploaded: true } }, - userconfig: { - userConfigList: [ - { - id: 'c0', - userName: 'admin', - filtername: 'service', - values: 'hdfs', - shareNameList: [ - 's0', - 's1' - ], - rowType: 'history' - }, - { - id: 'c0', - userName: 'user', - filtername: 'component', - values: 'namenode', - shareNameList: [ - 's2', - 's3' - ], - rowType: 'history' - } - ], + shipper: { filters: { - filter0: { - label: 'filter0', - hosts: [ - 'h0', - 'h1' - ], - defaultLevels: [ - 'l0', - 'l1' - ], - overrideLevels: [ - 'l2', - 'l3' - ], - expiryTime: '2017-05-29T11:30:22.531Z' + cl0: { + level: { + filter: { + ambari_agent: { + label: 'ambari_agent', + hosts: [ + 'h0' + ], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [ + 'FATAL' + ], + expiryTime: currentTime.clone().add(1, 'd').toISOString() + }, + ambari_alerts: { + label: 'ambari_alerts', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_audit: { + label: 'ambari_audit', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_config_changes: { + label: 'ambari_config_changes', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_eclipselink: { + label: 'ambari_eclipselink', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server: { + label: 'ambari_server', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server_check_database: { + label: 'ambari_server_check_database', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_collector: { + label: 'ams_collector', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_grafana: { + label: 'ams_grafana', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_master: { + label: 'ams_hbase_master', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_regionserver: { + label: 'ams_hbase_regionserver', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_monitor: { + label: 'ams_monitor', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_datanode: { + label: 'hdfs_datanode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_journalnode: { + label: 'hdfs_journalnode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_namenode: { + label: 'hdfs_namenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_nfs3: { + label: 'hdfs_nfs3', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_secondarynamenode: { + label: 'hdfs_secondarynamenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_zkfc: { + label: 'hdfs_zkfc', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + infra_solr: { + label: 'infra_solr', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_app: { + label: 'logsearch_app', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_feeder: { + label: 'logsearch_feeder', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_perf: { + label: 'logsearch_perf', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + zookeeper: { + label: 'zookeeper', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + } + } + } }, - filter1: { - label: 'filter1', - hosts: [ - 'h1', - 'h2' - ], - defaultLevels: [ - 'l4', - 'l5' - ], - overrideLevels: [ - 'l6', - 'l7' - ], - expiryTime: '2017-05-30T11:30:22.531Z' + cl1: { + level: { + filter: { + ambari_agent: { + label: 'ambari_agent', + hosts: [ + 'h0' + ], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [ + 'FATAL' + ], + expiryTime: currentTime.clone().add(1, 'd').toISOString() + }, + ambari_alerts: { + label: 'ambari_alerts', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_audit: { + label: 'ambari_audit', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_config_changes: { + label: 'ambari_config_changes', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_eclipselink: { + label: 'ambari_eclipselink', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server: { + label: 'ambari_server', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server_check_database: { + label: 'ambari_server_check_database', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_collector: { + label: 'ams_collector', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_grafana: { + label: 'ams_grafana', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_master: { + label: 'ams_hbase_master', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_regionserver: { + label: 'ams_hbase_regionserver', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_monitor: { + label: 'ams_monitor', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_datanode: { + label: 'hdfs_datanode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_journalnode: { + label: 'hdfs_journalnode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_namenode: { + label: 'hdfs_namenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_nfs3: { + label: 'hdfs_nfs3', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_secondarynamenode: { + label: 'hdfs_secondarynamenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_zkfc: { + label: 'hdfs_zkfc', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + infra_solr: { + label: 'infra_solr', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_app: { + label: 'logsearch_app', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_feeder: { + label: 'logsearch_feeder', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_perf: { + label: 'logsearch_perf', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + zookeeper: { + label: 'zookeeper', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + } + } + } + }, + cl2: { + level: { + filter: { + ambari_agent: { + label: 'ambari_agent', + hosts: [ + 'h0' + ], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [ + 'FATAL' + ], + expiryTime: currentTime.clone().add(1, 'd').toISOString() + }, + ambari_alerts: { + label: 'ambari_alerts', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_audit: { + label: 'ambari_audit', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_config_changes: { + label: 'ambari_config_changes', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_eclipselink: { + label: 'ambari_eclipselink', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server: { + label: 'ambari_server', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server_check_database: { + label: 'ambari_server_check_database', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_collector: { + label: 'ams_collector', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_grafana: { + label: 'ams_grafana', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_master: { + label: 'ams_hbase_master', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_regionserver: { + label: 'ams_hbase_regionserver', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_monitor: { + label: 'ams_monitor', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_datanode: { + label: 'hdfs_datanode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_journalnode: { + label: 'hdfs_journalnode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_namenode: { + label: 'hdfs_namenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_nfs3: { + label: 'hdfs_nfs3', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_secondarynamenode: { + label: 'hdfs_secondarynamenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_zkfc: { + label: 'hdfs_zkfc', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + infra_solr: { + label: 'infra_solr', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_app: { + label: 'logsearch_app', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_feeder: { + label: 'logsearch_feeder', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_perf: { + label: 'logsearch_perf', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + zookeeper: { + label: 'zookeeper', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + } + } + } + }, + cl3: { + level: { + filter: { + ambari_agent: { + label: 'ambari_agent', + hosts: [ + 'h0' + ], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [ + 'FATAL' + ], + expiryTime: currentTime.clone().add(1, 'd').toISOString() + }, + ambari_alerts: { + label: 'ambari_alerts', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_audit: { + label: 'ambari_audit', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_config_changes: { + label: 'ambari_config_changes', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_eclipselink: { + label: 'ambari_eclipselink', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server: { + label: 'ambari_server', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server_check_database: { + label: 'ambari_server_check_database', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_collector: { + label: 'ams_collector', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_grafana: { + label: 'ams_grafana', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_master: { + label: 'ams_hbase_master', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_regionserver: { + label: 'ams_hbase_regionserver', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_monitor: { + label: 'ams_monitor', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_datanode: { + label: 'hdfs_datanode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_journalnode: { + label: 'hdfs_journalnode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_namenode: { + label: 'hdfs_namenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_nfs3: { + label: 'hdfs_nfs3', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_secondarynamenode: { + label: 'hdfs_secondarynamenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_zkfc: { + label: 'hdfs_zkfc', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + infra_solr: { + label: 'infra_solr', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_app: { + label: 'logsearch_app', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_feeder: { + label: 'logsearch_feeder', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_perf: { + label: 'logsearch_perf', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + zookeeper: { + label: 'zookeeper', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + } + } + } + }, + cl4: { + level: { + filter: { + ambari_agent: { + label: 'ambari_agent', + hosts: [ + 'h0' + ], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [ + 'FATAL' + ], + expiryTime: currentTime.clone().add(1, 'd').toISOString() + }, + ambari_alerts: { + label: 'ambari_alerts', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_audit: { + label: 'ambari_audit', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_config_changes: { + label: 'ambari_config_changes', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_eclipselink: { + label: 'ambari_eclipselink', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server: { + label: 'ambari_server', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ambari_server_check_database: { + label: 'ambari_server_check_database', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_collector: { + label: 'ams_collector', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_grafana: { + label: 'ams_grafana', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_master: { + label: 'ams_hbase_master', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_hbase_regionserver: { + label: 'ams_hbase_regionserver', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + ams_monitor: { + label: 'ams_monitor', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_datanode: { + label: 'hdfs_datanode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_journalnode: { + label: 'hdfs_journalnode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_namenode: { + label: 'hdfs_namenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_nfs3: { + label: 'hdfs_nfs3', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_secondarynamenode: { + label: 'hdfs_secondarynamenode', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + hdfs_zkfc: { + label: 'hdfs_zkfc', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + infra_solr: { + label: 'infra_solr', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_app: { + label: 'logsearch_app', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_feeder: { + label: 'logsearch_feeder', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + logsearch_perf: { + label: 'logsearch_perf', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + }, + zookeeper: { + label: 'zookeeper', + hosts: [], + defaultLevels: [ + 'FATAL', + 'ERROR', + 'WARN', + 'INFO' + ], + overrideLevels: [], + expiryTime: null + } + } + } } }, names: [] diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.ts index 1f82367..bf06525 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.ts @@ -22,6 +22,7 @@ import {ComponentsService} from '@app/services/storage/components.service'; import {LogsContainerService} from '@app/services/logs-container.service'; import {NodeBarComponent} from '@app/components/node-bar/node-bar.component'; import {HistoryItemControlsComponent} from '@app/components/history-item-controls/history-item-controls.component'; +import {LogLevelObject} from '@app/classes/object'; @Injectable() export class ComponentGeneratorService { @@ -29,6 +30,10 @@ export class ComponentGeneratorService { constructor(private resolver: ComponentFactoryResolver, private hostsStorage: HostsService, private componentsStorage: ComponentsService, private logsContainer: LogsContainerService) { } + private get logLevels(): LogLevelObject[] { + return this.logsContainer.logLevels; + } + private createComponent(type: any, container: ViewContainerRef, properties?: any): void { const factory = this.resolver.resolveComponentFactory(type); container.clear(); @@ -43,7 +48,7 @@ export class ComponentGeneratorService { const selectedHost = hosts.find(host => host.name === hostName); data = selectedHost.logLevelCount.map(event => { return { - color: this.logsContainer.colors[event.name], + color: this.logLevels.find((level: LogLevelObject): boolean => level.name === event.name).color, value: event.value }; }); @@ -63,7 +68,7 @@ export class ComponentGeneratorService { const selectedHost = components.find(host => host.name === componentName); data = selectedHost.logLevelCount.map(event => { return { - color: this.logsContainer.colors[event.name], + color: this.logLevels.find((level: LogLevelObject): boolean => level.name === event.name).color, value: event.value }; }); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts index 1650d90..52f4d5b 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts @@ -82,6 +82,9 @@ export class HttpClientService extends Http { topAuditLogsResources: { url: variables => `audit/logs/resources/${variables.number}`, params: opts => new AuditLogsTopResourcesQueryParams(opts) + }, + logIndexFilters: { + url: variables => `shipper/filters/${variables.clusterName}/level` } }; @@ -148,10 +151,14 @@ export class HttpClientService extends Http { return req; } - get(url, params?: HomogeneousObject<string>, urlVariables?: HomogeneousObject<string>): Observable<Response> { + get(url: string, params?: HomogeneousObject<string>, urlVariables?: HomogeneousObject<string>): Observable<Response> { return super.get(this.generateUrlString(url, urlVariables), this.generateOptions(url, params)); } + put(url: string, body: any, params?: HomogeneousObject<string>, urlVariables?: HomogeneousObject<string>): Observable<Response> { + return super.put(this.generateUrlString(url, urlVariables), body, this.generateOptions(url, params)); + } + postFormData(url: string, params: HomogeneousObject<string>, options?: RequestOptionsArgs): Observable<Response> { const encodedParams = this.generateOptions(url, params).params; let body; diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts index a8e7c3f..2f83935 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts @@ -36,8 +36,6 @@ import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/s import {TabsService, tabs} from '@app/services/storage/tabs.service'; import {HttpClientService} from '@app/services/http-client.service'; import {UtilsService} from '@app/services/utils.service'; -import {ListItem} from '@app/classes/list-item'; -import {NodeItem} from '@app/classes/models/node-item'; import {LogsContainerService} from './logs-container.service'; @@ -98,28 +96,4 @@ describe('LogsContainerService', () => { expect(service).toBeTruthy(); })); - describe('#getListItemFromString()', () => { - it('should convert string to ListItem', inject([LogsContainerService], (service: LogsContainerService) => { - const getListItemFromString: (name: string) => ListItem = service['getListItemFromString']; - expect(getListItemFromString('customName')).toEqual({ - label: 'customName', - value: 'customName' - }); - })); - }); - - describe('#getListItemFromNode()', () => { - it('should convert NodeItem to ListItem', inject([LogsContainerService], (service: LogsContainerService) => { - const getListItemFromNode: (node: NodeItem) => ListItem = service['getListItemFromNode']; - expect(getListItemFromNode({ - name: 'customName', - value: '1', - isParent: true, - isRoot: true - })).toEqual({ - label: 'customName (1)', - value: 'customName' - }); - })); - }); }); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts index 6a2108b..a523e03 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts @@ -48,7 +48,7 @@ import { FilterCondition, TimeUnitListItem, SortingListItem, SearchBoxParameter, SearchBoxParameterTriggered } from '@app/classes/filtering'; import {ListItem} from '@app/classes/list-item'; -import {HomogeneousObject} from '@app/classes/object'; +import {HomogeneousObject, LogLevelObject} from '@app/classes/object'; import {LogsType, ScrollType, SortingType} from '@app/classes/string'; import {Tab} from '@app/classes/models/tab'; import {LogField} from '@app/classes/models/log-field'; @@ -126,6 +126,44 @@ export class LogsContainerService { private readonly paginationOptions: string[] = ['10', '25', '50', '100']; + readonly logLevels: LogLevelObject[] = [ + { + name: 'FATAL', + label: 'levels.fatal', + color: '#830A0A' + }, + { + name: 'ERROR', + label: 'levels.error', + color: '#E81D1D' + }, + { + name: 'WARN', + label: 'levels.warn', + color: '#FF8916' + }, + { + name: 'INFO', + label: 'levels.info', + color: '#2577B5' + }, + { + name: 'DEBUG', + label: 'levels.debug', + color: '#65E8FF' + }, + { + name: 'TRACE', + label: 'levels.trace', + color: '#888' + }, + { + name: 'UNKNOWN', + label: 'levels.unknown', + color: '#BDBDBD' + } + ]; + filters: HomogeneousObject<FilterCondition> = { clusters: { label: 'filter.clusters', @@ -365,36 +403,12 @@ export class LogsContainerService { levels: { label: 'filter.levels', iconClass: 'fa fa-sort-amount-asc', - options: [ - { - label: 'levels.fatal', - value: 'FATAL' - }, - { - label: 'levels.error', - value: 'ERROR' - }, - { - label: 'levels.warn', - value: 'WARN' - }, - { - label: 'levels.info', - value: 'INFO' - }, - { - label: 'levels.debug', - value: 'DEBUG' - }, - { - label: 'levels.trace', - value: 'TRACE' - }, - { - label: 'levels.unknown', - value: 'UNKNOWN' - } - ], + options: this.logLevels.map((level: LogLevelObject): ListItem => { + return { + label: level.label, + value: level.name + }; + }), defaultSelection: [], fieldName: 'level' }, @@ -487,16 +501,6 @@ export class LogsContainerService { } }; - readonly colors = { - FATAL: '#830A0A', - ERROR: '#E81D1D', - WARN: '#FF8916', - INFO: '#2577B5', - DEBUG: '#65E8FF', - TRACE: '#888', - UNKNOWN: '#BDBDBD' - }; - private readonly filtersMapping = { clusters: ['clusters'], timeRange: ['to', 'from'], @@ -616,30 +620,6 @@ export class LogsContainerService { auditLogs: Observable<AuditLog[]> = Observable.combineLatest(this.auditLogsStorage.getAll(), this.auditLogsColumns).map(this.logsMapper); - /** - * Get instance for dropdown list from string - * @param name {string} - * @returns {ListItem} - */ - private getListItemFromString(name: string): ListItem { - return { - label: name, - value: name - }; - } - - /** - * Get instance for dropdown list from NodeItem object - * @param node {NodeItem} - * @returns {ListItem} - */ - private getListItemFromNode(node: NodeItem): ListItem { - return { - label: `${node.name} (${node.value})`, - value: node.name - }; - } - queryParameterNameChange: Subject<SearchBoxParameterTriggered> = new Subject(); queryParameterAdd: Subject<SearchBoxParameter> = new Subject(); @@ -976,7 +956,7 @@ export class LogsContainerService { request.subscribe((response: Response): void => { const clusterNames = response.json(); if (clusterNames) { - this.filters.clusters.options.push(...clusterNames.map(this.getListItemFromString)); + this.filters.clusters.options.push(...clusterNames.map(this.utils.getListItemFromString)); this.clustersStorage.addInstances(clusterNames); } }); @@ -993,7 +973,7 @@ export class LogsContainerService { }, 0) })); if (components) { - this.filters.components.options.push(...components.map(this.getListItemFromNode)); + this.filters.components.options.push(...components.map(this.utils.getListItemFromNode)); this.componentsStorage.addInstances(components); } }); @@ -1006,7 +986,7 @@ export class LogsContainerService { const jsonResponse = response.json(), hosts = jsonResponse && jsonResponse.vNodeList; if (hosts) { - this.filters.hosts.options.push(...hosts.map(this.getListItemFromNode)); + this.filters.hosts.options.push(...hosts.map(this.utils.getListItemFromNode)); this.hostsStorage.addInstances(hosts); } }); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/reducers.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/reducers.service.ts index 3f54625..38085f8 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/reducers.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/reducers.service.ts @@ -22,7 +22,6 @@ import {appState} from '@app/services/storage/app-state.service'; import {auditLogs} from '@app/services/storage/audit-logs.service'; import {clusters} from '@app/services/storage/clusters.service'; import {components} from '@app/services/storage/components.service'; -import {filters} from '@app/services/storage/filters.service'; import {graphs} from '@app/services/storage/graphs.service'; import {hosts} from '@app/services/storage/hosts.service'; import {serviceLogs} from '@app/services/storage/service-logs.service'; @@ -45,7 +44,6 @@ export const reducers = { graphs, hosts, userConfigs, - filters, clusters, components, serviceLogsFields, diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.spec.ts similarity index 79% copy from ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts copy to ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.spec.ts index ba53ee1..33c0459 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.spec.ts @@ -16,9 +16,7 @@ * limitations under the License. */ -import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; -import {async, ComponentFixture, TestBed} from '@angular/core/testing'; -import {TranslationModules} from '@app/test-config.spec'; +import {TestBed, inject} from '@angular/core/testing'; import {StoreModule} from '@ngrx/store'; import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service'; import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service'; @@ -35,18 +33,14 @@ import {ComponentsService, components} from '@app/services/storage/components.se import {HostsService, hosts} from '@app/services/storage/hosts.service'; import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service'; import {TabsService, tabs} from '@app/services/storage/tabs.service'; -import {HistoryManagerService} from '@app/services/history-manager.service'; import {HttpClientService} from '@app/services/http-client.service'; import {LogsContainerService} from '@app/services/logs-container.service'; import {UtilsService} from '@app/services/utils.service'; -import {ActionMenuComponent} from './action-menu.component'; +import {UserSettingsService} from './user-settings.service'; -describe('ActionMenuComponent', () => { - let component: ActionMenuComponent; - let fixture: ComponentFixture<ActionMenuComponent>; - - beforeEach(async(() => { +describe('UserSettingsService', () => { + beforeEach(() => { const httpClient = { get: () => { return { @@ -57,7 +51,6 @@ describe('ActionMenuComponent', () => { }; TestBed.configureTestingModule({ imports: [ - ...TranslationModules, StoreModule.provideStore({ auditLogs, serviceLogs, @@ -74,13 +67,12 @@ describe('ActionMenuComponent', () => { tabs }) ], - declarations: [ActionMenuComponent], providers: [ + UserSettingsService, { provide: HttpClientService, useValue: httpClient }, - HistoryManagerService, LogsContainerService, UtilsService, AuditLogsService, @@ -96,19 +88,11 @@ describe('ActionMenuComponent', () => { HostsService, ServiceLogsTruncatedService, TabsService - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ActionMenuComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + ] + }); }); - it('should create component', () => { - expect(component).toBeTruthy(); - }); + it('should be created', inject([UserSettingsService], (service: UserSettingsService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.ts new file mode 100644 index 0000000..5992809 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.ts @@ -0,0 +1,162 @@ +/** + * 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 {Injectable} from '@angular/core'; +import {FormGroup, FormControl} from '@angular/forms'; +import {Response} from '@angular/http'; +import {HomogeneousObject, LogLevelObject} from '@app/classes/object'; +import {LevelOverridesConfig, LogIndexFilterComponentConfig} from '@app/classes/settings'; +import {LogLevel} from '@app/classes/string'; +import {Filter} from '@app/classes/models/filter'; +import {LogsContainerService} from '@app/services/logs-container.service'; +import {HttpClientService} from '@app/services/http-client.service'; +import {UtilsService} from '@app/services/utils.service'; +import {AppSettingsService} from '@app/services/storage/app-settings.service'; + +@Injectable() +export class UserSettingsService { + + constructor(private logsContainer: LogsContainerService, private httpClient: HttpClientService, + private utils: UtilsService, private settingsStorage: AppSettingsService) { + settingsStorage.getParameter('logIndexFilters').subscribe((filters: HomogeneousObject<HomogeneousObject<Filter>>): void => { + const configs = this.parseLogIndexFilterObjects(filters); + this.settingsFormGroup.controls.logIndexFilter.setValue(configs); + }); + } + + settingsFormGroup: FormGroup = new FormGroup({ + logIndexFilter: new FormControl() + }); + + currentValues = { + logIndexFilter: {} + }; + + readonly levelNames = this.logsContainer.logLevels.map((level: LogLevelObject): LogLevel => level.name); + + loadIndexFilterConfig(clusterNames: string[]): void { + let processedRequests: number = 0, + allFilters: HomogeneousObject<Filter> = {}, + totalCount = clusterNames.length; + clusterNames.forEach((clusterName: string): void => { + this.httpClient.get('logIndexFilters', null, { + clusterName + }).subscribe((response: Response): void => { + const filters = response.json() && response.json().filter; + if (filters) { + Object.assign(allFilters, { + [clusterName]: filters + }); + if (++processedRequests === totalCount) { + this.settingsStorage.setParameter('logIndexFilters', allFilters); + this.currentValues.logIndexFilter = allFilters; + } + } + }); + }); + } + + saveIndexFilterConfig(): void { + const savedValue = this.currentValues.logIndexFilter, + newValue = this.settingsFormGroup.controls.logIndexFilter.value, + clusters = Object.keys(newValue); + let storedValue = {}; + clusters.forEach((clusterName: string): void => { + const savedConfig = savedValue[clusterName], + newConfig = this.getLogIndexFilterObject(newValue[clusterName]); + Object.assign(storedValue, { + [clusterName]: newConfig + }); + if (!this.utils.isEqual(savedConfig, newConfig)) { + this.httpClient.put('logIndexFilters', { + filter: newConfig + }, null, { + clusterName + }); + } + }); + this.settingsStorage.setParameter('logIndexFilters', storedValue); + } + + /** + * Convert log index filter data for usage in component + * @param {HomogeneousObject<HomogeneousObject<Filter>>} filters + * @returns {HomogeneousObject<LogIndexFilterComponentConfig[]>} + */ + parseLogIndexFilterObjects( + filters: HomogeneousObject<HomogeneousObject<Filter>> + ): HomogeneousObject<LogIndexFilterComponentConfig[]> { + const levels = this.levelNames; + return filters ? Object.keys(filters).reduce(( + clustersCurrent: HomogeneousObject<LogIndexFilterComponentConfig[]>, clusterName: string + ): HomogeneousObject<LogIndexFilterComponentConfig[]> => { + const clusterConfigs = filters[clusterName], + clusterParsedObject = Object.keys(clusterConfigs).map((componentName: string) => { + const componentConfigs = clusterConfigs[componentName], + levelProperties = levels.reduce(( + levelsCurrent: HomogeneousObject<LevelOverridesConfig>, levelName: LogLevel + ): LevelOverridesConfig => { + return Object.assign({}, levelsCurrent, { + [levelName]: { + defaults: componentConfigs.defaultLevels.indexOf(levelName) > -1, + overrides: componentConfigs.overrideLevels.indexOf(levelName) > -1 + } + }); + }, {}); + return Object.assign({ + name: componentName, + label: componentConfigs.label, + hasOverrides: false, + hosts: componentConfigs.hosts.join(), + expiryTime: componentConfigs.expiryTime + }, levelProperties); + }); + return Object.assign({}, clustersCurrent, { + [clusterName]: clusterParsedObject + }); + }, {}) : {}; + } + + /** + * Convert data from log index filter component to format for PUT API call + * @param configs + * @returns {HomogeneousObject<Filter>} + */ + private getLogIndexFilterObject(configs): HomogeneousObject<Filter> { + const levelNames = this.levelNames; + return configs.reduce(( + currentObject: HomogeneousObject<Filter>, componentConfig: LogIndexFilterComponentConfig + ): HomogeneousObject<Filter> => { + const hosts = componentConfig.hosts; + return Object.assign({}, currentObject, { + [componentConfig.name]: { + defaultLevels: levelNames.filter((levelName: LogLevel): boolean => componentConfig[levelName].defaults), + expiryTime: componentConfig.expiryTime, + hosts: hosts ? hosts.split(',') : [], + label: componentConfig.label, + overrideLevels: levelNames.filter((levelName: LogLevel): boolean => componentConfig[levelName].overrides) + } + }); + }, {}); + } + + setTimeZone(timeZone: string): void { + this.settingsStorage.setParameter('timeZone', timeZone); + } + +} diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.spec.ts index f89462d..861e5c3 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.spec.ts @@ -417,4 +417,28 @@ describe('UtilsService', () => { })); }); }); + + describe('#getListItemFromString()', () => { + it('should convert string to ListItem', inject([UtilsService], (service: UtilsService) => { + expect(service.getListItemFromString('customName')).toEqual({ + label: 'customName', + value: 'customName' + }); + })); + }); + + describe('#getListItemFromNode()', () => { + it('should convert NodeItem to ListItem', inject([UtilsService], (service: UtilsService) => { + expect(service.getListItemFromNode({ + name: 'customName', + value: '1', + isParent: true, + isRoot: true + })).toEqual({ + label: 'customName (1)', + value: 'customName' + }); + })); + }); + }); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts index 514837c..952a1d1 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts @@ -18,7 +18,9 @@ import {Injectable} from '@angular/core'; import * as moment from 'moment-timezone'; +import {ListItem} from '@app/classes/list-item'; import {HomogeneousObject} from '@app/classes/object'; +import {NodeItem} from '@app/classes/models/node-item'; @Injectable() export class UtilsService { @@ -105,4 +107,28 @@ export class UtilsService { }, 0); } + /** + * Get instance for dropdown list from string + * @param name {string} + * @returns {ListItem} + */ + getListItemFromString(name: string): ListItem { + return { + label: name, + value: name + }; + } + + /** + * Get instance for dropdown list from NodeItem object + * @param node {NodeItem} + * @returns {ListItem} + */ + getListItemFromNode(node: NodeItem): ListItem { + return { + label: `${node.name} (${node.value})`, + value: node.name + }; + } + } diff --git a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json index 1d8f6c4..7f16078 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json +++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json @@ -12,6 +12,7 @@ "modal.cancel": "Cancel", "modal.apply": "Apply", "modal.close": "Close", + "modal.save": "Save", "authorization.logout": "Logout", "authorization.name": "Username", @@ -23,6 +24,7 @@ "topMenu.redo": "Redo", "topMenu.refresh": "Refresh", "topMenu.history": "History", + "topMenu.filter": "Filter", "filter.all": "All", @@ -183,5 +185,14 @@ "histogram.gap.day": "day", "histogram.gap.days": "days", "histogram.gap.week": "week", - "histogram.gap.weeks": "weeks" + "histogram.gap.weeks": "weeks", + + "logIndexFilter.title": "Log Index Filter", + "logIndexFilter.caption": "For each cluster, choose the components, hosts and log levels that will be indexed in the log feeder", + "logIndexFilter.select": "Select", + "logIndexFilter.selectCluster": "Select Cluster", + "logIndexFilter.override": "Override Hosts", + "logIndexFilter.addHosts": "Add Hosts", + "logIndexFilter.hostname": "Hostname", + "logIndexFilter.expiryDate": "Expiry Date" } -- To stop receiving notification emails like this one, please contact ababiic...@apache.org.