AMBARI-21726 Log Search UI: columns set for logs list should be customized by 
user. (ababiichuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/f4bb14c1
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/f4bb14c1
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/f4bb14c1

Branch: refs/heads/branch-feature-logsearch-ui
Commit: f4bb14c19cc943c7a46dc5cc336851cc54d9592c
Parents: f50c17b
Author: ababiichuk <[email protected]>
Authored: Tue Aug 15 17:21:49 2017 +0300
Committer: ababiichuk <[email protected]>
Committed: Tue Aug 15 17:21:49 2017 +0300

----------------------------------------------------------------------
 .../src/app/app.module.ts                       |   4 +
 .../dropdown-button.component.html              |   2 +-
 .../dropdown-button.component.less              |   2 +
 .../dropdown-button.component.ts                |   8 +-
 .../filter-dropdown.component.less              |  22 --
 .../filter-dropdown.component.ts                |   2 +-
 .../logs-container.component.html               |   5 +-
 .../logs-container.component.spec.ts            |  10 +
 .../logs-container/logs-container.component.ts  |  65 +++++-
 .../logs-list/logs-list.component.html          |  25 ++-
 .../logs-list/logs-list.component.less          |   9 +-
 .../components/logs-list/logs-list.component.ts |   9 +
 .../main-container.component.html               |   2 +-
 .../main-container.component.spec.ts            |   8 +-
 .../main-container/main-container.component.ts  |  27 ++-
 .../pagination-controls.component.less          |   1 +
 .../src/app/mock-data.ts                        | 122 ++++++++--
 .../src/app/models/audit-log-field.model.ts     | 225 +++++++++++++++++++
 .../src/app/models/log-field.model.ts           |  27 +++
 .../src/app/models/service-log-field.model.ts   | 107 +++++++++
 .../src/app/models/store.model.ts               |  28 ++-
 .../app/services/component-actions.service.ts   |   5 +
 .../src/app/services/http-client.service.ts     |   6 +
 .../storage/audit-logs-fields.service.ts        |  32 +++
 .../app/services/storage/reducers.service.ts    |   8 +-
 .../storage/service-logs-fields.service.ts      |  32 +++
 .../src/assets/i18n/en.json                     |  68 +++++-
 27 files changed, 790 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/app.module.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web-new/src/app/app.module.ts 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/app.module.ts
index f81e75b..775ac55 100644
--- a/ambari-logsearch/ambari-logsearch-web-new/src/app/app.module.ts
+++ b/ambari-logsearch/ambari-logsearch-web-new/src/app/app.module.ts
@@ -45,6 +45,8 @@ 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';
+import {AuditLogsFieldsService} from 
'@app/services/storage/audit-logs-fields.service';
 import {reducer} from '@app/services/storage/reducers.service';
 
 import {AppComponent} from '@app/components/app.component';
@@ -138,6 +140,8 @@ export function getXHRBackend(injector: Injector, browser: 
BrowserXhr, xsrf: XSR
     FiltersService,
     ClustersService,
     ComponentsService,
+    ServiceLogsFieldsService,
+    AuditLogsFieldsService,
     {
       provide: XHRBackend,
       useFactory: getXHRBackend,

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
index bd0d528..b777b52 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
@@ -18,7 +18,7 @@
 <div class="filter-label" *ngIf="label">{{label | translate}}</div>
 <div [ngClass]="{'dropup': isDropup}">
   <button class="btn btn-link dropdown-toggle" data-toggle="dropdown">
-    {{selectedLabel | translate}} <span class="caret"></span>
+    <span *ngIf="!isMultipleChoice">{{selectedLabel | translate}}</span> <span 
class="caret"></span>
   </button>
   <ul data-component="dropdown-list" [ngClass]="{'dropdown-menu': true, 
'dropdown-menu-right': isRightAlign}"
       [items]="options" (selectedItemChange)="updateValue($event)"></ul>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.less
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.less
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.less
index a60246c..55699b4 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.less
@@ -18,7 +18,9 @@
 @import '../variables';
 
 :host {
+  .default-flex;
   position: relative;
+  float: left;
 
   .filter-label {
     padding: @input-group-addon-padding;

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
index 3aecd9e..029d87b 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
@@ -50,6 +50,12 @@ export class DropdownButtonComponent implements OnInit {
   action?: string;
 
   @Input()
+  additionalArgs?: any[] = [];
+
+  @Input()
+  isMultipleChoice?: boolean = false;
+
+  @Input()
   isRightAlign?: boolean = false;
 
   @Input()
@@ -73,7 +79,7 @@ export class DropdownButtonComponent implements OnInit {
       this.value = value;
       this.selectedLabel = options.label;
       if (this.action) {
-        this.actions[this.action](value);
+        this.actions[this.action](value, ...this.additionalArgs);
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.less
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.less
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.less
deleted file mode 100644
index e5e85f4..0000000
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.less
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * 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';
-
-:host {
-  .default-flex;
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
index 8352ff1..c414782 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
@@ -24,7 +24,7 @@ import {DropdownButtonComponent} from 
'@app/components/dropdown-button/dropdown-
 @Component({
   selector: 'filter-dropdown',
   templateUrl: '../dropdown-button/dropdown-button.component.html',
-  styleUrls: ['../dropdown-button/dropdown-button.component.less', 
'./filter-dropdown.component.less'],
+  styleUrls: ['../dropdown-button/dropdown-button.component.less'],
   providers: [
     {
       provide: NG_VALUE_ACCESSOR,

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.html
index a7dabe8..2e642be 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.html
@@ -16,4 +16,7 @@
 -->
 
 <time-histogram class="col-md-12" [data]="histogramData" 
[customOptions]="histogramOptions"></time-histogram>
-<logs-list [logs]="logs | async" [totalCount]="totalCount"></logs-list>
+<dropdown-button class="pull-right" label="logs.columns" 
[options]="availableColumns | async" isRightAlign="true"
+                 isMultipleChoice="true" action="updateSelectedColumns"
+                 
[additionalArgs]="this[this.logsTypeMapObject.fieldsModel]"></dropdown-button>
+<logs-list [logs]="logs | async" [totalCount]="totalCount" 
[displayedColumns]="displayedColumns"></logs-list>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
index 7792fa9..4d6509b 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
@@ -22,7 +22,10 @@ import {StoreModule} from '@ngrx/store';
 import {AppSettingsService, appSettings} from 
'@app/services/storage/app-settings.service';
 import {ClustersService, clusters} from 
'@app/services/storage/clusters.service';
 import {ComponentsService, components} from 
'@app/services/storage/components.service';
+import {AuditLogsService, auditLogs} from 
'@app/services/storage/audit-logs.service';
+import {AuditLogsFieldsService, auditLogsFields} from 
'@app/services/storage/audit-logs-fields.service';
 import {ServiceLogsService, serviceLogs} from 
'@app/services/storage/service-logs.service';
+import {ServiceLogsFieldsService, serviceLogsFields} from 
'@app/services/storage/service-logs-fields.service';
 import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from 
'@app/services/storage/service-logs-histogram-data.service';
 import {HttpClientService} from '@app/services/http-client.service';
 import {FilteringService} from '@app/services/filtering.service';
@@ -50,7 +53,10 @@ describe('LogsContainerComponent', () => {
           appSettings,
           clusters,
           components,
+          auditLogs,
+          auditLogsFields,
           serviceLogs,
+          serviceLogsFields,
           serviceLogsHistogramData
         })
       ],
@@ -62,7 +68,10 @@ describe('LogsContainerComponent', () => {
         AppSettingsService,
         ClustersService,
         ComponentsService,
+        AuditLogsService,
+        AuditLogsFieldsService,
         ServiceLogsService,
+        ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
         FilteringService,
         UtilsService
@@ -75,6 +84,7 @@ describe('LogsContainerComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(LogsContainerComponent);
     component = fixture.componentInstance;
+    component.logsType = 'serviceLogs';
     fixture.detectChanges();
   });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
index 5823846..40e7d89 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
@@ -18,11 +18,18 @@
 
 import {Component, OnInit, Input} from '@angular/core';
 import {FormGroup} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
 import 'rxjs/add/operator/map';
 import {HttpClientService} from '@app/services/http-client.service';
 import {FilteringService} from '@app/services/filtering.service';
+import {AuditLogsService} from '@app/services/storage/audit-logs.service';
+import {AuditLogsFieldsService} from 
'@app/services/storage/audit-logs-fields.service';
 import {ServiceLogsService} from '@app/services/storage/service-logs.service';
+import {ServiceLogsFieldsService} from 
'@app/services/storage/service-logs-fields.service';
 import {ServiceLogsHistogramDataService} from 
'@app/services/storage/service-logs-histogram-data.service';
+import {AuditLog} from '@app/models/audit-log.model';
+import {ServiceLog} from '@app/models/service-log.model';
+import {LogField} from '@app/models/log-field.model';
 
 @Component({
   selector: 'logs-container',
@@ -31,7 +38,7 @@ import {ServiceLogsHistogramDataService} from 
'@app/services/storage/service-log
 })
 export class LogsContainerComponent implements OnInit {
 
-  constructor(private httpClient: HttpClientService, private 
serviceLogsStorage: ServiceLogsService, private serviceLogsHistogramStorage: 
ServiceLogsHistogramDataService, private filtering: FilteringService) {
+  constructor(private httpClient: HttpClientService, private auditLogsStorage: 
AuditLogsService, private auditLogsFieldsStorage: AuditLogsFieldsService, 
private serviceLogsStorage: ServiceLogsService, private 
serviceLogsFieldsStorage: ServiceLogsFieldsService, private 
serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private 
filtering: FilteringService) {
     this.serviceLogsHistogramStorage.getAll().subscribe(data => {
       let histogramData = {};
       data.forEach(type => {
@@ -51,6 +58,31 @@ export class LogsContainerComponent implements OnInit {
   }
 
   ngOnInit() {
+    this.logsTypeMapObject = this.logsTypeMap[this.logsType];
+    this.availableColumns = 
this[this.logsTypeMapObject.fieldsModel].getAll().map(fields => {
+      const availableFields = fields.filter(field => field.isAvailable),
+        availableNames = availableFields.map(field => field.name);
+      if (availableNames.length) {
+        this.logs = this[this.logsTypeMapObject.logsModel].getAll().map(logs 
=> logs.map(log => {
+          let logObject = availableNames.reduce((obj, key) => 
Object.assign(obj, {
+            [key]: log[key]
+          }), {});
+          if (logObject.level) {
+            logObject.className = logObject.level.toLowerCase();
+          }
+          return logObject;
+        }));
+      }
+      return availableFields.map(field => {
+        return {
+          value: field.name,
+          label: field.displayName || field.name
+        };
+      });
+    });
+    this[this.logsTypeMapObject.fieldsModel].getAll().subscribe(columns => {
+      this.displayedColumns = columns.filter(column => column.isAvailable && 
column.isDisplayed);
+    });
     this.loadLogs();
     this.filtersForm.valueChanges.subscribe(() => {
       this.loadLogs();
@@ -58,7 +90,9 @@ export class LogsContainerComponent implements OnInit {
   }
 
   @Input()
-  private logsArrayId: string;
+  private logsType: string;
+
+  logsTypeMapObject: any;
 
   totalCount: number = 0;
 
@@ -77,15 +111,22 @@ export class LogsContainerComponent implements OnInit {
     timeRange: ['to', 'from']
   };
 
-  logs = this.serviceLogsStorage.getAll().map(logs => logs.map(log => {
-    return {
-      type: log.type,
-      level: log.level,
-      className: log.level.toLowerCase(),
-      message: log.log_message,
-      time: log.logtime
-    };
-  }));
+  private readonly logsTypeMap = {
+    auditLogs: {
+      logsModel: 'auditLogsStorage',
+      fieldsModel: 'auditLogsFieldsStorage'
+    },
+    serviceLogs: {
+      logsModel: 'serviceLogsStorage',
+      fieldsModel: 'serviceLogsFieldsStorage'
+    }
+  };
+
+  logs: Observable<AuditLog[] | ServiceLog[]>;
+
+  availableColumns: Observable<LogField[]>;
+
+  displayedColumns: any[] = [];
 
   histogramData: any;
 
@@ -106,7 +147,7 @@ export class LogsContainerComponent implements OnInit {
   }
 
   private loadLogs(): void {
-    this.httpClient.get(this.logsArrayId, 
this.getParams('listFilters')).subscribe(response => {
+    this.httpClient.get(this.logsType, 
this.getParams('listFilters')).subscribe(response => {
       const jsonResponse = response.json();
       this.serviceLogsStorage.clear();
       if (jsonResponse) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
index e9b3c67..caae24e 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
@@ -15,11 +15,11 @@
   limitations under the License.
 -->
 
-<form *ngIf="logs.length" [formGroup]="filtersForm" class="pull-right">
+<form *ngIf="logs && logs.length" [formGroup]="filtersForm" class="pull-right">
   <filter-dropdown [label]="filters.sorting.label" formControlName="sorting" 
[options]="filters.sorting.options"
                    [defaultLabel]="filters.sorting.defaultLabel" 
isRightAlign="true"></filter-dropdown>
 </form>
-<div class="col-md-12 text-center" *ngIf="logs.length">
+<div class="col-md-12 text-center" *ngIf="logs && logs.length">
   <div class="logs-header">
     <div class="col-md-1">{{'logs.status' | translate}}</div>
     <div class="col-md-11">{{'logs.details' | translate}}</div>
@@ -27,11 +27,13 @@
 </div>
 <accordion-panel *ngFor="let log of logs; let i = index" 
[toggleId]="'details-' + i" class="col-md-12">
   <ng-template>
-    <div [ngClass]="'hexagon ' + log.className"></div>
-    <div [ngClass]="'col-md-1 log-status ' + log.className">{{log.level}}</div>
-    <div class="col-md-3">
-      <div class="log-type">{{log.type}}</div>
-      <time class="log-time">{{log.time | amTz: timeZone | amDateFormat: 
timeFormat}}</time>
+    <div *ngIf="isColumnDisplayed('level')" [ngClass]="'hexagon ' + 
log.className"></div>
+    <div *ngIf="isColumnDisplayed('level')" [ngClass]="'col-md-1 log-status ' 
+ log.className">{{log.level}}</div>
+    <div *ngIf="isColumnDisplayed('type') || isColumnDisplayed('logtime')" 
class="col-md-3">
+      <div *ngIf="isColumnDisplayed('type')" 
class="log-type">{{log.type}}</div>
+      <time *ngIf="isColumnDisplayed('logtime')" class="log-time">
+        {{log.logtime | amTz: timeZone | amDateFormat: timeFormat}}
+      </time>
     </div>
     <div class="col-md-6 log-content-wrapper">
       <div class="collapse log-actions" [attr.id]="'details-' + i">
@@ -39,9 +41,14 @@
         <span class="action-icon fa fa-external-link"></span>
         <span class="action-icon fa fa-bullseye"></span>
       </div>
-      <div class="log-content">{{log.message}}</div>
+      <div class="log-content-inner-wrapper">
+        <div class="log-content" 
*ngIf="isColumnDisplayed('log_message')">{{log.log_message}}</div>
+      </div>
+    </div>
+    <div *ngFor="let column of displayedColumns">
+      <div *ngIf="customStyledColumns.indexOf(column.name) === -1" 
[innerHTML]="log[column.name]" class="col-md-1"></div>
     </div>
   </ng-template>
 </accordion-panel>
-<pagination class="col-md-12" *ngIf="logs.length" [totalCount]="totalCount" 
[filtersForm]="filtersForm"
+<pagination class="col-md-12" *ngIf="logs && logs.length" 
[totalCount]="totalCount" [filtersForm]="filtersForm"
             [filterInstance]="filters.pageSize" 
[currentCount]="logs.length"></pagination>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.less
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.less
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.less
index 63c0354..91d796f 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.less
@@ -107,11 +107,14 @@
   position: relative;
 
   // TODO get rid of magic numbers, base on actual design
-  .log-content {
+  .log-content-inner-wrapper {
     overflow: hidden;
     max-height: @default-line-height * 2em;
     padding-right: 65px;
-    white-space: pre-wrap;
+
+    .log-content {
+      white-space: pre-wrap;
+    }
   }
 
   .log-actions {
@@ -120,7 +123,7 @@
     top: 0;
     border: @input-border;
 
-    &.collapsing + .log-content, &.collapse.in + .log-content {
+    &.collapsing + .log-content-inner-wrapper, &.collapse.in + 
.log-content-inner-wrapper {
       min-height: 6em;
       max-height: none;
       overflow-x: auto;

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.ts
index 27b5021..6d73dcb 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.ts
@@ -36,6 +36,11 @@ export class LogsListComponent {
   @Input()
   totalCount: number = 0;
 
+  @Input()
+  displayedColumns: any[] = [];
+
+  readonly customStyledColumns = ['level', 'type', 'logtime', 'log_message'];
+
   timeFormat: string = 'DD/MM/YYYY HH:mm:ss';
 
   get timeZone(): string {
@@ -50,4 +55,8 @@ export class LogsListComponent {
     return this.filtering.filtersForm;
   }
 
+  isColumnDisplayed(key: string): boolean {
+    return this.displayedColumns.some(column => column.name === key);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.html
index f4cf47a..69b3887 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.html
@@ -21,4 +21,4 @@
 </div>
 <login-form *ngIf="!isInitialLoading && !isAuthorized"></login-form>
 <filters-panel *ngIf="isAuthorized" class="row"></filters-panel>
-<logs-container *ngIf="isAuthorized" 
logsArrayId="serviceLogs"></logs-container>
+<logs-container *ngIf="isAuthorized" logsType="serviceLogs"></logs-container>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.spec.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.spec.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.spec.ts
index fa8954c..42fba68 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.spec.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.spec.ts
@@ -21,6 +21,8 @@ import {async, ComponentFixture, TestBed} from 
'@angular/core/testing';
 import {HttpModule} from '@angular/http';
 import {StoreModule} from '@ngrx/store';
 import {AppStateService, appState} from 
'@app/services/storage/app-state.service';
+import {AuditLogsFieldsService, auditLogsFields} from 
'@app/services/storage/audit-logs-fields.service';
+import {ServiceLogsFieldsService, serviceLogsFields} from 
'@app/services/storage/service-logs-fields.service';
 import {HttpClientService} from '@app/services/http-client.service';
 
 import {MainContainerComponent} from './main-container.component';
@@ -35,12 +37,16 @@ describe('MainContainerComponent', () => {
       imports: [
         HttpModule,
         StoreModule.provideStore({
-          appState
+          appState,
+          auditLogsFields,
+          serviceLogsFields
         })
       ],
       schemas: [CUSTOM_ELEMENTS_SCHEMA],
       providers: [
         AppStateService,
+        AuditLogsFieldsService,
+        ServiceLogsFieldsService,
         HttpClientService
       ]
     })

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.ts
index b0c3943..78df822 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/main-container/main-container.component.ts
@@ -17,7 +17,12 @@
  */
 
 import {Component, ContentChild, TemplateRef} from '@angular/core';
+import {HttpClientService} from '@app/services/http-client.service';
 import {AppStateService} from '@app/services/storage/app-state.service';
+import {AuditLogsFieldsService} from 
'@app/services/storage/audit-logs-fields.service';
+import {ServiceLogsFieldsService} from 
'@app/services/storage/service-logs-fields.service';
+import {AuditLogField} from '@app/models/audit-log-field.model';
+import {ServiceLogField} from '@app/models/service-log-field.model';
 
 @Component({
   selector: 'main-container',
@@ -26,7 +31,8 @@ import {AppStateService} from 
'@app/services/storage/app-state.service';
 })
 export class MainContainerComponent {
 
-  constructor(private appState: AppStateService) {
+  constructor(private httpClient: HttpClientService, private appState: 
AppStateService, private auditLogsFieldsStorage: AuditLogsFieldsService, 
private serviceLogsFieldsStorage: ServiceLogsFieldsService) {
+    this.loadColumnsNames();
     this.appState.getParameter('isAuthorized').subscribe(value => 
this.isAuthorized = value);
     this.appState.getParameter('isInitialLoading').subscribe(value => 
this.isInitialLoading = value);
   }
@@ -38,4 +44,23 @@ export class MainContainerComponent {
 
   isInitialLoading: boolean = false;
 
+  private loadColumnsNames(): void {
+    this.httpClient.get('serviceLogsFields').subscribe(response => {
+      const jsonResponse = response.json();
+      if (jsonResponse) {
+        
this.serviceLogsFieldsStorage.addInstances(this.getColumnsArray(jsonResponse, 
ServiceLogField));
+      }
+    });
+    this.httpClient.get('auditLogsFields').subscribe(response => {
+      const jsonResponse = response.json();
+      if (jsonResponse) {
+        
this.auditLogsFieldsStorage.addInstances(this.getColumnsArray(jsonResponse, 
AuditLogField));
+      }
+    });
+  }
+
+  private getColumnsArray(keysObject: any, fieldClass: any): any[] {
+    return Object.keys(keysObject).map(key => new fieldClass(key));
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/components/pagination-controls/pagination-controls.component.less
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/pagination-controls/pagination-controls.component.less
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/pagination-controls/pagination-controls.component.less
index c21e83e..8238eaf 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/pagination-controls/pagination-controls.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/pagination-controls/pagination-controls.component.less
@@ -15,6 +15,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 @import '../variables';
 
 .pagination-control {

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/mock-data.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web-new/src/app/mock-data.ts 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/mock-data.ts
index ecac362..1f738ae 100644
--- a/ambari-logsearch/ambari-logsearch-web-new/src/app/mock-data.ts
+++ b/ambari-logsearch/ambari-logsearch-web-new/src/app/mock-data.ts
@@ -68,7 +68,7 @@ export const mockData = {
               event_count: 0,
               event_md5: '09876543211234567890',
               event_dur_ms: 100,
-              _ttl_: "+7DAYS",
+              _ttl_: '+7DAYS',
               _expire_at_: '2017-05-29T11:30:22.531Z',
               _router_field_: 5
             },
@@ -115,7 +115,7 @@ export const mockData = {
               event_count: 2,
               event_md5: '01928374650192837465',
               event_dur_ms: 500,
-              _ttl_: "+7DAYS",
+              _ttl_: '+7DAYS',
               _expire_at_: '2017-05-29T11:30:22.531Z',
               _router_field_: 10
             }
@@ -182,7 +182,79 @@ export const mockData = {
             ]
           },
           schema: {
-            fields: ''
+            fields: {
+              'cluster': 'key_lower_case',
+              'ws_status': 'text_ws',
+              'reason': 'text_std_token_lower_case',
+              'agent': 'key_lower_case',
+              'Base URL': 'key_lower_case',
+              'sess': 'key_lower_case',
+              'type': 'key_lower_case',
+              'seq_num': 'tlong',
+              'path': 'key_lower_case',
+              'ugi': 'key_lower_case',
+              'host': 'key_lower_case',
+              'case_id': 'key_lower_case',
+              'action': 'key_lower_case',
+              'id': 'string',
+              'logger_name': 'key_lower_case',
+              'text': 'text_std_token_lower_case',
+              'Repo id': 'key_lower_case',
+              'Stack version': 'tdouble',
+              'logfile_line_number': 'tint',
+              'Status': 'tlong',
+              'RequestId': 'tlong',
+              'level': 'key_lower_case',
+              'resource': 'key_lower_case',
+              'resType': 'key_lower_case',
+              'ip': 'key_lower_case',
+              'Hostname': 'key_lower_case',
+              'Roles': 'key_lower_case',
+              'Stack': 'key_lower_case',
+              'req_self_id': 'key_lower_case',
+              'repoType': 'tint',
+              'VersionNote': 'key_lower_case',
+              'Cluster name': 'key_lower_case',
+              'bundle_id': 'key_lower_case',
+              'cliType': 'key_lower_case',
+              'reqContext': 'key_lower_case',
+              'ws_result_status': 'text_ws',
+              'proxyUsers': 'key_lower_case',
+              'RequestType': 'key_lower_case',
+              'Repositories': 'key_lower_case',
+              'logType': 'key_lower_case',
+              'Repo version': 'key_lower_case',
+              'TaskId': 'tlong',
+              'User': 'key_lower_case',
+              'access': 'key_lower_case',
+              'dst': 'key_lower_case',
+              'perm': 'key_lower_case',
+              'event_count': 'tlong',
+              'repo': 'key_lower_case',
+              'reqUser': 'key_lower_case',
+              'task_id': 'tlong',
+              'Operation': 'key_lower_case',
+              'Reason': 'key_lower_case',
+              'reqData': 'text_std_token_lower_case',
+              'result': 'tint',
+              'file': 'key_lower_case',
+              'log_message': 'key_lower_case',
+              'agentHost': 'key_lower_case',
+              'Component': 'key_lower_case',
+              'authType': 'key_lower_case',
+              'Display name': 'key_lower_case',
+              'policy': 'tlong',
+              'cliIP': 'key_lower_case',
+              'OS': 'key_lower_case',
+              'RemoteIp': 'key_lower_case',
+              'ResultStatus': 'tlong',
+              'evtTime': 'tdate',
+              'VersionNumber': 'key_lower_case',
+              'url': 'key_lower_case',
+              'req_caller_id': 'key_lower_case',
+              'enforcer': 'key_lower_case',
+              'Command': 'key_lower_case'
+            }
           },
           serviceload: {
             graphData: [
@@ -242,7 +314,7 @@ export const mockData = {
               event_count: 5,
               event_md5: '1908755391',
               event_dur_ms: 200,
-              _ttl_: "+5DAYS",
+              _ttl_: '+5DAYS',
               _expire_at_: moment().add(5, 'd').valueOf(),
               _router_field_: 20
             },
@@ -266,7 +338,7 @@ export const mockData = {
               event_count: 2,
               event_md5: '1029384756',
               event_dur_ms: 700,
-              _ttl_: "+5DAYS",
+              _ttl_: '+5DAYS',
               _expire_at_: moment().add(3, 'd').valueOf(),
               _router_field_: 5
             },
@@ -290,7 +362,7 @@ export const mockData = {
               event_count: 2,
               event_md5: '67589403',
               event_dur_ms: 100,
-              _ttl_: "+5DAYS",
+              _ttl_: '+5DAYS',
               _expire_at_: moment().subtract(5, 'd').valueOf(),
               _router_field_: 45
             },
@@ -314,7 +386,7 @@ export const mockData = {
               event_count: 2,
               event_md5: '67589403',
               event_dur_ms: 1000,
-              _ttl_: "+5DAYS",
+              _ttl_: '+5DAYS',
               _expire_at_: moment().subtract(25, 'h').add(5, 'd').valueOf(),
               _router_field_: 55
             },
@@ -338,7 +410,7 @@ export const mockData = {
               event_count: 2,
               event_md5: '67589403',
               event_dur_ms: 1000,
-              _ttl_: "+5DAYS",
+              _ttl_: '+5DAYS',
               _expire_at_: moment().subtract(20, 'd').valueOf(),
               _router_field_: 55
             },
@@ -362,7 +434,7 @@ export const mockData = {
               event_count: 2,
               event_md5: '67589403',
               event_dur_ms: 1000,
-              _ttl_: "+5DAYS",
+              _ttl_: '+5DAYS',
               _expire_at_: moment().subtract(2, 'h').add(5, 'd').valueOf(),
               _router_field_: 55
             },
@@ -386,7 +458,7 @@ export const mockData = {
               event_count: 2,
               event_md5: '67589403',
               event_dur_ms: 1000,
-              _ttl_: "+5DAYS",
+              _ttl_: '+5DAYS',
               _expire_at_: moment().subtract(26, 'd').valueOf(),
               _router_field_: 55
             }
@@ -688,7 +760,31 @@ export const mockData = {
             }
           },
           schema: {
-            fields: ''
+            fields: {
+              cluster: 'key_lower_case',
+              method: 'key_lower_case',
+              level: 'key_lower_case',
+              event_count: 'tlong',
+              ip: 'string',
+              rowtype: 'key_lower_case',
+              key_log_message: 'key_lower_case',
+              type: 'key_lower_case',
+              seq_num: 'tlong',
+              path: 'key_lower_case',
+              logtype: 'key_lower_case',
+              file: 'key_lower_case',
+              line_number: 'tint',
+              thread_name: 'key_lower_case',
+              bundle_id: 'key_lower_case',
+              host: 'key_lower_case',
+              case_id: 'key_lower_case',
+              log_message: 'text_std_token_lower_case',
+              id: 'string',
+              logger_name: 'key_lower_case',
+              text: 'text_std_token_lower_case',
+              logfile_line_number: 'tint',
+              logtime: 'tdate'
+            }
           },
           serviceconfig: '',
           tree: {
@@ -783,7 +879,7 @@ export const mockData = {
                 event_count: 5,
                 event_md5: '1908755391',
                 event_dur_ms: 200,
-                _ttl_: "+5DAYS",
+                _ttl_: '+5DAYS',
                 _expire_at_: '2017-05-29T11:30:22.531Z',
                 _router_field_: 20
               },
@@ -807,7 +903,7 @@ export const mockData = {
                 event_count: 2,
                 event_md5: '1029384756',
                 event_dur_ms: 700,
-                _ttl_: "+5DAYS",
+                _ttl_: '+5DAYS',
                 _expire_at_: '2017-05-29T10:30:22.531Z',
                 _router_field_: 5
               }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/models/audit-log-field.model.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/audit-log-field.model.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/audit-log-field.model.ts
new file mode 100644
index 0000000..96372a1
--- /dev/null
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/audit-log-field.model.ts
@@ -0,0 +1,225 @@
+/**
+ * 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 {LogField} from '@app/models/log-field.model';
+
+const columnsNamesMap = {
+  access: {
+    displayName: 'logs.accessType',
+    isDisplayed: true
+  },
+  action: {
+    displayName: 'logs.action'
+  },
+  agent: {
+    displayName: 'logs.agent'
+  },
+  agentHost: {
+    displayName: 'logs.agentHost'
+  },
+  authType: {
+    displayName: 'logs.authType'
+  },
+  bundle_id: {
+    displayName: 'logs.bundleId'
+  },
+  case_id: {
+    displayName: 'logs.caseId'
+  },
+  cliIP: {
+    displayName: 'logs.clientIp',
+    isDisplayed: true
+  },
+  cliType: {
+    displayName: 'logs.clientType'
+  },
+  cluster: {
+    displayName: 'logs.cluster'
+  },
+  dst: {
+    displayName: 'logs.dst'
+  },
+  evtTime: {
+    displayName: 'logs.eventTime',
+    isDisplayed: true
+  },
+  file: {
+    displayName: 'logs.file'
+  },
+  host: {
+    displayName: 'logs.host'
+  },
+  id: {
+    displayName: 'logs.id'
+  },
+  ip: {
+    displayName: 'logs.ip'
+  },
+  level: {
+    displayName: 'logs.level'
+  },
+  log_message: {
+    displayName: 'logs.message'
+  },
+  logType: {
+    displayName: 'logs.logType'
+  },
+  logfile_line_number: {
+    displayName: 'logs.logfileLineNumber'
+  },
+  logger_name: {
+    displayName: 'logs.loggerName'
+  },
+  logtime: {
+    displayName: 'logs.logTime'
+  },
+  path: {
+    displayName: 'logs.path'
+  },
+  perm: {
+    displayName: 'logs.perm'
+  },
+  policy: {
+    displayName: 'logs.policy'
+  },
+  proxyUsers: {
+    displayName: 'logs.proxyUsers'
+  },
+  reason: {
+    displayName: 'logs.reason'
+  },
+  repo: {
+    displayName: 'logs.repo',
+    isDisplayed: true
+  },
+  repoType: {
+    displayName: 'logs.repoType'
+  },
+  req_caller_id: {
+    displayName: 'logs.reqCallerId'
+  },
+  reqContext: {
+    displayName: 'logs.reqContext'
+  },
+  reqData: {
+    displayName: 'logs.reqData'
+  },
+  req_self_id: {
+    displayName: 'logs.reqSelfId'
+  },
+  resType: {
+    displayName: 'logs.resType'
+  },
+  resource: {
+    displayName: 'logs.resource',
+    isDisplayed: true
+  },
+  result: {
+    displayName: 'logs.result',
+    isDisplayed: true
+  },
+  sess: {
+    displayName: 'logs.session'
+  },
+  text: {
+    displayName: 'logs.text'
+  },
+  type: {
+    displayName: 'logs.type'
+  },
+  ugi: {
+    displayName: 'logs.ugi'
+  },
+  reqUser: {
+    displayName: 'logs.user',
+    isDisplayed: true
+  },
+  ws_base_url: {
+    displayName: 'logs.baseUrl'
+  },
+  ws_command: {
+    displayName: 'logs.command'
+  },
+  ws_component: {
+    displayName: 'logs.component'
+  },
+  ws_details: {
+    displayName: 'logs.details'
+  },
+  ws_display_name: {
+    displayName: 'logs.displayName'
+  },
+  ws_os: {
+    displayName: 'logs.os'
+  },
+  ws_repo_id: {
+    displayName: 'logs.repoId'
+  },
+  ws_repo_version: {
+    displayName: 'logs.repoVersion'
+  },
+  ws_repositories: {
+    displayName: 'logs.repositories'
+  },
+  ws_request_id: {
+    displayName: 'logs.requestId'
+  },
+  ws_result_status: {
+    displayName: 'logs.resultStatus'
+  },
+  ws_roles: {
+    displayName: 'logs.roles'
+  },
+  ws_stack_version: {
+    displayName: 'logs.stackVersion'
+  },
+  ws_stack: {
+    displayName: 'logs.stack'
+  },
+  ws_status: {
+    displayName: 'logs.status'
+  },
+  ws_task_id: {
+    displayName: 'logs.taskId'
+  },
+  ws_version_note: {
+    displayName: 'logs.versionNote'
+  },
+  ws_version_number: {
+    displayName: 'logs.versionNumber'
+  },
+  tags: {
+    isAvailable: false
+  },
+  tags_str: {
+    isAvailable: false
+  },
+  seq_num: {
+    isAvailable: false
+  }
+};
+
+export class AuditLogField extends LogField {
+  constructor(name: string) {
+    super(name);
+    const preset = columnsNamesMap[this.name];
+    if (preset) {
+      Object.assign(this, preset);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/models/log-field.model.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/log-field.model.ts 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/log-field.model.ts
new file mode 100644
index 0000000..0e738ab
--- /dev/null
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/log-field.model.ts
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+
+export class LogField {
+  constructor(name: string) {
+    this.name = name;
+  }
+  name: string;
+  displayName: string = this.name;
+  isDisplayed: boolean = false;
+  isAvailable: boolean = true;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/models/service-log-field.model.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/service-log-field.model.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/service-log-field.model.ts
new file mode 100644
index 0000000..081eecf
--- /dev/null
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/service-log-field.model.ts
@@ -0,0 +1,107 @@
+/**
+ * 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 {LogField} from '@app/models/log-field.model';
+
+const columnsNamesMap = {
+  log_message: {
+    displayName: 'logs.message',
+    isDisplayed: true
+  },
+  bundle_id: {
+    displayName: 'logs.bundleId'
+  },
+  case_id: {
+    displayName: 'logs.caseId'
+  },
+  cluster: {
+    displayName: 'logs.cluster'
+  },
+  event_count: {
+    displayName: 'logs.eventCount'
+  },
+  file: {
+    displayName: 'logs.file'
+  },
+  host: {
+    displayName: 'logs.host'
+  },
+  id: {
+    displayName: 'logs.id'
+  },
+  ip: {
+    displayName: 'logs.ip'
+  },
+  level: {
+    displayName: 'logs.level',
+    isDisplayed: true
+  },
+  line_number: {
+    displayName: 'logs.lineNumber'
+  },
+  logtype: {
+    displayName: 'logs.logType'
+  },
+  logfile_line_number: {
+    displayName: 'logs.logfileLineNumber'
+  },
+  logger_name: {
+    displayName: 'logs.loggerName'
+  },
+  logtime: {
+    isDisplayed: true
+  },
+  method: {
+    displayName: 'logs.method'
+  },
+  path: {
+    displayName: 'logs.path'
+  },
+  rowtype: {
+    displayName: 'logs.rowType'
+  },
+  thread_name: {
+    displayName: 'logs.threadName'
+  },
+  type: {
+    displayName: 'logs.type',
+    isDisplayed: true
+  },
+  tags: {
+    isAvailable: false
+  },
+  text: {
+    isAvailable: false
+  },
+  message: {
+    isAvailable: false
+  },
+  seq_num: {
+    isAvailable: false
+  }
+};
+
+export class ServiceLogField extends LogField {
+  constructor(name: string) {
+    super(name);
+    const preset = columnsNamesMap[this.name];
+    if (preset) {
+      Object.assign(this, preset);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts
index fce72d7..13a1e68 100644
--- a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts
+++ b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts
@@ -7,7 +7,7 @@
  * "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
+ *     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,
@@ -27,12 +27,15 @@ import {Graph} from '@app/models/graph.model';
 import {Node} from '@app/models/node.model';
 import {UserConfig} from '@app/models/user-config.model';
 import {Filter} from '@app/models/filter.model';
+import {AuditLogField} from '@app/models/audit-log-field.model';
+import {ServiceLogField} from '@app/models/service-log-field.model';
 
 export const storeActions = {
   'ARRAY.ADD': 'ADD',
   'ARRAY.DELETE.PRIMITIVE': 'DELETE_PRIMITIVE',
   'ARRAY.DELETE.OBJECT': 'DELETE_OBJECT',
   'ARRAY.CLEAR': 'CLEAR',
+  'ARRAY.UPDATE.OBJECT': 'UPDATE_OBJECT',
 
   'OBJECT.SET': 'SET'
 };
@@ -49,6 +52,8 @@ export interface AppStore {
   filters: Filter[];
   clusters: string[];
   components: string[];
+  serviceLogsFields: ServiceLogField[];
+  auditLogsFields: AuditLogField[];
 }
 
 export class ModelService {
@@ -90,7 +95,7 @@ export class CollectionModelService extends ModelService {
 
   deletePrimitiveInstance(instance: any): void {
     this.store.dispatch({
-      type: `${storeActions['ARRAY.DELETE.PRINITIVE']}_${this.modelName}`,
+      type: `${storeActions['ARRAY.DELETE.PRIMITIVE']}_${this.modelName}`,
       payload: instance
     });
   }
@@ -101,6 +106,17 @@ export class CollectionModelService extends ModelService {
     });
   }
 
+  updateObjectInstance(key: string, value: any, keyToModify: string, modifier: 
(value: any) => {}): void {
+    this.store.dispatch({
+      type: `${storeActions['ARRAY.UPDATE.OBJECT']}_${this.modelName}`,
+      payload: {
+        selector: item => item[key] === value,
+        key: keyToModify,
+        modifier: (currentValue) => modifier(currentValue)
+      }
+    });
+  }
+
 }
 
 export class ObjectModelService extends ModelService {
@@ -135,6 +151,14 @@ export function getCollectionReducer(modelName: string, 
defaultState: any = []):
         return state.filter(item => item !== action.payload);
       case `${storeActions['ARRAY.CLEAR']}_${modelName}`:
         return [];
+      case `${storeActions['ARRAY.UPDATE.OBJECT']}_${modelName}`:
+        const payload = action.payload;
+        let newState = state.slice(),
+          item = newState.find(payload.selector);
+        if (item) {
+          item[payload.key] = payload.modifier(item[payload.key]);
+        }
+        return newState;
       default:
         return state;
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
index 512b4f3..f79ba45 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
@@ -18,6 +18,7 @@
 
 import {Injectable} from '@angular/core';
 import {AppSettingsService} from '@app/services/storage/app-settings.service';
+import {CollectionModelService} from '@app/models/store.model';
 
 @Injectable()
 export class ComponentActionsService {
@@ -43,4 +44,8 @@ export class ComponentActionsService {
     this.appSettings.setParameter('timeZone', timeZone);
   }
 
+  updateSelectedColumns(columnName: string, model: CollectionModelService): 
void {
+    model.updateObjectInstance('name', columnName, 'isDisplayed', currentValue 
=> !currentValue);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/services/http-client.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/http-client.service.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/http-client.service.ts
index 260b920..625a55c 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/http-client.service.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/http-client.service.ts
@@ -42,6 +42,9 @@ export class HttpClientService extends Http {
       url: 'audit/logs',
       params: opts => new AuditLogsQueryParams(opts)
     },
+    auditLogsFields: {
+      url: 'audit/logs/schema/fields'
+    },
     serviceLogs: {
       url: 'service/logs',
       params: opts => new ServiceLogsQueryParams(opts)
@@ -50,6 +53,9 @@ export class HttpClientService extends Http {
       url: 'service/logs/histogram',
       params: opts => new ServiceLogsHistogramQueryParams(opts)
     },
+    serviceLogsFields: {
+      url: 'service/logs/schema/fields'
+    },
     components: {
       url: 'service/logs/components'
     },

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/audit-logs-fields.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/audit-logs-fields.service.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/audit-logs-fields.service.ts
new file mode 100644
index 0000000..bb8c661
--- /dev/null
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/audit-logs-fields.service.ts
@@ -0,0 +1,32 @@
+/**
+ * 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 {Store} from '@ngrx/store';
+import {AppStore, CollectionModelService, getCollectionReducer} from 
'@app/models/store.model';
+
+export const modelName = 'auditLogsFields';
+
+@Injectable()
+export class AuditLogsFieldsService extends CollectionModelService {
+  constructor(store: Store<AppStore>) {
+    super(modelName, store);
+  }
+}
+
+export const auditLogsFields = getCollectionReducer(modelName);

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/reducers.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/reducers.service.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/reducers.service.ts
index 3c92c75..d941beb 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/reducers.service.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/reducers.service.ts
@@ -7,7 +7,7 @@
  * "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
+ *     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,
@@ -27,6 +27,8 @@ import {graphs} from '@app/services/storage/graphs.service';
 import {nodes} from '@app/services/storage/nodes.service';
 import {serviceLogs} from '@app/services/storage/service-logs.service';
 import {serviceLogsHistogramData} from 
'@app/services/storage/service-logs-histogram-data.service';
+import {serviceLogsFields} from 
'@app/services/storage/service-logs-fields.service';
+import {auditLogsFields} from 
'@app/services/storage/audit-logs-fields.service';
 import {userConfigs} from '@app/services/storage/user-configs.service';
 
 export const reducers = {
@@ -40,7 +42,9 @@ export const reducers = {
   userConfigs,
   filters,
   clusters,
-  components
+  components,
+  serviceLogsFields,
+  auditLogsFields
 };
 
 export function reducer(state: any, action: any) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/service-logs-fields.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/service-logs-fields.service.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/service-logs-fields.service.ts
new file mode 100644
index 0000000..0082cd6
--- /dev/null
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/storage/service-logs-fields.service.ts
@@ -0,0 +1,32 @@
+/**
+ * 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 {Store} from '@ngrx/store';
+import {AppStore, CollectionModelService, getCollectionReducer} from 
'@app/models/store.model';
+
+export const modelName = 'serviceLogsFields';
+
+@Injectable()
+export class ServiceLogsFieldsService extends CollectionModelService {
+  constructor(store: Store<AppStore>) {
+    super(modelName, store);
+  }
+}
+
+export const serviceLogsFields = getCollectionReducer(modelName);

http://git-wip-us.apache.org/repos/asf/ambari/blob/f4bb14c1/ambari-logsearch/ambari-logsearch-web-new/src/assets/i18n/en.json
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web-new/src/assets/i18n/en.json 
b/ambari-logsearch/ambari-logsearch-web-new/src/assets/i18n/en.json
index d6a46ac..1b71f24 100644
--- a/ambari-logsearch/ambari-logsearch-web-new/src/assets/i18n/en.json
+++ b/ambari-logsearch/ambari-logsearch-web-new/src/assets/i18n/en.json
@@ -49,6 +49,72 @@
   "pagination.title": "Rows per page:",
   "pagination.numbers": "{{startIndex}}-{{endIndex}} of {{totalCount}}",
 
+  "logs.columns": "Columns",
   "logs.status": "Status",
-  "logs.details": "Details"
+  "logs.details": "Details",
+  "logs.message": "Message",
+  "logs.bundleId": "Bundle Id",
+  "logs.caseId": "Case Id",
+  "logs.cluster": "Cluster",
+  "logs.eventCount": "Event Count",
+  "logs.file": "File",
+  "logs.host": "Host",
+  "logs.id": "Id",
+  "logs.ip": "Ip",
+  "logs.level": "Level",
+  "logs.lineNumber": "Line Number",
+  "logs.logType": "Log Type",
+  "logs.logfileLineNumber": "Logfile Line Number",
+  "logs.loggerName": "Logger Name",
+  "logs.method": "Method",
+  "logs.path": "Path",
+  "logs.rowType": "Row Type",
+  "logs.threadName": "Thread",
+  "logs.type": "Type",
+  "logs.enforcer": "Access Enforcer",
+  "logs.accessType": "Access Type",
+  "logs.action": "Action",
+  "logs.agent": "Agent",
+  "logs.agentHost": "Agent Host",
+  "logs.authType": "Auth Type",
+  "logs.clientIp": "Client Ip",
+  "logs.clientType": "Client Type",
+  "logs.dst": "DST",
+  "logs.eventTime": "Event Time",
+  "logs.logMessage": "Log Message",
+  "logs.logTime": "Log Time",
+  "logs.perm": "Perm",
+  "logs.policy": "Policy",
+  "logs.proxyUsers": "Proxy Users",
+  "logs.reason": "Reason",
+  "logs.repo": "Repo",
+  "logs.repoType": "Repo Type",
+  "logs.reqCallerId": "Req Caller Id",
+  "logs.reqContext": "Req Context",
+  "logs.reqData": "Req Data",
+  "logs.reqSelfId": "Req Self Id",
+  "logs.resType": "Res Type",
+  "logs.resource": "Resource",
+  "logs.result": "Result",
+  "logs.session": "Session",
+  "logs.text": "Text",
+  "logs.ugi": "UGI",
+  "logs.user": "User",
+  "logs.baseUrl": "Base URL",
+  "logs.command": "Command",
+  "logs.component": "Component",
+  "logs.details": "Details",
+  "logs.displayName": "Display Name",
+  "logs.os": "OS",
+  "logs.repoId": "Repo Id",
+  "logs.repoVersion": "Repo Version",
+  "logs.repositories": "Repositories",
+  "logs.requestId": "Request Id",
+  "logs.resultStatus": "Result Status",
+  "logs.roles": "Roles",
+  "logs.stackVersion": "Stack Version",
+  "logs.stack": "Stack",
+  "logs.taskId": "Task Id",
+  "logs.versionNote": "Version Note",
+  "logs.versionNumber": "Version Number"
 }

Reply via email to